WPF 深入研究 之 图形--Point篇

 

18

WPF性能分析器

http://msdn.microsoft.com/zh-cn/library/aa969767.aspx

1ColorPickerCustomControl

This sample shows how to create a custom control and display it in a dialog window. This sample defines a custom color picker control that enables users to browse colors by their hue, saturation, and brightness. The control also enables the user to enter RGB, scRGB, and hexadecimal values directly.

2CompositionTargetSample

This sample illustrates how to use CompositionTarget object to create custom drawings or animations based on a per-frame callback.

这个事例演示了CompositionTarget的使用方法,从而使我们可以基于每个帧回调来创建自定义动画。

CompositionTarget是一个静态类,表示应用程序要在其上进行绘制的显示图面。每次绘制应用程序的场景时,都会引发Rendering事件。

我们为其Rendering事件绑定方法,并在UpdateColor方法中改变一些参数:

            CompositionTarget.Rendering += UpdateColor;

UpdateColor方法产生两个效果:

1)基于鼠标在Canvas上的移动,来将鼠标的位置设置给Point类型对象_pt的,从而Canvas的背景色发生改变:

        public void MouseMoveHandler(object sender, MouseEventArgs e)

        {

            _pt = e.GetPosition((UIElement)sender);

        }

2)设置了一个Stopwatch类型的计时器:

        private Stopwatch _stopwatch = new Stopwatch();

通过其Start方法,开始计算时间和速率,这里表示_frameCounter帧数:

            if (_frameCounter++ == 0)

            {

                _stopwatch.Start();

            }

从而使界面上的三个Label即时发生改变。

3ConverterSample

This sample shows how to use the converter classes to convert an instance of a type into a string.

这个例子演示了如何使用转换类把字符串转换为各种坐标向量,有以下7种转换类:

1PointConverter

                        string string1 = "10,20";

                        PointConverter pConverter = new PointConverter();

                        Point pointResult = new Point();

                        pointResult = (Point)pConverter.ConvertFromString(string1);

2VectorConverter

                        string string1 = "10,20";

                        VectorConverter vConverter = new VectorConverter();

                        Vector vectorResult = new Vector();

                        vectorResult = (Vector)vConverter.ConvertFromString(string1);

3MatrixConverter

                        string string2 = "10,20,30,40,50,60";

                        MatrixConverter mConverter = new MatrixConverter();

                        Matrix matrixResult = new Matrix();

                        matrixResult = (Matrix)mConverter.ConvertFromString(string2);

4Point3DConverter

                        string string3 = "10,20,30";

                        Point3DConverter p3DConverter = new Point3DConverter();

                        Point3D point3DResult = new Point3D();

                        point3DResult = (Point3D)p3DConverter.ConvertFromString(string3);

5Vector3DConverter

                        string string3 = "10,20,30";

                        Vector3DConverter v3DConverter = new Vector3DConverter();

                        Vector3D vector3DResult = new Vector3D();

                        vector3DResult = (Vector3D)v3DConverter.ConvertFromString(string3);

6Size3DConverter

                        string string3 = "10,20,30";

                        Size3DConverter s3DConverter = new Size3DConverter();

                        Size3D size3DResult = new Size3D();

                        size3DResult = (Size3D)s3DConverter.ConvertFromString(string3);

7Point4DConverter

                        string string4 = "10,20,30,40";

                        Point4DConverter p4DConverter = new Point4DConverter();

                        Point4D point4DResult = new Point4D();

                        point4DResult = (Point4D)p4DConverter.ConvertFromString(string4);

总结:基本都是相同形式的语法:

                        (转换类型)装换类.ConvertFromString(字符串)

4DrawingVisualSample

This sample illustrates how to use DrawingVisual objects to create lightweight graphics. The sample also shows how to implement hit testing at the visual object level.

这个例子演示了如何使用DrawingVisual对象创建简单图形,如rectangletext,以及circle

为此创建一个派生于FrameworkElement的自定义控件MyVisualHost

在主程序中只需如此简单的将其实例添加到Canvas布局中:

            MyVisualHost visualHost = new MyVisualHost();

            MyCanvas.Children.Add(visualHost);

继续讨论MyVisualHost

该类中保存一个对VisualCollection集合的引用:

        private VisualCollection _children;

在相应的构造函数中,将自身作为_children这个集合的父亲,同时将一些使用DrawingVisual绘制的元素添加到这个集合中:

        public MyVisualHost()

        {

            _children = new VisualCollection(this);

            _children.Add(CreateDrawingVisualRectangle());

            _children.Add(CreateDrawingVisualText());

            _children.Add(CreateDrawingVisualEllipses());

        }

考察其中的一个绘制动作,CreateDrawingVisualEllipses方法:

        private DrawingVisual CreateDrawingVisualEllipses()

       {

            DrawingVisual drawingVisual = new DrawingVisual();

            DrawingContext drawingContext = drawingVisual.RenderOpen();

            drawingContext.DrawEllipse(Brushes.Maroon, null, new Point(430, 136), 20, 20);

            drawingContext.Close();

            return drawingVisual;

        }

上述方法绘制了一个椭圆,其他方法的结构与之类似,不再多说。

这个例子还演示了“可视化层中的命中测试”的技术。

通过这个技术,我们可以确定某个几何图形或点值是否位于 Visual的呈现内容内,从而可以进行更细微的控制。

为此引进了VisualTreeHelper类的HitTest方法,来确定“是否位于”这个关系。如果有多个重叠的对象,那么不仅仅是对最顶层的对象有效,而是以Z顺序返回这一组对象。有两种方案,分别对应于HitTestResultBehavior枚举的两个值:

1)如果只想针对于最顶层对象,可以从回调方法返回HitTestResultBehavior.Stop以停止命中测试遍历。(后面有详细代码)。

2)如果希望枚举特定点或几何图形下的所有可视化对象,就从回调方法返回 HitTestResultBehavior.Continue

本例中为第一种情况:

        public HitTestResultBehavior myCallback(HitTestResult result)

        {

           

            return HitTestResultBehavior.Stop;

        }

命中测试有两种实现方式,对应于VisualTreeHelper.HitTest()方法的两个重载方式。

1)默认实现,以点和区域为参数,通过判断点是否在区域内,返回相应的HitTestResult,返回值为null时表示不在区域中。

        public void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            // Retrieve the coordinate of the mouse position.

            Point pt = e.GetPosition((UIElement)sender);

            // Perform the hit test against a given portion of the visual object tree.

            HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);

            if (result != null)

            {

                // Perform action on hit visual object.

            }

        }

2)使用命中测试结果回调,这种方式也是从顶至下进行遍历的。

本例使用的就是这种方式:

        void MyVisualHost_MouseLeftButtonUp(object sender,MouseButtonEventArgs e)

        {

            // Retreive the coordinates of the mouse button event.

            Point pt = e.GetPosition((UIElement)sender);

            // Initiate the hit test by setting up a hit test result callback method.

            VisualTreeHelper.HitTest(this, null, new HitTestResultCallback(myCallback), new PointHitTestParameters(pt));

        }

可以看到,第一个参数就是区域;第二个参数是过滤器回调方法,这里为null,后面会对此进行详细描述;第三个参数是回调方法HitTestResultCallback;第四个参数就是点,只是这里使用了PointHitTestParameters对其进行了包装。

注意,回调方法HitTestResultCallback在当指定的坐标值包含在可视化对象中时发生。这样做的目的,是为了对这些可视化对象进行一些额外的操作,如本例是改变了透明度:

        public HitTestResultBehavior myCallback(HitTestResult result)

        {

            if (result.VisualHit.GetType() == typeof(DrawingVisual))

            {

                if (((DrawingVisual)result.VisualHit).Opacity == 1.0)

                {

                    ((DrawingVisual)result.VisualHit).Opacity = 0.4;

                }

                else

                {

                    ((DrawingVisual)result.VisualHit).Opacity = 1.0;

                }

            }

            // Stop the hit test enumeration of objects in the visual tree.

            return HitTestResultBehavior.Stop;

        }

无论回调方法的意图如何,都不应执行修改可视化树的任何操作,如删除和增加一个节点。为此,可以事先建立一个集合,在回调方法中将这些可视化对象置入这个集合,以后一起处理,所谓的“秋后算账”,代码如下:

        public HitTestResultBehavior MyHitTestResult(HitTestResult result)

        {

            // Add the hit test result to the list that will be processed after the enumeration.

            hitResultsList.Add(result.VisualHit);

            // Set the behavior to return visuals at all z-order levels.

            return HitTestResultBehavior.Continue;

        }

还记得上面的第二个参数:过滤器回调方法,从而忽略可视化树中的无关部分。

        public HitTestFilterBehavior MyHitTestFilter(DependencyObject o)

        {

            // Test for the object value you want to filter.

            if (o.GetType() == typeof(Label))

            {

                // Visual object and descendants are NOT part of hit test results enumeration.

                return HitTestFilterBehavior.ContinueSkipSelfAndChildren;

            }

            else

            {

                // Visual object is part of hit test results enumeration.

                return HitTestFilterBehavior.Continue;

            }

        }

以上代码,在遍历可视化树的过程中,只要遇到Label,就将其以及其下所有子节点都过滤掉,这就是HitTestFilterBehavior.ContinueSkipSelfAndChildren的含义

相应的命中测试为:

            VisualTreeHelper.HitTest(myCanvas,

                  new HitTestFilterCallback(MyHitTestFilter),

                  new HitTestResultCallback(MyHitTestResult),

                  new PointHitTestParameters(pt));

最后一点,我们可以重写HitTestCore方法来重写可视化对象的默认命中测试支持,比如说,同时也支持border而不仅仅是在其内部。

        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)

        {

            // 这里,支持边框的命中测试逻辑并没有实现

            return base.HitTestCore(hitTestParameters);

        }

原始图:

点击背景后的效果:

点击圆形后的效果:

5MatrixSample

This sample shows how to use the various methods and properties of the Matrix structure.This sample creates a list of radio buttons that, when selected, perform an operation using one of the methods or properties from the Matrix structure.

这个例子演示了Matrix的若干种用法,Matrix是一个struct

Matrix表示用于二维空间变换的3x3仿射变换矩阵。

注:写到这里,终于发现我的四年复旦数学本科还是学到一些东西的,这里完全使用的是高等代数,尤其是矩阵和向量的概念。

二维 x-y 平面中进行变换时使用 3x3 矩阵。通过让仿射变换矩阵相乘可形成任意数目的线性变换,例如先旋转扭曲(切变),再平移。仿射变换矩阵的最后一列等于 (0, 0, 1),因此只需指定前两列中的成员。请注意,向量用行向量而非列向量表示。

WPFMatrix 采用行优先的顺序存储,其结构如下:

最后一行中的成员 OffsetX OffsetY 表示平移值。

在方法和属性中,变换矩阵通常被指定为只具有六个成员的向量,如下所示:

    (M11, M12, M21, M22, OffsetX, OffsetY)

虽然可以使用 Matrix 结构直接平移各个点或使用 MatrixTransform 转换对象,但 WPF 还提供了一组类,使用它们可以转换对象而无需直接使用矩阵:RotateTransformScaleTransformSkewTransform TranslateTransform

关于PointMatrixVectorMatrix的运算,参见下面的示例。

6PointSample

This sample illustrates the different operations of the Point structure.

这个例子演示了Point的若干种用法:

Point是一个struct

1Point中的运算符+重载

Point + Vector,得到一个新的Point,这里用到了运算符重载+

        public static Point operator +(Point point, Vector vector);

这意味着将一个点位移到一个新的位置。

注:另一个+重载位于Vector类中:

        public static Point operator +(Vector vector, Point point);

可以实现Vector + Point 的运算,见下面的示例。

2)以上的效果,使用静态方法Point.Add也能实现:

        pointResult = Point.Add(point1, vector1);

3-- 6

对于Point减法,有两种:

一种是Point-Vector,得到Point

                        pointResult = point1 - vector1;

另一种是Point-Point,得到Vector

                        vectorResult = point1 - point2;

相应的静态方法分别为

pointResult = Point.Subtract(point1, vector1)

vectorResult = Point.Subtract(point1, point2)

7)如果只是想产生位移,而不需得到这个位移量,可以使用Offset方法:

          point1.Offset(20, 30);

8-- 9)Point * Matrix,即将一个点位移(因为是点,没有变形效果):

                        Point point1 = new Point(10, 5);

                        Matrix matrix1 = new Matrix(40, 50, 60, 70, 80, 90);

                       //X = 10 * 40 + 5 * 60 + 80; Y = 10 * 50 + 5 * 70 + 90;

                        Point pointResult = point1 * matrix1;    //(780, 940)

以上的效果,使用静态方法Point.Multiply也能实现:

Point.Multiply(point1, matrix1);

10) - 12) 比较两个Point

使用运算符重载==,或其实例方法point1.Equals(point2),或其静态方法Point.Equals(point1, point2)来比较两个Point

这里比较的是相等性,而不是对象实例的同一性,只要两个点在同一位置就返回true

13)这里是比较两个Vector

          vector1.Equals(vector2);

14-- 15)这里演示了PointString的转换:

                        pointResult = Point.Parse("1,3");             //解析为一个Point

                        pointString = pointResult.ToString();         //返回字符串:"1,3"

16)重写了GetHashCode方法,以得到一个唯一的哈希值。

17-- 18)可以将Point转换为Size类型或Vector类型:

                        Point point1 = new Point(10, 5);

                        Size size1 = new Size();

                        size1 = (Size)point1;

这将得到一个宽105Size对象。

                        Vector vector1 = new Vector();

                        vector1 = (Vector)point1;

这将得到一个(10, 5)Vector对象。

7VectorSample

This sample shows how to use the various methods and properties of the Vector structure.A list of radio buttons is created that selects the operation to perform.

Vector表示一个向量:Vector(X, Y),这个例子演示了Vector的多种用法:

Vector是一个struct

1-- 4Vector中的运算符+重载,有两种,

一种是Vector + Point,得到Point

                        pointResult = vector1 + point1;

另一种是Vector + Vector,得到Vector

                        vectorResult = vector1 + vector2;

相应的静态方法分别为

pointResult = Vector.Add(vector1, point1)

vectorResult = Vector.Add(vector1, vector2)

5-- 6Vector的减法:

     vectorResult = vector1 - vector2;

以上的效果,使用静态方法Vector.Subtract也能实现:

Vector.Subtract(vector1, vector2);

7-- 8Vector的乘法

Vector * 倍数:

                        Vector vector1 = new Vector(20, 30);

                        Double scalar1 = 75;

                        Vector vectorResult = new Vector();

                        vectorResult = vector1 * scalar1;    //得到(1500, 2250),放大75倍

使用乘法运算符的重载,得到同样的效果:

                        vectorResult = scalar1 * vector1;

9VectorVector的点积:

                        Vector vector1 = new Vector(20, 30);

                        Vector vector2 = new Vector(45, 70);

                        Double doubleResult;

                        doubleResult = vector1 * vector2;    // 20*45 + 30 * 70 = 3000

10Vector * Matrix,即将一个向量变形(因为是向量,没有位移效果):

                        Vector vector1 = new Vector(20, 30);

                        Matrix matrix1 = new Matrix(40, 50, 60, 70, 80, 90);

                        Vector vectorResult = new Vector();

                       //X = 20 * 40 + 30 * 60; Y = 20 * 50 + 30 * 70;

                        vectorResult = vector1 * matrix1;    //(2600,3100)

11-- 14)使用Vector的静态方法Vector.Multiply,达到前面几个乘法的同样效果:

Vector * 倍数:

                        vectorResult = Vector.Multiply(vector1, scalar1);

                        vectorResult = Vector.Multiply(scalar1, vector1);

Vector点积:

                        doubleResult = Vector.Multiply(vector1,vector2);

Vector* Matrix

                        vectorResult = Vector.Multiply(vector1,matrix1);

15-- 16)也可以将Vector缩小,即所谓的“除法”:

                        vectorResult = vector1 / scalar1;

以上的效果,使用静态方法Vector.Divide也能实现:

Vector.Divide(vector1, scalar1);

18Vector的长度Length属性,是它的XY的勾股值

                        Vector vector1 = new Vector(3, 4);   // vector1.Length = 5

19VectorLengthSquared属性,是Length属性的平方

20VectorNormalize方法,调用后,将XY都除以Length长度值,新的XY值分别是sincos三角函数值。

21)计算两个Vector的角度:

Vector.AngleBetween(vector1, vector2);

22)计算两个Vector的叉积:

Vector.CrossProduct(vector1,vector2);

22)计算两个Vector的行列式:

Vector.Determinant(vector1, vector2);

27VectorString的转换:

Vector vectorResult = Vector.Parse("1,3");

         vectorString = vectorResult.ToString();        //返回字符串:"1,3"

30)反向一个Vector

         vectorResult = -vector1;

32-- 33)将Vector转换为Size类型:

                        Vector vector1 = new Vector(20, 30);

                        Size size1 = (Size)vector1;

这将得到一个宽2030Size对象。

                       Vector vector1 = new Vector(20, 30);

                        Point point1 = new Point();

                        point1 = (Point)vector1;

这将得到一个(20, 30)Point对象。

40)两个Vector的加法:

                        vector1 = vector1 + vector2;

以上的效果,使用静态方法Vector.Divide也能实现:

Vector.Add(vector1, vector2);

8VisualsHitTesting

This sample illustrates how to use the default hit testing support in the visual layer of WPF. Hit testing is performed by using the HitTest method of the VisualTreeHelper class to determine whether a point coordinate value is within the boundary of a given object. The sample also uses Windows Presentation Foundation interop support to create a host Win32 window for the visual objects.

这个例子演示的是在传统Windows中如何使用WPF技术。

posted @ 2008-06-08 14:05  包建强  Views(7016)  Comments(1Edit  收藏  举报