程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Paint.Net學習筆記——四、窗體粘靠

Paint.Net學習筆記——四、窗體粘靠

編輯:關於.NET

本節介紹在Windows應用程序中出現的“控件粘靠”效果的實現。

之前一直用Winamp播放音樂,直到現在使用千千靜聽和酷狗,這幾款音頻播放軟件界面一脈相承,並 都具有“控件粘靠”效果,現在讓我們一起來看看PDN裡這種效果是如何實現的。

在PDN中,實現該效果的是由SnapManager類和SnapObstacle(下稱障礙物)抽象類實現的,當然還有 一些輔助類,譬如SnapDescription(下稱粘靠定義)類。

SnapManager顧名思義是粘靠效果的管理類,負責各障礙物的定位、判斷是否需要粘靠、保存加載效 果、生成粘靠定義對象等工作。

SnapObstacle是一個抽象類,定義了作為“可粘靠”的控件的必要屬性,包括障礙物輪廓,粘靠距離 ,粘靠事件等。

接下來看看它們是怎樣在一起工作的。

“粘靠”效果主要應用在浮動窗口(工具欄、歷史、顏色和圖層)。從上篇文章中,我們已經了解了 這幾個窗體的繼承關系和各自實現的接口,在這裡簡單復習一下:

PdnBaseForm為所有窗體的父類,實現了ISnapManagerHost接口,所有浮動窗口繼承自 FloatingToolForm,實現了ISnapObstacleHost接口。所有“粘靠”效果就是由這兩個接口提供的。

這兩個接口相當簡單,各自只提供了一個屬性,該屬性分別是SnapManager和SnapObstacle。

我們先看看SnapManager類,上面已經解釋了該類的主要作用,不再羅嗦,接下來細致看看那裡面的 代碼:

SnapManager字段

 1private Dictionary<SnapObstacle, SnapDescription> obstacles =new Dictionary<SnapObstacle, SnapDescription>();
  2private const string isSnappedValueName = "IsSnapped";
  3private const string leftValueName = "Left";
  4private const string topValueName = "Top";
  5private const string widthValueName = "Width";
  6private const string heightValueName = "Height";
  7private const string nullName = "";
  8
  9private const string snappedToValueName = "SnappedTo";
10private const string horizontalEdgeValueName = "HorizontalEdge";
11private const string verticalEdgeValueName = "VerticalEdge";
12private const string xOffsetValueName = "XOffset";
13private const string yOffsetValueName = "YOffset";

obstacles字典保存了“障礙物”和“粘靠定義”的一一對應,一連串的字符串用於獲取資源中的初 始值以及保存粘靠定義,這些都在LoadSnapObstacleData和SaveSnapObstacleData方法中體現。

無論從子類中怎樣跟蹤進來,我們看見,最重要的,就是AdjustObstacleDestination和 AdjustNewLocation方法:

AdjustNewLocation

 1/**//// <summary>
  2        /// 獲取粘靠物新坐標
  3        /// </summary>
  4        /// <param name="obstacle">障礙物</param>
  5        /// <param name="newLocation">新坐標</param>
  6        /// <param name="snapDescription">粘靠定義</param>
  7        /// <returns>粘靠物的新坐標</returns>
  8        private static Point AdjustNewLocation(SnapObstacle obstacle, Point newLocation, SnapDescription snapDescription)
  9        {
10            if (snapDescription == null ||
11                (snapDescription.HorizontalEdge == HorizontalSnapEdge.Neither &&
12                 snapDescription.VerticalEdge == VerticalSnapEdge.Neither))
13            {
14                //如果粘靠定義為"不粘靠",那麼返回障礙物的當前坐標
15                return obstacle.Bounds.Location;
16            }
17
18            Rectangle obstacleRect = new Rectangle(newLocation, obstacle.Bounds.Size);
19            Rectangle snappedToRect = snapDescription.SnappedTo.Bounds;
20            HorizontalSnapEdge hEdge = snapDescription.HorizontalEdge;
21            VerticalSnapEdge vEdge = snapDescription.VerticalEdge;
22            SnapRegion region = snapDescription.SnappedTo.SnapRegion;
23
24            //粘靠Y間距
25            int deltaY = 0;
26
27            //Y間距只需要判斷Top和Buttom,所以這裡分開四個情況判斷
28            if (hEdge == HorizontalSnapEdge.Top && region == SnapRegion.Exterior)
29            {
30                //間距在障礙物外部
31                int newBottomEdge = snappedToRect.Top - snapDescription.YOffset;
32                deltaY = obstacleRect.Bottom - newBottomEdge;
33            }
34            else if (hEdge == HorizontalSnapEdge.Bottom && region == SnapRegion.Exterior)
35            {
36                int newTopEdge = snappedToRect.Bottom + snapDescription.YOffset;
37                deltaY = obstacleRect.Top - newTopEdge;
38            }
39            else if (hEdge == HorizontalSnapEdge.Top && region == SnapRegion.Interior)
40            {
41                //間距在障礙物內部
42                int newTopEdge = Math.Min(snappedToRect.Bottom, snappedToRect.Top + snapDescription.YOffset);
43                deltaY = obstacleRect.Top - newTopEdge;
44            }
45            else if (hEdge == HorizontalSnapEdge.Bottom && region == SnapRegion.Interior)
46            {
47                int newBottomEdge = Math.Max(snappedToRect.Top, snappedToRect.Bottom - snapDescription.YOffset);
48                deltaY = obstacleRect.Bottom - newBottomEdge;
49            }
50
51            //粘靠X邊距
52            int deltaX = 0;
53
54            if (vEdge == VerticalSnapEdge.Left && region == SnapRegion.Exterior)
55            {
56                int newRightEdge = snappedToRect.Left - snapDescription.XOffset;
57                deltaX = obstacleRect.Right - newRightEdge;
58            }
59            else if (vEdge == VerticalSnapEdge.Right && region == SnapRegion.Exterior)
60            {
61                int newLeftEdge = snappedToRect.Right + snapDescription.XOffset;
62                deltaX = obstacleRect.Left - newLeftEdge;
63            }
64            else if (vEdge == VerticalSnapEdge.Left && region == SnapRegion.Interior)
65            {
66                int newLeftEdge = Math.Min(snappedToRect.Right, snappedToRect.Left + snapDescription.XOffset);
67                deltaX = obstacleRect.Left - newLeftEdge;
68            }
69            else if (vEdge == VerticalSnapEdge.Right && region == SnapRegion.Interior)
70            {
71                int newRightEdge = Math.Max(snappedToRect.Left, snappedToRect.Right - snapDescription.XOffset);
72                deltaX = obstacleRect.Right - newRightEdge;
73            }
74            //粘靠物新坐標為"障礙物坐標-間距"
75            Point adjustedLocation = new Point(obstacleRect.Left - deltaX, obstacleRect.Top - deltaY);
76            return adjustedLocation;
77        }

AdjustObstacleDestination

  1/**//// <summary>
  2        /// Given an obstacle and its attempted destination, determines the correct landing
  3        /// spot for an obstacle.
  4        /// </summary>
  5        /// <summary>
  6        /// 使用粘靠定義,決定粘靠物的新坐標
  7        /// </summary>
  8        /// <param name="movingObstacle">The obstacle that is moving.</param>
  9        /// <param name="movingObstacle">移動中的粘靠物(也是障礙物,需 要及時調整的)</param>
10        /// <param name="newLocation">The upper-left coordinate of the obstacle's original intended destination.</param>
11        /// <param name="newLocation">粘靠物當前左上角坐標 </param>
12        /// <returns>
13        /// A Point that determines where the obstacle should be placed instead. If there are no adjustments
14        /// required to the obstacle's desintation, then the return value will be equal to newLocation.
15        /// </returns>
16        /// <returns>
17        /// 用於替換障礙物的坐標,如果對粘靠物的粘靠沒有任何要求,會返回粘靠物的 當前坐標(newLocation參數)
18        /// </returns>
19        /// <remarks>
20        /// movingObstacle's SnapDescription will also be updated. The caller of this method is required
21        /// to update the SnapObstacle with the new, adjusted location.
22        /// </remarks>
23        /// <remarks>
24        /// 粘靠定義將會更新,請調用此方法時更新新的粘靠坐標
25        /// </remarks>
26        public Point AdjustObstacleDestination(SnapObstacle movingObstacle, Point newLocation)
27        {
28            //第一次調用,使粘靠物與障礙物實現粘靠
29            Point adjusted1 = AdjustObstacleDestination(movingObstacle, newLocation, false);
30            //第二次調用,粘靠無與四周障礙物調整粘靠
31            Point adjusted2 = AdjustObstacleDestination(movingObstacle, adjusted1, true);
32            return adjusted2;
33        }
34
35        public Point AdjustObstacleDestination(SnapObstacle movingObstacle, Point newLocation, bool considerStickies)
36        {
37            Point adjustedLocation = newLocation;
38            //獲取當前粘靠物的粘靠定義
39            SnapDescription sd = this.obstacles[movingObstacle];
40            SnapDescription newSD = null;
41
42            foreach (SnapObstacle avoidee in this.obstacles.Keys)
43            {
44                if (avoidee.StickyEdges != considerStickies)
45                {
46                    continue;
47                }
48
49                if (avoidee.Enabled && !object.ReferenceEquals (avoidee, movingObstacle))
50                {
51                    SnapDescription newSD2 = DetermineNewSnapDescription(movingObstacle, adjustedLocation, avoidee, newSD);
52
53                    if (newSD2 != null)
54                    {
55                        //獲取新坐標
56                        Point adjustedLocation2 = AdjustNewLocation(movingObstacle, adjustedLocation, newSD2);
57                        newSD = newSD2;
58                        adjustedLocation = adjustedLocation2;
59                        Rectangle newBounds = new Rectangle (adjustedLocation, movingObstacle.Bounds.Size);
60                    }
61                }
62            }
63
64            if (sd == null || !sd.SnappedTo.StickyEdges || newSD == null || newSD.SnappedTo.StickyEdges)
65            {
66                //更新粘靠定義
67                this.obstacles[movingObstacle] = newSD;
68            }
69
70            return adjustedLocation;
71        }

以上在代碼中添加了一些注釋,就不再羅嗦了,如果有不懂就請留言或來信詢問。

在以上我們看出,對於“障礙物”和“粘靠物”的“粘靠”效果新坐標的計算及管理,都是在 SnapManager類中實現的,那麼接下來,在“障礙物”中的定義就簡單多了,只是在適當的時候,調用 SnapManager的AdjustNewLocation方法就可以了,在FloatingToolForm中的實現是這樣的:

UpdateParking

 1/**//// <summary>
  2        /// 更新粘靠
  3        /// </summary>
  4        private void UpdateParking()
  5        {
  6            if (this.FormBorderStyle == FormBorderStyle.Fixed3D ||
  7                this.FormBorderStyle == FormBorderStyle.FixedDialog ||
  8                this.FormBorderStyle == FormBorderStyle.FixedSingle ||
  9                this.FormBorderStyle == FormBorderStyle.FixedToolWindow)
10            {
11                //獲取SnapManager
12                ISnapManagerHost ismh = this.Owner as ISnapManagerHost;
13
14                if (ismh != null)
15                {
16                    SnapManager mySM = ismh.SnapManager;
17                    //使用SpanManager更新當前窗體的位置
18                    mySM.ReparkObstacle(this);
19                }
20            }
21        }

以上已經很清楚地說明了“粘靠”效果的實現過程,希望能對大家都開發有所幫助!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved