WPF中自定义绘制内容

先说结论:实现了在自定义大小的窗口中,加载图片,并在图片上绘制一个矩形框;且在窗口大小改变的情况,保持绘制的矩形框与图片的先对位置不变。

在WinForm中,我们可以很方便地绘制自己需要的内容,在WPF中似乎被限制了,不能够很方便的使用;然后需求有总是奇葩的,所以在这里简单地总结一下。

在WinForm中,如果需要自己绘制,就需要拿到Graphics对象;同样的,我们就希望在WPF也得到一个其同样作用的对象,这个对象就是DrawingContext类的实例对象。

具体来说,就是要重载 UIElement 类的 OnRender 方法。

 1 public class YourControl : UIElement
 2 {
 3     /// <summary>
 4     /// 重写绘制
 5     /// </summary>
 6     protected override void OnRender(DrawingContext drawingContext)
 7     {
 8         // your logic here
 9     }
10 }

Talk is cheap, here is the code. 下面的代码完整的组织后,编译可运行,已经调试通了;希望对看到的同学有帮助,有问题,我们也可以探讨。

  • 项目的组织方式;

  • DrawableGrid.cs
  1 using System;
  2 using System.Windows;
  3 using System.Windows.Controls;
  4 using System.Windows.Input;
  5 using System.Windows.Media;
  6 using System.Windows.Media.Imaging;
  7
  8 namespace draw
  9 {
 10     /// <summary>
 11     /// 基本思想
 12     /// 1 在绘制结束时,计算相对于图片真实大小的情况下,绘制的矩形框的大小,及相对于图片的偏移
 13     /// 2 每次刷新绘制前,计算当前窗口大小,及应该绘制的图片的大小,及其偏移
 14     /// 3 每次刷新绘制前,计算绘制的矩形框,相对于当前图片的偏移
 15     /// 其中,
 16     /// 框的偏移及大小,每次使用针对原始图片的数据,作为基础来计算比例,就能够保证即使窗体缩小到0,依旧可以恢复
 17     /// </summary>
 18     public class DrawableGrid : Control
 19     {
 20         #region vars
 21
 22         private Point mousedown;
 23         private Point mouseup;
 24         private bool mouseBtnDown = false;
 25         private bool bSelectionDraw = false;
 26
 27         private SolidColorBrush mBrush = Brushes.LightBlue;
 28         private Pen mPen = new Pen(Brushes.Red, 1);
 29         private BitmapImage Img = null;
 30
 31         private Grid drawgrid;
 32
 33         private Rect curPicRect;
 34         private Rect curSelectRect;
 35
 36         private Rect realSelectRect;
 37         private Rect realPicRect;
 38
 39         #endregion
 40
 41         #region ctors
 42
 43         static DrawableGrid()
 44         {
 45             DefaultStyleKeyProperty.OverrideMetadata(typeof(DrawableGrid), new FrameworkPropertyMetadata(typeof(DrawableGrid)));
 46         }
 47
 48         #endregion
 49
 50         #region overrides
 51
 52         /// <summary>
 53         /// 重写绘制
 54         /// </summary>
 55         protected override void OnRender(DrawingContext drawingContext)
 56         {
 57             if (Img == null)
 58                 return;
 59
 60             curPicRect = CalcRect(Img.Height, Img.Width, this.ActualHeight, this.ActualWidth);
 61             curSelectRect = CalcResizeRect(curPicRect.X, curPicRect.Y, realSelectRect.X, realSelectRect.Y, realSelectRect.Height, realSelectRect.Width, curPicRect.Height / realPicRect.Height);
 62
 63             drawingContext.DrawImage(Img, curPicRect);
 64
 65             if (mouseBtnDown)
 66             {
 67                 int xmin = (int)Math.Min(mousedown.X, mouseup.X);
 68                 int xmax = (int)Math.Max(mousedown.X, mouseup.X);
 69                 int ymin = (int)Math.Min(mousedown.Y, mouseup.Y);
 70                 int ymax = (int)Math.Max(mousedown.Y, mouseup.Y);
 71                 var r = new Rect(xmin, ymin, xmax - xmin, ymax - ymin);
 72                 drawingContext.DrawRectangle(mBrush, mPen, r);
 73             }
 74
 75             if (bSelectionDraw)
 76             {
 77                 drawingContext.DrawRectangle(mBrush, mPen, curSelectRect);
 78             }
 79
 80             base.OnRender(drawingContext);
 81         }
 82
 83         public override void OnApplyTemplate()
 84         {
 85             drawgrid = GetTemplateChild("drawgrid") as Grid;
 86             if (drawgrid != null)
 87             {
 88                 drawgrid.MouseDown += drawgrid_MouseDown;
 89                 drawgrid.MouseMove += drawgrid_MouseMove;
 90                 drawgrid.MouseUp += drawgrid_MouseUp;
 91             }
 92
 93             string picaddr = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "pic", "2.jpg");
 94             Img = new BitmapImage(new Uri(picaddr));
 95             realPicRect = new Rect(0, 0, Img.Width, Img.Height);
 96         }
 97
 98         #endregion
 99
100         #region methods
101
102         /// <summary>
103         /// 计算图片大小相对于当前的控件大小,应该如何展示
104         /// </summary>
105         /// <param name="imgHeight">图片高</param>
106         /// <param name="imgWidth">图片宽</param>
107         /// <param name="gridHeight">容器高</param>
108         /// <param name="gridWidth">容器宽</param>
109         /// <returns>当前实际应该绘制图片的矩形大小及相对于容器的偏移</returns>
110         private Rect CalcRect(double imgHeight, double imgWidth, double gridHeight, double gridWidth)
111         {
112             Rect rect;
113             double imgRatio = imgHeight / imgWidth;
114             double gridRatio = gridHeight / gridWidth;
115
116             if (imgRatio >= gridRatio)
117             {
118                 double hi = gridHeight;
119                 double wi = gridHeight / imgRatio;
120
121                 double left = (gridWidth - wi) / 2;
122                 double top = 0;
123
124                 rect = new Rect(left, top, wi, hi);
125             }
126             else
127             {
128                 double wi = gridWidth;
129                 double hi = gridWidth * imgRatio;
130
131                 double left = 0;
132                 double top = (gridHeight - hi) / 2;
133
134                 rect = new Rect(left, top, wi, hi);
135             }
136
137             return rect;
138         }
139
140         /// <summary>
141         /// 在图片上绘制的框相对于图片的位置
142         /// </summary>
143         private Rect CalcResizeRect(double curx, double cury, double lastx, double lasty, double lastheight, double lastwidth, double ratio)
144         {
145             double x = curx + lastx * ratio;
146             double y = cury + lasty * ratio;
147             double wid = lastwidth * ratio;
148             double hei = lastheight * ratio;
149             return new Rect(x, y, wid, hei);
150         }
151
152         #endregion
153
154         #region events
155
156         private void drawgrid_MouseDown(object sender, MouseButtonEventArgs e)
157         {
158             if (e.LeftButton == MouseButtonState.Pressed && e.RightButton == MouseButtonState.Released)
159             {
160                 bSelectionDraw = false;
161                 mouseBtnDown = true;
162                 mousedown = e.GetPosition(drawgrid);
163             }
164         }
165
166         private void drawgrid_MouseMove(object sender, MouseEventArgs e)
167         {
168             if (mouseBtnDown)
169             {
170                 mouseup = e.GetPosition(drawgrid);
171                 this.InvalidateVisual();
172             }
173         }
174
175         private void drawgrid_MouseUp(object sender, MouseButtonEventArgs e)
176         {
177             if (e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
178             {
179                 bSelectionDraw = true;
180                 mouseBtnDown = false;
181                 mouseup = e.GetPosition(drawgrid);
182
183                 int xmin = (int)Math.Min(mousedown.X, mouseup.X);
184                 int xmax = (int)Math.Max(mousedown.X, mouseup.X);
185                 int ymin = (int)Math.Min(mousedown.Y, mouseup.Y);
186                 int ymax = (int)Math.Max(mousedown.Y, mouseup.Y);
187
188                 var relativeRect = new Rect(xmin, ymin, xmax - xmin, ymax - ymin);
189                 var fullSizeImgRect = CalcRect(Img.Height, Img.Width, Img.Height, Img.Width);
190                 double tempration = fullSizeImgRect.Height / curPicRect.Height;
191                 realSelectRect = CalcResizeRect(fullSizeImgRect.X, fullSizeImgRect.Y, relativeRect.X - curPicRect.X, relativeRect.Y - curPicRect.Y, relativeRect.Height, relativeRect.Width, tempration);
192             }
193         }
194
195         #endregion
196     }
197 }
  • DrawableGrid.xaml
 1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 3                     xmlns:local="clr-namespace:draw">
 4
 5     <Style TargetType="{x:Type local:DrawableGrid}">
 6         <Setter Property="Template">
 7             <Setter.Value>
 8                 <ControlTemplate TargetType="{x:Type local:DrawableGrid}">
 9                     <Grid x:Name="drawgrid" Background="Transparent" />
10                 </ControlTemplate>
11             </Setter.Value>
12         </Setter>
13     </Style>
14 </ResourceDictionary>
  • Generic.xaml
1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
3
4     <ResourceDictionary.MergedDictionaries>
5         <ResourceDictionary Source="draw;component/DrawableGrid/DrawableGrid.xaml" />
6     </ResourceDictionary.MergedDictionaries>
7
8 </ResourceDictionary>
  • MainWindow.xaml
1 <Window x:Class="draw.MainWindow"
2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4         xmlns:local="clr-namespace:draw"
5         Title="MainWindow" Height="500" Width="500">
6     <local:DrawableGrid />
7 </Window>
  • MainWindow.xaml.cs
 1 using System.Windows;
 2
 3 namespace draw
 4 {
 5     public partial class MainWindow : Window
 6     {
 7         public MainWindow()
 8         {
 9             InitializeComponent();
10         }
11     }
12 }
时间: 2024-10-09 16:10:03

WPF中自定义绘制内容的相关文章

WPF中自定义MarkupExtension

原文:WPF中自定义MarkupExtension 在介绍这一篇文章之前,我们首先来回顾一下WPF中的一些基础的概念,首先当然是XAML了,XAML全称是Extensible Application Markup Language (可扩展应用程序标记语言),是专门用于WPF技术中的UI设计语言,通过使用XAML语言,我们能够快速设计软件界面,同时能够通过绑定这种机制能够很好地实现界面和实现逻辑之间的解耦,这个就是MVVM模式的核心了,那么今天我们介绍的MarkupExtension和XAML之

在VS2005中设置WPF中自定义按钮的事件

原文:在VS2005中设置WPF中自定义按钮的事件 上篇讲了如何在Blend中绘制圆角矩形(http://blog.csdn.net/johnsuna/archive/2007/08/13/1740781.aspx),本篇继续下一步骤,如何自定义按钮的事件. (1)首先,在VS2005中打开上篇所建的项目(File - Open Project),找到LinearGradientButton.csproj(这是我这里的项目名称),打开之后,双击LinearGradientDemo.xaml.cs

WPF中自定义标题栏时窗体最大化处理之WindowChrome

注意: 本文方法基础是WindowChrome,而WindowChrome在.NET Framework 4.5之后才集成发布的.见:WindowChrome Class 在.NET Framework 4.0中使用WindowChrome,需要安装Ribbon来支持WindowChrome 目前官方文档的内容较为陈旧(但仍有参考价值),其中提到了SystemParameters2,这个应该是Ribbon里的东西,4.5想用可以安装Xceed.Wpf.AvalonDock库,这里面有现成的Mic

【转】WPF中实现自定义虚拟容器(实现VirtualizingPanel)

在WPF应用程序开发过程中,大数据量的数据展现通常都要考虑性能问题.有下面一种常见的情况:原始数据源数据量很大,但是某一时刻数据容器中的可见元素个数是有限的,剩余大多数元素都处于不可见状态,如果一次性将所有的数据元素都渲染出来则会非常的消耗性能.因而可以考虑只渲染当前可视区域内的元素,当可视区域内的元素需要发生改变时,再渲染即将展现的元素,最后将不再需要展现的元素清除掉,这样可以大大提高性能.在WPF中System.Windows.Controls命名空间下的VirtualizingStackP

在WPF中绘制多维数据集

原文 https://stuff.seans.com/2008/08/13/drawing-a-cube-in-wpf/ 是时候使用WPF绘制一个简单的3D对象了.作为WPF中3D图形的快速介绍,让我们只渲染一个最简单的对象 - 一个立方体. 在这个例子中,我将直接在XAML中定义我们需要的所有内容.与WPF中的其他内容一样,我们可以直接在代码中完成所有这些操作.但是在XAML中定义所有内容更加清晰,因为它使对象层次结构更加明显.在实际项目中,您显然会在代码中执行此操作,例如创建或加载3D网格(

在WPF中自定义控件

一, 不一定需要自定义控件在使用WPF以前,动辄使用自定义控件几乎成了惯性思维,比如需要一个带图片的按钮,但在WPF中此类任务却不需要如此大费周章,因为控件可以嵌套使用以及可以为控件外观打造一套新的样式就可以了.是否需要我们来自定义控件,这需要你考虑目前已有控件的真正逻辑功能而不要局限于外观,如果目前的控件都不能直觉地表达你的想法,那么你可以自己来打造一个控件,否则,也许我们仅仅改变一下目前控件的模板等就可以完成任务.很多人在自定义控件上经常犯的错误是:重复撰写已有的逻辑 二,UserContr

WPF中使用amCharts绘制股票K线图

原文:WPF中使用amCharts绘制股票K线图 本想自己用GDI绘图, 通过数据直接绘制一张蜡柱图, 但觉得这样子的功能比较少, 所以到网上搜索一些能画出K线图的控件. 发现DynamicDataDisplay挺好的, 在它的开发截图里也发现可以绘制蜡柱图, 但文档好像做的不是很好, 不知道怎么用它来画, 在寻找它比较好的例子的时候发现有人推荐用amCharts绘制, 所以便去看了一下, 发现挺好用的, 效果如下: 准备工作 先要去这里下载amCharts Stock Chart for WP

C#中TreeView节点的自定义绘制方法

本文实例讲述了C#中TreeView节点的自定义绘制方法.分享给大家供大家参考.具体如下: if ((e.State & TreeNodeStates.Selected) != 0) { //演示为绿底白字 e.Graphics.FillRectangle(Brushes.DeepSkyBlue, e.Node.Bounds); Font nodeFont = e.Node.NodeFont; if (nodeFont == null) nodeFont = ((TreeView)sender)

WPF中如何使用图像API进行绘制

原文:WPF中如何使用图像API进行绘制 首先,由于WPF中不象GDI+中有Graphics对象,因此你无法使用Graphics进行绘图了,取而代之的是:DrawingContext:类似地,GDI+中的OnPaint已被OnRender取代.其次,UIElement有一个OnRendar方法,它的定义是:protected virtual void OnRender (DrawingContext drawingContext)但我们不能直接调用OnRender方法,也不能直接创建Drawin