[WPF系列]Adorner应用-自定义控件ImageHotSpot
引言
项目中有时需要在图片上标注热区,在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的更多相关文章
- [WPF系列]-Adorner
简介 通常我们想对现有的控件,做些修饰时我们就会想到一个装饰模式.WPF中也提供了这样的实现思路:通过将Adorner添加到AdornerLayer中来实现装饰现有控件的效果.如图示: 本来T ...
- [WPF系列]从基础起步学习系列计划
引言 WPF技术已经算不什么新技术,一搜一大把关于WPF基础甚至高级的内容.之前工作中一直使用winform所以一直没有深入学习WPF,这次因项目中使用了WPF技术来实现比较酷的展示界面.我在这里只是 ...
- WPF系列 —— 控件添加依赖属性(转)
WPF系列 —— 控件添加依赖属性 依赖属性的概念,用途 ,如何新建与使用.本文用做一个自定义TimePicker控件来演示WPF的依赖属性的简单应用. 先上TimePicker的一个效果图. 概念 ...
- [WPF系列]-ListBox
引言 本文就WPF中的ListBox常用项给以实例代码演示,包括隐蔽属性的设置,Style设置,以及ControlTemplate的自定义. Listbox平滑滚动 <ListBox Ite ...
- [WPF系列]-数据邦定之DataTemplate 对分层数据的支持
到目前为止,我们仅讨论如何绑定和显示单个集合. 某些时候,您要绑定的集合包含其他集合. HierarchicalDataTemplate 类专用于 HeaderedItemsControl 类型以显示 ...
- [WPF系列]-数据邦定之DataTemplate 根据对象属性切换模板
引言 书接上回[WPF系列-数据邦定之DataTemplate],本篇介绍如何根据属性切换模板(DataTemplate) 切换模板的两种方式: 使用DataTemplateSelecto ...
- [WPF系列]-TreeView的常用事项
引言 项目经常会用Treeview来组织一些具有层级结构的数据,本节就将项目使用Treeview常见的问题作一个总结. DataBinding数据绑定 DataTemplate自定义 <Hier ...
- WPF系列教程——(三)使用Win10 Edge浏览器内核 - 简书
原文:WPF系列教程--(三)使用Win10 Edge浏览器内核 - 简书 在需要显示一些 H5网站的时候自带的WebBrowser总是显示不了,WebBrowser使用的是IE内核,许多H5新特性都 ...
- WPF系列教程——(一)仿TIM QQ界面 - 简书
原文:WPF系列教程--(一)仿TIM QQ界面 - 简书 TIM QQ 我们先来看一下TIM QQ长什么样,整体可以将界面分为三个部分 TIM QQ 1. 准备 阅读本文假设你已经有XAML布局的基 ...
随机推荐
- Git 初始化版本库
创建带工作区的版本库 在开始一个新项目时,首先就要创建并初始化代码库.如果是在本机的工作目录中,那么: $ git init 也就够用了.如果想要初始化的版本库不在当前目录,需要为 git init ...
- C#学习笔记-封装
前言 说起来惭愧,学了大半年的C#,其实最开始就接触到了封装的部分,但是一直模模糊糊的弄不清楚,也觉得没什么影响就没怎么在意,现在才开始认真的看这部分内容,看懂了过后好多东西清晰了不少,才发现封装这个 ...
- Win10 IoT C#开发 4 - UART 串口通信
Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,既可以开发设备UI与用户交互式操作,又可以控制GPIO等接口,使得原来嵌入式繁琐的开发变得简单.通过Remote Debug ...
- .net实现与excel的数据交互、导入导出
应该说,一套成熟的基于web的管理系统,与用户做好的excel表格进行数据交互是一个不可或缺的功能,毕竟,一切以方便客(jin)户(qian)为宗旨. 本人之前从事PHP的开发工作,熟悉PHP的都应该 ...
- nodejs连接mongodb的方法
一. var express = require('express'); var mongodb = require('mongodb'); var app = express(); app.use( ...
- zigbee 路由节点丢失后清除 该节点的残余网络信息
清除脱离网络的 路由节点(stale device)的 残留在各表中以AssociationDevList为例的残余信息. 如图所示拓扑结构中: 路由器1脱离网络后,通过协调器按键操作来 清除 协调 ...
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(22)-为用户设置角色
ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) (1):框架搭建 (2):数据库访问层的设计Demo (3):面向接口编程 (4 ):业务逻辑层的封装 ...
- jquery自定义对话框alert、confirm和prompt
jQuery Alert Dialogs,又一个基于jQuery的提示框插件,主要包括Alert.Confirm.prompt这三种,还有一个高级范例,可以在提示框内嵌入HTML语言,可以自定义风格样 ...
- 软件开发流程 Software development process
软件开发流程(Software development process)即软件设计思路和方法的一般过程,包括设计软件的功能和实现的算法和方法.软件的总体结构设计和模块设计.编程和调试.程序联调和测试以 ...
- ALV TREE中双击触发PAI事件的方法
用事件类实现双击事件,实例化后使用set handler注册到ALV对象.斜体部分为事件方法的具体实现. 代码如下 CLASS lcl_tree_event_receiver DEFINITION. ...