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 }