WPF开发快速入门【4】自定义控件与用户控件
概述
本文描述WPF的自定义控件和用户控件。
自定义控件
前面文章介绍了WPF的ControlTemplate,当我们对系统控件自带的样式不太满意时,我们可以通过控件模板自定义用户的样式,以Button为例,我们可以设计一个圆形的按钮,并通过触发器控制一些动态效果。在使用控件模板时,我们通过TemplateBinding来引用控件的一些属性,这个属性的范围仅限于Button本身所拥有的属性。
如果我想设计一款带图片的按钮,通过控件模板就实现不了了,因为这个图片按钮的控件应该具备一个类似Image这样的属性,但Button控件没有这个属性,所以就实现不了我们想要的功能了。
这时候可以使用自定义控件来解决问题。
我们新建一个ImageButton的自定义控件,系统自动生成一个类文件和一个样式文件:
//ImageButton.cs
public class ImageButton : Control
{
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
} //Generic.xaml
<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
首先我们需要把ImageButton 父类修改为Button,表示这个控件功能继承于Button,然后我们为这个类增加一个Image属性
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
} public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(null));
此时,这个控件的后台代码就完成了。
然后我们再仔细看看Generic.xaml中对于local:ImageButton这个控件的样式描述,这不就是修改它的控件模板吗?不过此时,除了Button的现有属性,我们还多了一个Image属性可以使用。
下面我们完善一下这个控件模板的描述。
<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="Gray"
BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Image Source="{TemplateBinding Image}"/>
<Label Content="{TemplateBinding Content}" VerticalAlignment="Center"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这样一个自定义控件就做好了。

和控件模板一样,我们还是可以通过Trigger控制控件的一些动态效果:
<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Border x:Name="border">
...
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="LightBlue" />
<Setter TargetName="border" Property="BorderBrush" Value="Gray" />
<Setter TargetName="image" Property="Margin" Value="2" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Background" Value="LightGray" />
<Setter TargetName="image" Property="Opacity" Value="0.2" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
用户控件
用户控件比较简单,就是通过一些现有控件的组合,形成一个可以通用的控件。例如:通过组合一个加号的图片、一个减号的图片、一个文本框,我们可以组合一个NumericUpDown控件。
<UserControl x:Class="LearnWPF.Controls.NumericUpDown">
<Border BorderThickness="1" BorderBrush="LightGray" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" >
<Image Source="Images/Subtract.png" Margin="4"
MouseDown="ButtonSubtract_Click">
</Image>
</Border>
<TextBox x:Name="txtValue" Text="1.220"
TextChanged="txtValue_TextChanged"/>
<Border Grid.Column="2">
<Image Source="Images/Add.png" Margin="4"
MouseDown="ButtonAdd_Click">
</Image>
</Border>
</Grid>
</Border>
</UserControl>
我们可以给控件增加一个Value的属性,然后当用户点击图片按钮时,我们修改Value的值即可。
private void ButtonAdd_Click(object sender, RoutedEventArgs e)
{
Value += Increment;
} private void ButtonSubtract_Click(object sender, RoutedEventArgs e)
{
Value -= Increment;
}
此时,这个用户控件就开发完成了。但它不支持MVVM模式,因为Value属性不是依赖属性,我们需要把Value属性定义为依赖属性:
public decimal Value
{
get { return (decimal)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
} public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(decimal), typeof(NumericUpDown), new UIPropertyMetadata(new decimal(), ValuePropertyChanged)); private static void ValuePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)
{
NumericUpDown control = obj as NumericUpDown;
decimal Data = (decimal)arg.NewValue;
control.txtValue.Text = Math.Round(Data, control.DecimalPlaces).ToString();
}
此时Value属性就支持MVVM,而且支持双向Binding模式
<xyc:NumericUpDown Value="{Binding Hour,Mode=TwoWay}" Minimum="0" Maximum="23"/>
由于 Minimum和Maximum不是依赖属性,只能直接幅值,不能绑定。
自绘的用户控件
有时候我们需要做一些很奇怪的控件,如下:

这个也是通过用户控件实现。我的经验是:在用户控件里放一个Image控件,缩放模式设置为Fill,然后在后台画个图并复值给Image控件即可。
在绘图时,由于用户会调整控件尺寸,所以绘图控件的定位是比较麻烦的,我们可以画一个指定尺寸的图,然后让Image来处理缩放,这就比较简单了。
设计代码:
<UserControl x:Class="LearnWPF.Controls.Clock"
<Grid>
<Image x:Name="imageBitmap" Stretch="Fill" />
</Grid>
</UserControl>
后台代码:
public partial class Clock : UserControl
{ public Clock()
{
InitializeComponent();
this.Loaded += Clock_Loaded;
} private void Clock_Loaded(object sender, RoutedEventArgs e)
{
DrawImage();
} private void DrawImage()
{
DrawingGroup group = new DrawingGroup();
using (DrawingContext ctx = group.Open())
{
DrawImageByContext(ctx);
}
group.Freeze();
DrawingImage drawimage = new DrawingImage(group);
this.imageBitmap.Source = drawimage;
} private void DrawImageByContext(DrawingContext ctx)
{
int PicWidth = 500;
int PicHeight = 500;
ctx.DrawRectangle(Brushes.Transparent, null, new Rect(0, 0, PicWidth, PicHeight));
//拿着ctx尽情绘图吧
}
}
资源
系列目录:WPF开发快速入门【0】前言与目录
代码下载:Learn WPF: WPF学习笔记 (gitee.com)
WPF开发快速入门【4】自定义控件与用户控件的更多相关文章
- WPF开发快速入门【7】WPF的拖放功能(Drag and Drop)
概述 本文描述WPF的拖放功能(Drag and Drop). 拖放功能涉及到两个功能,一个就是拖,一个是放.拖放可以发生在两个控件之间,也可以在一个控件自己内部拖放.假设界面上有两个控件,一个Tre ...
- C# 自定义控件VS用户控件
1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Con ...
- 自定义控件VS用户控件
自定义控件VS用户控件 2015-06-16 1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container ...
- C#自定义控件、用户控件、动态加载菜单按钮
一.效果图,动态加载5个菜单按钮: 二.实现方法 1.创建用户控件 2.在用户控件拖入toolStrip 3.进入用户控件的Lood事件,这里自动添加5个选 ToolStripMenuItem,后期 ...
- VS2010下WPF开发ARCGIS ENGINE 10的带Ribbon控件项目
原文 http://blog.sina.com.cn/s/blog_47522f7f0100nq5t.html 题目好长,但是集目前最新的工具于一身..VS是最新的2010版,不过用的是.net3.5 ...
- HealthKit开发快速入门教程之HealthKit数据的操作
HealthKit开发快速入门教程之HealthKit数据的操作 数据的表示 在HealthKit中,数据是最核心的元素.通过分析数据,人们可以看到相关的健康信息.例如,通过统计步数数据,人们可以知道 ...
- HealthKit开发快速入门教程之HealthKit框架体系创建健康AppID
HealthKit开发快速入门教程之HealthKit框架体系创建健康AppID HealthKit开发准备工作 在开发一款HealthKit应用程序时,首先需要讲解HealthKit中有哪些类,在i ...
- HealthKit开发快速入门教程之HealthKit开发概述简介
HealthKit开发快速入门教程之HealthKit开发概述简介 2014年6月2日召开的年度开发者大会上,苹果发布了一款新的移动应用平台,可以收集和分析用户的健康数据.该移动应用平台被命名为“He ...
- Apple Watch开发快速入门教程
Apple Watch开发快速入门教程 试读下载地址:http://pan.baidu.com/s/1eQ8JdR0 介绍:苹果为Watch提供全新的开发框架WatchKit.本教程是国内第一本A ...
- WPF/MVVM Quick Start Tutorial - WPF/MVVM 快速入门教程 -原文,翻译及一点自己的补充
转载自 https://www.codeproject.com/articles/165368/wpf-mvvm-quick-start-tutorial WPF/MVVM Quick Start T ...
随机推荐
- HarmonyOS Connect认证测试
原文链接:https://mp.weixin.qq.com/s/zRG97PWPqfDo0vfwQWSUew,点击链接查看更多技术内容: 在HarmonyOS Connect生态产品的认证测试过 ...
- C++ 递归与面向对象编程基础
C++ 递归 递归是一种使函数调用自身的技术.这种技术提供了一种将复杂问题分解为简单问题的方法,从而更容易解决问题. 递归可能有点难以理解.理解其工作原理的最佳方法是通过实验来尝试. 递归示例 将两个 ...
- 力扣184(MySQL)-部门工资最高的员工(中等)
题目: 表: Employee 表: Department 编写SQL查询以查找每个部门中薪资最高的员工.按 任意顺序 返回结果表.查询结果格式如下例所示. 解题思路: 方法一:窗口函数和多表联结 ...
- 龙蜥开源Plugsched:首次实现 Linux kernel 调度器热升级 | 龙蜥技术
简介:对于plugsched而言,无论是 bugfix,还是性能优化,甚至是特性的增.删.改,都可胜任. 文/龙蜥社区内核开发人员 陈善佩.吴一昊.邓二伟 Plugsched 是 Linux 内 ...
- dotnet 6 HttpClientHandler 和 SocketsHttpHandler 有什么差别
本文来告诉大家在 dotnet 6 的 HttpClientHandler 和 SocketsHttpHandler 两个类型有什么不同 在创建 HttpClient 时,可以在 HttpClient ...
- K8s包管理工具Helm v3(19)
一.Helm概述 官网:https://v3.helm.sh/zh/docs/ https://helm.sh/ helm 官方的 chart 站点: https://hub.kubeapps.com ...
- Pod入门知识(4)
一.Pod是什么? 官方文档:https://kubernetes.io/docs/concepts/workloads/pods/ Pod 是 Kubernetes 中的最小调度单元,k8s 是通过 ...
- Java获取电脑盘符(最后一个盘符)
//遍历获得所有盘符 File[] roots = File.listRoots(); for (int i =0; i < roots.length; i++) { System.out.pr ...
- Unity 热更--AssetBundle学习笔记 1.0【AB包资源加载工具类的实现】
工具类封装 通过上文中对AB包加载API的了解和简单使用,对AB包资源加载的几种方法进行封装,将其写入单例类中,如代码展示. 确保每个AB资源包只加载一次: 在LoadAssetBundleManag ...
- 【爬虫数据集】滇西小哥YouTube频道TOP10热门视频的热评数据,共2W条!
目录 一.背景介绍 二.爬取目标 三.结果展示 四.演示视频 五.附完整数据 一.背景介绍 滇西小哥是一位来自中国云南省的视频博主,他在YouTube上拥有超过1000万的订阅者和上亿的观看量.他的视 ...