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

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

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

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

 public class YourControl : UIElement
{
/// <summary>
/// 重写绘制
/// </summary>
protected override void OnRender(DrawingContext drawingContext)
{
// your logic here
}
}

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

  • 项目的组织方式;

  • DrawableGrid.cs
 using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging; namespace draw
{
/// <summary>
/// 基本思想
/// 1 在绘制结束时,计算相对于图片真实大小的情况下,绘制的矩形框的大小,及相对于图片的偏移
/// 2 每次刷新绘制前,计算当前窗口大小,及应该绘制的图片的大小,及其偏移
/// 3 每次刷新绘制前,计算绘制的矩形框,相对于当前图片的偏移
/// 其中,
/// 框的偏移及大小,每次使用针对原始图片的数据,作为基础来计算比例,就能够保证即使窗体缩小到0,依旧可以恢复
/// </summary>
public class DrawableGrid : Control
{
#region vars private Point mousedown;
private Point mouseup;
private bool mouseBtnDown = false;
private bool bSelectionDraw = false; private SolidColorBrush mBrush = Brushes.LightBlue;
private Pen mPen = new Pen(Brushes.Red, );
private BitmapImage Img = null; private Grid drawgrid; private Rect curPicRect;
private Rect curSelectRect; private Rect realSelectRect;
private Rect realPicRect; #endregion #region ctors static DrawableGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DrawableGrid), new FrameworkPropertyMetadata(typeof(DrawableGrid)));
} #endregion #region overrides /// <summary>
/// 重写绘制
/// </summary>
protected override void OnRender(DrawingContext drawingContext)
{
if (Img == null)
return; curPicRect = CalcRect(Img.Height, Img.Width, this.ActualHeight, this.ActualWidth);
curSelectRect = CalcResizeRect(curPicRect.X, curPicRect.Y, realSelectRect.X, realSelectRect.Y, realSelectRect.Height, realSelectRect.Width, curPicRect.Height / realPicRect.Height); drawingContext.DrawImage(Img, curPicRect); if (mouseBtnDown)
{
int xmin = (int)Math.Min(mousedown.X, mouseup.X);
int xmax = (int)Math.Max(mousedown.X, mouseup.X);
int ymin = (int)Math.Min(mousedown.Y, mouseup.Y);
int ymax = (int)Math.Max(mousedown.Y, mouseup.Y);
var r = new Rect(xmin, ymin, xmax - xmin, ymax - ymin);
drawingContext.DrawRectangle(mBrush, mPen, r);
} if (bSelectionDraw)
{
drawingContext.DrawRectangle(mBrush, mPen, curSelectRect);
} base.OnRender(drawingContext);
} public override void OnApplyTemplate()
{
drawgrid = GetTemplateChild("drawgrid") as Grid;
if (drawgrid != null)
{
drawgrid.MouseDown += drawgrid_MouseDown;
drawgrid.MouseMove += drawgrid_MouseMove;
drawgrid.MouseUp += drawgrid_MouseUp;
} string picaddr = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "pic", "2.jpg");
Img = new BitmapImage(new Uri(picaddr));
realPicRect = new Rect(, , Img.Width, Img.Height);
} #endregion #region methods /// <summary>
/// 计算图片大小相对于当前的控件大小,应该如何展示
/// </summary>
/// <param name="imgHeight">图片高</param>
/// <param name="imgWidth">图片宽</param>
/// <param name="gridHeight">容器高</param>
/// <param name="gridWidth">容器宽</param>
/// <returns>当前实际应该绘制图片的矩形大小及相对于容器的偏移</returns>
private Rect CalcRect(double imgHeight, double imgWidth, double gridHeight, double gridWidth)
{
Rect rect;
double imgRatio = imgHeight / imgWidth;
double gridRatio = gridHeight / gridWidth; if (imgRatio >= gridRatio)
{
double hi = gridHeight;
double wi = gridHeight / imgRatio; double left = (gridWidth - wi) / ;
double top = ; rect = new Rect(left, top, wi, hi);
}
else
{
double wi = gridWidth;
double hi = gridWidth * imgRatio; double left = ;
double top = (gridHeight - hi) / ; rect = new Rect(left, top, wi, hi);
} return rect;
} /// <summary>
/// 在图片上绘制的框相对于图片的位置
/// </summary>
private Rect CalcResizeRect(double curx, double cury, double lastx, double lasty, double lastheight, double lastwidth, double ratio)
{
double x = curx + lastx * ratio;
double y = cury + lasty * ratio;
double wid = lastwidth * ratio;
double hei = lastheight * ratio;
return new Rect(x, y, wid, hei);
} #endregion #region events private void drawgrid_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && e.RightButton == MouseButtonState.Released)
{
bSelectionDraw = false;
mouseBtnDown = true;
mousedown = e.GetPosition(drawgrid);
}
} private void drawgrid_MouseMove(object sender, MouseEventArgs e)
{
if (mouseBtnDown)
{
mouseup = e.GetPosition(drawgrid);
this.InvalidateVisual();
}
} private void drawgrid_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
{
bSelectionDraw = true;
mouseBtnDown = false;
mouseup = e.GetPosition(drawgrid); int xmin = (int)Math.Min(mousedown.X, mouseup.X);
int xmax = (int)Math.Max(mousedown.X, mouseup.X);
int ymin = (int)Math.Min(mousedown.Y, mouseup.Y);
int ymax = (int)Math.Max(mousedown.Y, mouseup.Y); var relativeRect = new Rect(xmin, ymin, xmax - xmin, ymax - ymin);
var fullSizeImgRect = CalcRect(Img.Height, Img.Width, Img.Height, Img.Width);
double tempration = fullSizeImgRect.Height / curPicRect.Height;
realSelectRect = CalcResizeRect(fullSizeImgRect.X, fullSizeImgRect.Y, relativeRect.X - curPicRect.X, relativeRect.Y - curPicRect.Y, relativeRect.Height, relativeRect.Width, tempration);
}
} #endregion
}
}
  • DrawableGrid.xaml
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:draw"> <Style TargetType="{x:Type local:DrawableGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DrawableGrid}">
<Grid x:Name="drawgrid" Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
  • Generic.xaml
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="draw;component/DrawableGrid/DrawableGrid.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary>
  • MainWindow.xaml
 <Window x:Class="draw.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:draw"
Title="MainWindow" Height="" Width="">
<local:DrawableGrid />
</Window>
  • MainWindow.xaml.cs
 using System.Windows;

 namespace draw
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

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

  1. 在WPF中自定义你的绘制(五)

    原文:在WPF中自定义你的绘制(五) 在WPF中自定义你的绘制(五)                                                                   ...

  2. 在WPF中自定义你的绘制(三)

    原文:在WPF中自定义你的绘制(三) 在WPF中自定义你的绘制(三)                                                                  ...

  3. 在WPF中自定义你的绘制(四)

    原文:在WPF中自定义你的绘制(四)                                   在WPF中自定义你的绘制(四)                                 ...

  4. 在WPF中自定义你的绘制(一)

    原文:在WPF中自定义你的绘制(一)   在WPF中自定义你的绘制(一)                                                                 ...

  5. 在WPF中自定义你的绘制(二)

    原文:在WPF中自定义你的绘制(二)   在WPF中自定义你的绘制(二)                                                                 ...

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

    原文:在VS2005中设置WPF中自定义按钮的事件 上篇讲了如何在Blend中绘制圆角矩形(http://blog.csdn.net/johnsuna/archive/2007/08/13/17407 ...

  7. WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探

    原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探         最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感 ...

  8. 示例:WPF中自定义MessageService应用DialogHost、Snackbar、NotifyIcon显示各种场景提示消息

    原文:示例:WPF中自定义MessageService应用DialogHost.Snackbar.NotifyIcon显示各种场景提示消息 一.目的:不同交互场景需要提示不同的消息,不同的消息需要用不 ...

  9. 示例:WPF中自定义StoryBoarService在代码中封装StoryBoard、Animation用于简化动画编写

    原文:示例:WPF中自定义StoryBoarService在代码中封装StoryBoard.Animation用于简化动画编写 一.目的:通过对StoryBoard和Animation的封装来简化动画 ...

随机推荐

  1. sendrose【SPFA】

    之前看到一题需要并查集+SPFA,然后就特别囧的发现自己SPFA这个历史遗留问题已经不知道怎么打了╮(╯▽╰)╭ 就果断挑了一题特别裸的SPFA赶紧搞搞掉,顺便自己乱YY下学SPFA的笔记,免得自己下 ...

  2. EF POWER TOOLS由数据库逆向CODE FIRST

    EF POWER TOOLS由数据库逆向CODE FIRST 前言 利用db first的开发方式有很多可供选择的方案,一种可以用ado.net实体框架模型,由向导直接生成edmx,并生成数据库上下文 ...

  3. linux不同环境下c/c++程序移植方法

    这边之前的大多数项目都用的java,而自己用的c++,等到快要上线的时候才发现线上机器的gcc和libc的版本都巨低,跟自己测试开发用的环境不兼容,编译出的c++可执行文件没法运行.解决c++程序的移 ...

  4. MVC 5显示、创建、编辑、删除等功能实练

    MVC 5显示.创建.编辑.删除等功能实练 在前天的学习小结中<15天学习MVC后的小结(分享经历与想法)>http://www.cnblogs.com/insus/p/3369870.h ...

  5. OA小助手

    基于 WPF + Modern UI 的 公司OA小助手 开发总结 前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会 ...

  6. WPF之TreeList的实现方法1

    WPF之TreeList的实现方法(一) 做项目的时候根据需求,WPF现有的控件不能完全满足我们的需求, 很多时候我们需要对现有的控件做一下加工. 最简单的我们可能会把Tree转换成List形式有的叫 ...

  7. 老鸟都应该注意的git 提交规范

    不知道大家有没有看过自己项目的git 提交信息-----我看过好多次 ,不忍直视  然后提醒一起的小伙伴 :大家规范点 信息要详细, 过段时间再看下 ,还是一样. 相信很多猿都有这样的感受,对于垃圾的 ...

  8. 用c#实现与飞环语音卡的交互

    现在很多企业都采用freeswitch搭建的软交换来实现通话,主要优势成本低吞吐量大,但是语音卡的通话质量还是瑞胜一筹. 去年有机会在朋友公司里帮忙开发与软交换交互的上层服务及接口,在开发过程中稍微研 ...

  9. service structure flowchart [mobile to server via TCP/IP protocol]

    For instant message delivering

  10. 迷你MVVM框架 avalonjs 0.82发布

    迷你MVVM框架 avalonjs 0.82发布 本版本最大的改进是启用全新的parser. parser是用于干什么的?在视图中,我们通过绑定属性实现双向绑定,比如ms-text="fir ...