Drawing 对象不支持布局、输入和焦点,因此它们提供性能优势,使其非常适合用于描述背景、剪贴画以及用于对象的低级别绘图 Visual 。
由于 Drawing对象是一个类型 Freezable 对象,因此 Drawing 对象获取几个特殊功能,其中包括:它们可以声明为资源、在多个对象之间共享、变为只读以提高性能、克隆以及使线程安全。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="PresentationOptions" Background="White" Margin="20"> <Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10"> <!-- This image uses a Drawing object for its source. --> <Image> <Image.Source> <DrawingImage PresentationOptions:Freeze="True"> <DrawingImage.Drawing> <GeometryDrawing> <GeometryDrawing.Geometry> <GeometryGroup> <EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" /> <EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" /> </GeometryGroup> </GeometryDrawing.Geometry> <GeometryDrawing.Brush> <LinearGradientBrush> <GradientStop Offset="0.0" Color="Blue" /> <GradientStop Offset="1.0" Color="#CCCCFF" /> </LinearGradientBrush> </GeometryDrawing.Brush> <GeometryDrawing.Pen> <Pen Thickness="10" Brush="Black" /> </GeometryDrawing.Pen> </GeometryDrawing> </DrawingImage.Drawing> </DrawingImage> </Image.Source> </Image> </Border> </Page>
为了使用DrawingVisual对象,您需要为对象创建一个宿主容器。 主机容器对象必须派生自FrameworkElement类,该类提供类缺少的DrawingVisual布局和事件处理支持。 宿主容器对象不显示任何可视属性,因为它的主要用途是包含子对象。
为可视对象创建宿主容器对象时,需要在 中VisualCollection存储可视对象引用。
// Create a host visual derived from the FrameworkElement class. // This class provides layout, event handling, and container support for // the child visual objects. public class MyVisualHost : FrameworkElement { // Create a collection of child visual objects. private VisualCollection _children; public MyVisualHost() { _children = new VisualCollection(this); _children.Add(CreateDrawingVisualRectangle()); _children.Add(CreateDrawingVisualText()); _children.Add(CreateDrawingVisualEllipses()); // Add the event handler for MouseLeftButtonUp. this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp); }
宿主容器对象负责管理其视觉对象的集合。 这要求主机容器实现成员重写派生FrameworkElement类。
下表介绍了必须重写的两个成员:
-
GetVisualChild:从子元素集合中返回指定索引处的子项。
-
VisualChildrenCount:获取此元素中的可视子元素的数量。
在下面的示例中,将实现两FrameworkElement个成员的重写。
// Provide a required override for the VisualChildrenCount property. protected override int VisualChildrenCount { get { return _children.Count; } } // Provide a required override for the GetVisualChild method. protected override Visual GetVisualChild(int index) { if (index < 0 || index >= _children.Count) { throw new ArgumentOutOfRangeException(); } return _children[index]; }
事件处理例程可以通过调用HitTest方法实现命中测试。
// Capture the mouse event and hit test the coordinate point value against // the child visual objects. void MyVisualHost_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { // Retrieve the coordinates of the mouse button event. System.Windows.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)); } // If a child visual object is hit, toggle its opacity to visually indicate a hit. 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; }
创建DrawingVisual 对象
// Create a DrawingVisual that contains a rectangle. private DrawingVisual CreateDrawingVisualRectangle() { DrawingVisual drawingVisual = new DrawingVisual(); // Retrieve the DrawingContext in order to create new drawing content. DrawingContext drawingContext = drawingVisual.RenderOpen(); // Create a rectangle and draw it in the DrawingContext. Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80)); drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect); // Persist the drawing content. drawingContext.Close(); return drawingVisual; }
将 Visual 编码为图像文件
// Base Image BitmapImage bi = new BitmapImage(); bi.BeginInit(); bi.UriSource = new Uri("sampleImages/waterlilies.jpg",UriKind.Relative); bi.DecodePixelWidth = 200; bi.EndInit(); // Text to render on the image. FormattedText text = new FormattedText("Waterlilies", new CultureInfo("en-us"), FlowDirection.LeftToRight, new Typeface(this.FontFamily, FontStyles.Normal, FontWeights.Normal, new FontStretch()), this.FontSize, Brushes.White); // The Visual to use as the source of the RenderTargetBitmap. DrawingVisual drawingVisual = new DrawingVisual(); DrawingContext drawingContext = drawingVisual.RenderOpen(); drawingContext.DrawImage(bi,new Rect(0,0,bi.Width,bi.Height)); drawingContext.DrawText(text, new Point(bi.Height/2, 0)); drawingContext.Close(); // The BitmapSource that is rendered with a Visual. RenderTargetBitmap rtb = new RenderTargetBitmap(bi.PixelWidth, bi.PixelHeight, 96, 96, PixelFormats.Pbgra32); rtb.Render(drawingVisual); // Encoding the RenderBitmapTarget as a PNG file. PngBitmapEncoder png = new PngBitmapEncoder(); png.Frames.Add(BitmapFrame.Create(rtb)); using (Stream stm = File.Create("new.png")) { png.Save(stm); }
复合几何图形
使用 GeometryGroup、CombinedGeometry 或者通过调用静态的 Geometry 方法 Combine,可以创建复合几何图形对象。它们主要的区别是:
- CombinedGeometry 对子图形进行叠加操作,没有面积的子图形将被丢弃。只能组合两个子图形(但是这两个子图形也可以是复合几何图形)。CombinedGeometry的叠加方式有四种:Union、Intersect、Exclude 和 Xor
-
GeometryGroup 只进行组合,而不进行面积叠加。可以添加多个子图形。填充方式由FillRule设定
Geometry对象中本身还包含了一系列非常有用的方法,如:
-
FillContains - 确定是否包含其他 Geometry。
-
StrokeContains - 确定是否包含指定的点。
-
Bounds:获取外接矩形
FillContains,StrokeContains用于鼠标命中测试是非常方便的。
Geometry对象并不能作为图像独立呈现出来,它一般有如下几种呈现方式:
- 在Path中呈现
- 在DrawingContext中呈现
- 在GeometryDrawing中呈现
DrawingContext比较类似WinForm中的Graphics 类,是基础的绘图对象,用于绘制各种图形。使用DrawingContext绘图的一个最简单的方式是重载控件的OnRender方法