引言

项目中有时需要在图片上标注热区,在HTML中有<area>标签,但在WPF中目前还没现成的控件来实现这这一功能。至于图片热区功能有道词典的【图解词典】是个不错的例子,如图1:

图 1

什么是Adorner?

Adoner 是一个绑定到某个UIElement自定义的FrameWorkElemnt 。Adoner被渲染在AdornerLayer,而AdornerLayer又被渲染在被装饰的Element或者Element集合上面。Adorner的渲染独立于它所装饰的UIElement。Adoner通常使用相对坐标来定位到它所装饰的Element上。

Adorner应用场景:

  • 为UIElement添加功能句柄,从而使用户能够通过某种方式操作改Element(如Resize,Rotate,Reposition等)
  • 可以提供多种状态提示或者响应许多事件
  • 在UIElement上叠加一些可视修饰(外观修饰)
  • 遮罩UIElement的部分或者全部

本文所涉及的功能一方面是要添加一个装饰元素去标记热区另一方面就是为该热区添加响应事件。

ImageWithHotSpot

先看下效果图:

当用户在图片上点击红色圆圈内部是,将会触发一个点击事件。

实现思路:

首先新建一个用户自定义控件ImageWithHotSpots.xaml并在其中添加一个Image控件来存放要添加热点的图片,为了在Image控件上添加热点Adorner,我新建了个ImageHotSpotAdorner类,为了方便控制热点的形状外观我独立定义了个ImageHotSpot类,另一方面在我们引用该自定义控件时不用关注具体的Adorner,而只需要关注热点本身就可以了。

代码结构

代码:

ImageHotSpot Class code:

  public class ImageHotSpot
{
public int Id { get; set; }
public Point Location { get; set; } public double With { get; set; } public double Height { get; set; } public Color BorderColor { get; set; } public double BorderThickness { get; set; } public event EventHandler MouseClick; public void InvokeMouseClickEvent()
{
if (MouseClick != null)
{
MouseClick(this, EventArgs.Empty);
}
}
}

ImageWithHotSpots.xaml

<UserControl x:Class="WPFImageHotSopts.ImageWithHotSpots" 

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 

mc:Ignorable="d" 

d:DesignHeight="300" d:DesignWidth="300"> 

<Grid> 

<Image Name="ImageForHotSpot"/> 

</Grid> 

</UserControl> 

ImageWithHotSpots.cs

 /// <summary>
/// Interaction logic for ImageWithHotSpots.xaml
/// </summary>
public partial class ImageWithHotSpots : UserControl
{
private List<ImageHotSpot> _imageHotSpotsList = new List<ImageHotSpot>(); public ImageWithHotSpots()
{
InitializeComponent();
Loaded += OnLoaded;
} private void OnLoaded(object sender, RoutedEventArgs args)
{
foreach (var imageHotSpot in _imageHotSpotsList)
{
AddImageHotSpot(imageHotSpot);
}
} public ImageSource Source
{
get { return ImageForHotSpot.Source; }
set { ImageForHotSpot.Source = value; }
} public List<ImageHotSpot> ImageHotSpots {
get { return _imageHotSpotsList; }
} public void AddImageHotSpot(ImageHotSpot hotSpot)
{
var adorner = new ImageHotSpotAdorner(ImageForHotSpot,hotSpot);
_imageHotSpotsList.Add(hotSpot);
var layer = AdornerLayer.GetAdornerLayer(ImageForHotSpot);
layer.Add(adorner);
} public void RemoveImageHotSpot(ImageHotSpot hotSpot)
{
var layer = AdornerLayer.GetAdornerLayer(ImageForHotSpot);
var adorners=layer.GetAdorners(ImageForHotSpot);
if (adorners != null)
{
var adorner = adorners.FirstOrDefault(a => ((ImageHotSpot) a.Tag).Id == hotSpot.Id);
layer.Remove(adorner);
}
} }

ImageHotSpotAdorner.cs

public class ImageHotSpotAdorner : Adorner 

{ 

#region Data 

private Ellipse _control; 

private Point _location; 

private ArrayList _logicalChildren; 

#endregion Data 

#region Constructor 

public ImageHotSpotAdorner(Image adornedImage, ImageHotSpot hotSpot) 

: base(adornedImage) 

{ 

if (hotSpot == null) 

throw new ArgumentNullException("hotSpot is null"); 

_location = hotSpot.Location; 

_control = new Ellipse(); 

_control.Height = 0.1 * adornedImage.ActualHeight; 

_control.Width = 0.1 * adornedImage.ActualWidth; 

_control.Stroke = new SolidColorBrush(hotSpot.BorderColor); 

_control.StrokeThickness = hotSpot.BorderThickness; 

_control.Fill=new SolidColorBrush(Colors.Transparent); 

_control.MouseLeftButtonUp += _control_MouseLeftButtonUp; 

_control.MouseEnter += _control_MouseEnter; 

_control.MouseLeave += _control_MouseLeave; 

_control.Tag = hotSpot; 

base.AddLogicalChild(_control); 

base.AddVisualChild(_control); 

} 

void _control_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) 

{ 

this.Cursor=Cursors.Arrow; 

} 

void _control_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) 

{ 

this.Cursor = Cursors.Hand; 

} 

private void _control_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) 

{ 

if (_control != null && e.ClickCount == ) 

{ 

var hotSpot = (ImageHotSpot)_control.Tag; 

hotSpot.InvokeMouseClickEvent(); 

} 

} 

#endregion Constructor 

#region UpdateTextLocation 

public void UpdateTextLocation(Point newLocation) 

{ 

_location = newLocation; 

_control.InvalidateArrange(); 

} 

#endregion UpdateTextLocation 

#region Measure/Arrange 

/// <summary> 

/// Allows the control to determine how big it wants to be. 

/// </summary> 

/// <param name="constraint">A limiting size for the control.</param> 

protected override Size MeasureOverride(Size constraint) 

{ 

_control.Measure(constraint); 

return _control.DesiredSize; 

} 

/// <summary> 

/// Positions and sizes the control. 

/// </summary> 

/// <param name="finalSize">The actual size of the control.</param> 

protected override Size ArrangeOverride(Size finalSize) 

{ 

Rect rect = new Rect(_location, finalSize); 

_control.Arrange(rect); 

return finalSize; 

} 

#endregion Measure/Arrange 

#region Visual Children 

/// <summary> 

/// Required for the element to be rendered. 

/// </summary> 

protected override int VisualChildrenCount 

{ 

get { return ; } 

} 

/// <summary> 

/// Required for the element to be rendered. 

/// </summary> 

protected override Visual GetVisualChild(int index) 

{ 

if (index != ) 

throw new ArgumentOutOfRangeException("index"); 

return _control; 

} 

#endregion Visual Children 

#region Logical Children 

/// <summary> 

/// Required for the displayed element to inherit property values 

/// from the logical tree, such as FontSize. 

/// </summary> 

protected override IEnumerator LogicalChildren 

{ 

get 

{ 

if (_logicalChildren == null) 

{ 

_logicalChildren = new ArrayList(); 

_logicalChildren.Add(_control); 

} 

return _logicalChildren.GetEnumerator(); 

} 

} 

#endregion Logical Children 

} 

参考文档

MSDN Adorners Overview http://msdn.microsoft.com/en-us/library/ms743737(v=vs.110).aspx

Annotating an Image in WPF http://www.codeproject.com/Articles/20457/Annotating-an-Image-in-WPF

源码下载:WPFImageHotSopts.rar

[WPF系列]Adorner应用-自定义控件ImageHotSpot的更多相关文章

  1. [WPF系列]-Adorner

      简介 通常我们想对现有的控件,做些修饰时我们就会想到一个装饰模式.WPF中也提供了这样的实现思路:通过将Adorner添加到AdornerLayer中来实现装饰现有控件的效果.如图示:   本来T ...

  2. [WPF系列]从基础起步学习系列计划

    引言 WPF技术已经算不什么新技术,一搜一大把关于WPF基础甚至高级的内容.之前工作中一直使用winform所以一直没有深入学习WPF,这次因项目中使用了WPF技术来实现比较酷的展示界面.我在这里只是 ...

  3. WPF系列 —— 控件添加依赖属性(转)

    WPF系列 —— 控件添加依赖属性 依赖属性的概念,用途 ,如何新建与使用.本文用做一个自定义TimePicker控件来演示WPF的依赖属性的简单应用. 先上TimePicker的一个效果图. 概念 ...

  4. [WPF系列]-ListBox

    引言 本文就WPF中的ListBox常用项给以实例代码演示,包括隐蔽属性的设置,Style设置,以及ControlTemplate的自定义.   Listbox平滑滚动 <ListBox Ite ...

  5. [WPF系列]-数据邦定之DataTemplate 对分层数据的支持

    到目前为止,我们仅讨论如何绑定和显示单个集合. 某些时候,您要绑定的集合包含其他集合. HierarchicalDataTemplate 类专用于 HeaderedItemsControl 类型以显示 ...

  6. [WPF系列]-数据邦定之DataTemplate 根据对象属性切换模板

      引言 书接上回[WPF系列-数据邦定之DataTemplate],本篇介绍如何根据属性切换模板(DataTemplate)   切换模板的两种方式:   使用DataTemplateSelecto ...

  7. [WPF系列]-TreeView的常用事项

    引言 项目经常会用Treeview来组织一些具有层级结构的数据,本节就将项目使用Treeview常见的问题作一个总结. DataBinding数据绑定 DataTemplate自定义 <Hier ...

  8. WPF系列教程——(三)使用Win10 Edge浏览器内核 - 简书

    原文:WPF系列教程--(三)使用Win10 Edge浏览器内核 - 简书 在需要显示一些 H5网站的时候自带的WebBrowser总是显示不了,WebBrowser使用的是IE内核,许多H5新特性都 ...

  9. WPF系列教程——(一)仿TIM QQ界面 - 简书

    原文:WPF系列教程--(一)仿TIM QQ界面 - 简书 TIM QQ 我们先来看一下TIM QQ长什么样,整体可以将界面分为三个部分 TIM QQ 1. 准备 阅读本文假设你已经有XAML布局的基 ...

随机推荐

  1. Xamarin.Android之给我们的应用加点过渡效果

    零.前言 试想一下,我们的应用正在请求一些数据,假设网络不是很好,要花比较长的时间等待,这个时候界面什么反应也没有, 一动不动,用户可能就会认为应用挂掉了,这么久都没反应的,说不定下一分钟用户就把它卸 ...

  2. Visual Studio Code 1.0发布,支持中文在内9种语言

    Visual Studio Code 1.0发布,支持中文在内的9种语言:Simplified Chinese, Traditional Chinese, French, German, Italia ...

  3. 基于CkEditor实现.net在线开发之路(4)快速布局,工具箱,模板载入,tab选项卡简单说明与使用

    上一章给常用的from表单控件属性页面,进行了简单说明和介绍,但是由于是在网页中做界面设计,操作肯定没有桌面应用程序方便,便捷,为了更方便的布局与设计,今天我主要说一下快速布局,工具箱,tab选项卡, ...

  4. 图解SQL的Join(转)

    对于SQL的Join,在学习起来可能是比较乱的.我们知道,SQL的Join语法有很多inner的,有outer的,有left的,有时候,对于Select出来的结果集是什么样子有点不是很清楚.Codin ...

  5. poj2186--tarjan+缩点

    题目大意:       每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也 ...

  6. “三巨头”有变化,BAT还能走多久?

    在腾讯市值超越阿里巴巴后,市场分析多数认为,当年的BAT“三巨头”时代已经彻底结束,进入了“双寡头”时代了 从对外投资来看,BAT不同的投资逻辑可以推测其战略方向 撰文/梁云风 时评员,关注财经与互联 ...

  7. linux命令大全之ln命令详解(创建软链接和硬链接)

    ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接,分为软链接.硬链接.软链接相当于windows的快捷方式,下面是使用方法和示例   ln是linux中又一 ...

  8. jqgrid(转载)

    一.主要API接口getGridParam.setGridParam: getGridParam方法: getGridParam("url"): 获取当前的AJAX的URL get ...

  9. MySQL 相关总结

    MySQL 优秀在线教程 RUNOOB-SQL 教程 MySQL 常用命令 导出操作 -- 某数据库 全部表 结构和数据 mysqldump -h192.168.8.152 -uroot -p man ...

  10. AJAX与PHP(PHP笔记)--动态验证用户名

    在PHP基础的学习过程中经常会遇到对页面的局部刷新. 比如说,我们在填写用户名的同时,对数据库中的信息进行验证,检查是否充分. 这时就要用到AJAX实现页面的动态加载. 下面例子是简单的PHP与AJA ...