WPF设计自定义控件
在实际工作中,WPF提供的控件并不能完全满足不同的设计需求。这时,需要我们设计自定义控件。
这里LZ总结一些自己的思路,特性如下:
- Coupling
- UITemplate
- Behaviour
- Function Package
下面举例说说在项目中我们经常用到调音台音量条,写一个自定义控件模拟调音台音量条。
自定义控件SingnalLight,实现功能
- 接收来自外部的范围0~100的数值
- 实时显示接收数值
- 数值范围0~50显示绿色,50~85显示黄色,85~100显示红色,没有数值显示褐色
- 可在父控件上拖拽该控件
首先New WPF Application Project,在Ui上放2个Button,代码:

1 <Grid>
2 <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
3 <Button Content="Start" Click="Start_Click"></Button>
4 <Button Content="Stop" Click="Stop_Click"></Button>
5 </StackPanel>
6 </Grid>

Start,Stop事件实现

1 private void Start_Click(object sender, RoutedEventArgs e) {
2 SignalManager.Instance.Start();
3 }
4
5 private void Stop_Click(object sender, RoutedEventArgs e) {
6 SignalManager.Instance.Stop();
7 }

这里创建一个SignalManager类,在Start时开启一个计时器,每隔1秒生成一个0~100的随机数,并作为模拟数值输出。
SignalManager类代码:

1 namespace SignalLightDemo.Business {
2 public class SignalManager : DependencyObject {
3 public static SignalManager Instance { get { return instance; } }
4
5 public int RandomA {
6 get { return (int)GetValue(RandomAProperty); }
7 set { SetValue(RandomAProperty, value); }
8 }
9
10 SignalManager() {
11 InitializationTimer();
12 }
13
14 public void Start() {
15 if (!timerA.Enabled) timerA.Start();
16 }
17
18 public void Stop() {
19 if (timerA.Enabled) timerA.Stop();
20 }
21
22 private void InitializationTimer() {
23 timerA = new Timer();
24 timerA.Interval = INTERVAL;
25 timerA.Elapsed += timerA_Elapsed;
26 }
27
28 void timerA_Elapsed(object sender, ElapsedEventArgs e) {
29 this.Dispatcher.BeginInvoke(new Action(() => {
30 RandomA = a.Next(MAX_VALUE);
31 }));
32 }
33
34 public static readonly DependencyProperty RandomAProperty =
35 DependencyProperty.Register("RandomA", typeof(int), typeof(SignalManager), new PropertyMetadata(0));
36
37 private Random a = new Random((int)DateTime.Now.Ticks);
38 private const int MAX_VALUE = 100;
39 private const double INTERVAL = 1000;
40 private Timer timerA;
41 private static SignalManager instance = new SignalManager();
42 }
43 }

下面来重点:
1.创建自定义控件SingnalLight

1 public class SingnalLight : ContentControl {
2 public int ValueA {
3 get { return (int)GetValue(ValueAProperty); }
4 set { SetValue(ValueAProperty, value); }
5 }
6
7
8 public SingnalLight() {
9 this.AllowDrop = true;
10 }
11
12
13 static SingnalLight() {
14 DefaultStyleKeyProperty.OverrideMetadata(typeof(SingnalLight), new FrameworkPropertyMetadata(typeof(SingnalLight)));
15 }
16
17
18 }

ValueA为接受外部数值的属性
2.复写控件UITemplate

1 <Style TargetType="{x:Type control:SingnalLight}">
2 <Setter Property="RenderTransform">
3 <Setter.Value>
4 <TranslateTransform X="{Binding Path=X,RelativeSource={RelativeSource AncestorType={x:Type control:SingnalLight}}}"
5 Y="{Binding Path=Y,RelativeSource={RelativeSource AncestorType={x:Type control:SingnalLight}}}"/>
6 </Setter.Value>
7 </Setter>
8 <Setter Property="Template">
9 <Setter.Value>
10 <ControlTemplate>
11 <ControlTemplate.Resources>
12 <control:SingnalLightStatusConverter x:Key="colorconverter"></control:SingnalLightStatusConverter>
13 <control:SingnalLightValueConverter x:Key="valueconverter"></control:SingnalLightValueConverter>
14 </ControlTemplate.Resources>
15 <StackPanel>
16 <TextBlock Text="{Binding Path=ValueA,RelativeSource={RelativeSource AncestorType={x:Type control:SingnalLight}}}"></TextBlock>
17 <TextBlock Text="100"></TextBlock>
18 <Border
19 x:Name="bd1"
20 Height="{Binding Path=LightHeight,RelativeSource={RelativeSource AncestorType={x:Type control:SingnalLight}}}"
21 SnapsToDevicePixels="True"
22 BorderBrush="Black" BorderThickness="1" Background="Transparent">
23 <Rectangle Fill="{Binding Path=ValueA,
24 RelativeSource={RelativeSource AncestorType={x:Type control:SingnalLight}},
25 Converter={StaticResource ResourceKey=colorconverter}}"
26 VerticalAlignment="Bottom">
27 <Rectangle.Height>
28 <MultiBinding Converter="{StaticResource ResourceKey=valueconverter}">
29 <Binding Path="ValueA" RelativeSource="{RelativeSource AncestorType={x:Type control:SingnalLight}}"></Binding>
30 <Binding Path="Height" ElementName="bd1"></Binding>
31 </MultiBinding>
32 </Rectangle.Height>
33 </Rectangle>
34 </Border>
35 <TextBlock Text="0"></TextBlock>
36 </StackPanel>
37 </ControlTemplate>
38 </Setter.Value>
39 </Setter>
40 </Style>

3.接受值判断,SingnalLight通过实现IValueConverter和Override Arrange & Measure Methods,实现了UI呈现的绑定,

1 public class SingnalLightStatusConverter : IValueConverter {
2 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
3 SolidColorBrush result = Brushes.Transparent;
4 if (value.GetType() == typeof(int)) {
5 var color = System.Convert.ToInt32(value);
6 if (color < 50) result = Brushes.Green;
7 else if (color < 85 && color >= 50) result = Brushes.Yellow;
8 else if (color <= 100 && color >= 85) result = Brushes.Red;
9 else result = Brushes.Gray;
10 }
11 return result;
12 }
13
14 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
15 throw new NotImplementedException();
16 }
17 }
18
19 public class SingnalLightValueConverter : IMultiValueConverter {
20 public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
21 double result = 0;
22 if (values[0].GetType() == typeof(int) && values[1].GetType() == typeof(double)) {
23 result = (double)values[1] / 100 * System.Convert.ToDouble(values[0]);
24 }
25 return result;
26 }
27
28 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
29 throw new NotImplementedException();
30 }
31 }


1 protected override Size MeasureOverride(Size constraint) {
2 if (ActualHeight > 0) LightHeight = ActualHeight * .7;
3 return base.MeasureOverride(constraint);
4 }
5
6 protected override Size ArrangeOverride(Size arrangeBounds) {
7 return base.ArrangeOverride(arrangeBounds);
8 }

4.控件支持拖拽,覆写MouseDown,MouseMove,MouseUp方法。这样写的好处是,如果在父控件的事件中实现Drag,父控件如果有多个对象,这样逻辑会十分混乱。

1 protected override void OnMouseMove(MouseEventArgs e) {
2 base.OnMouseMove(e);
3 if (e.LeftButton == MouseButtonState.Pressed) {
4 _currentPoint = e.GetPosition(this);
5 X += _currentPoint.X - _startPoint.X;
6 Y += _currentPoint.Y - _startPoint.Y;
7 }
8 }
9
10 protected override void OnMouseDown(MouseButtonEventArgs e) {
11 base.OnMouseDown(e);
12 _startPoint = e.GetPosition(this);
13 this.CaptureMouse();
14 }
15
16 protected override void OnMouseUp(MouseButtonEventArgs e) {
17 base.OnMouseUp(e);
18 this.ReleaseMouseCapture();
19 }

SingnalLight完整代码:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Windows;
7 using System.Windows.Controls;
8 using System.Windows.Data;
9 using System.Windows.Input;
10 using System.Windows.Media;
11
12 namespace SignalLightDemo.Control {
13 public class SingnalLight : ContentControl {
14 public int ValueA {
15 get { return (int)GetValue(ValueAProperty); }
16 set { SetValue(ValueAProperty, value); }
17 }
18
19 public double LightHeight {
20 get { return (double)GetValue(LightHeightProperty); }
21 set { SetValue(LightHeightProperty, value); }
22 }
23
24 public double X {
25 get { return (double)GetValue(XProperty); }
26 set { SetValue(XProperty, value); }
27 }
28
29 public double Y {
30 get { return (double)GetValue(YProperty); }
31 set { SetValue(YProperty, value); }
32 }
33
34 public SingnalLight() {
35 this.AllowDrop = true;
36 }
37
38 protected override void OnMouseMove(MouseEventArgs e) {
39 base.OnMouseMove(e);
40 if (e.LeftButton == MouseButtonState.Pressed) {
41 _currentPoint = e.GetPosition(this);
42 X += _currentPoint.X - _startPoint.X;
43 Y += _currentPoint.Y - _startPoint.Y;
44 }
45 }
46
47 protected override void OnMouseDown(MouseButtonEventArgs e) {
48 base.OnMouseDown(e);
49 _startPoint = e.GetPosition(this);
50 this.CaptureMouse();
51 }
52
53 protected override void OnMouseUp(MouseButtonEventArgs e) {
54 base.OnMouseUp(e);
55 this.ReleaseMouseCapture();
56 }
57
58 protected override Size MeasureOverride(Size constraint) {
59 if (ActualHeight > 0) LightHeight = ActualHeight * .7;
60 return base.MeasureOverride(constraint);
61 }
62
63 protected override Size ArrangeOverride(Size arrangeBounds) {
64 return base.ArrangeOverride(arrangeBounds);
65 }
66
67 static SingnalLight() {
68 DefaultStyleKeyProperty.OverrideMetadata(typeof(SingnalLight), new FrameworkPropertyMetadata(typeof(SingnalLight)));
69 }
70
71 public static readonly DependencyProperty LightHeightProperty =
72 DependencyProperty.Register("LightHeight", typeof(double), typeof(SingnalLight), new PropertyMetadata((double)0));
73
74 public static readonly DependencyProperty ValueAProperty =
75 DependencyProperty.Register("ValueA", typeof(int), typeof(SingnalLight), new PropertyMetadata(0));
76
77 public static readonly DependencyProperty XProperty =
78 DependencyProperty.Register("X", typeof(double), typeof(SingnalLight), new PropertyMetadata((double)0));
79
80 public static readonly DependencyProperty YProperty =
81 DependencyProperty.Register("Y", typeof(double), typeof(SingnalLight), new PropertyMetadata((double)0));
82
83 private Point _startPoint;
84 private Point _currentPoint;
85 }
86
87 public class SingnalLightStatusConverter : IValueConverter {
88 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
89 SolidColorBrush result = Brushes.Transparent;
90 if (value.GetType() == typeof(int)) {
91 var color = System.Convert.ToInt32(value);
92 if (color < 50) result = Brushes.Green;
93 else if (color < 85 && color >= 50) result = Brushes.Yellow;
94 else if (color <= 100 && color >= 85) result = Brushes.Red;
95 else result = Brushes.Gray;
96 }
97 return result;
98 }
99
100 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
101 throw new NotImplementedException();
102 }
103 }
104
105 public class SingnalLightValueConverter : IMultiValueConverter {
106 public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
107 double result = 0;
108 if (values[0].GetType() == typeof(int) && values[1].GetType() == typeof(double)) {
109 result = (double)values[1] / 100 * System.Convert.ToDouble(values[0]);
110 }
111 return result;
112 }
113
114 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
115 throw new NotImplementedException();
116 }
117 }
118 }

界面调用:

1 <Window x:Class="SignalLightDemo.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:control="clr-namespace:SignalLightDemo.Control"
5 xmlns:business="clr-namespace:SignalLightDemo.Business"
6 Title="SignalLight" Height="600" Width="800">
7 <Grid>
8 <control:SingnalLight Width="50" Height="300"
9 ValueA="{Binding Source={x:Static business:SignalManager.Instance},Path=RandomA}"></control:SingnalLight>
10 <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
11 <Button Content="Start" Click="Start_Click"></Button>
12 <Button Content="Stop" Click="Stop_Click"></Button>
13 </StackPanel>
14 </Grid>
15 </Window>

实现效果:
WPF设计自定义控件的更多相关文章
- 在WPF中自定义控件
一, 不一定需要自定义控件在使用WPF以前,动辄使用自定义控件几乎成了惯性思维,比如需要一个带图片的按钮,但在WPF中此类任务却不需要如此大费周章,因为控件可以嵌套使用以及可以为控件外观打造一套新的样 ...
- 在WPF中自定义控件(3) CustomControl (上)
原文:在WPF中自定义控件(3) CustomControl (上) 在WPF中自定义控件(3) CustomControl (上) 周银辉 ...
- 在WPF中自定义控件(1)
原文:在WPF中自定义控件(1) 在WPF中自定义控件(1):概述 周银辉一, 不一定需要自定 ...
- 在WPF中自定义控件(3) CustomControl (下)
原文:在WPF中自定义控件(3) CustomControl (下) 在WPF中自定义控件(3) CustomControl (下) ...
- 在WPF中自定义控件(2) UserControl
原文:在WPF中自定义控件(2) UserControl 在WPF中自定义控件(2) UserControl ...
- 在WPF设计工具Blend2中制作立方体图片效果
原文:在WPF设计工具Blend2中制作立方体图片效果 ------------------------------------------------------------------------ ...
- [转]在WPF中自定义控件 UserControl
在这里我们将将打造一个UserControl(用户控件)来逐步讲解如何在WPF中自定义控件,并将WPF的一些新特性引入到自定义控件中来.我们制作了一个带语音报时功能的钟表控件, 效果如下: 在VS中右 ...
- WPF 杂谈——自定义控件
如果只是使用现有的WPF控件的话,是很难满足当前社会多复杂的业务.所以用户自己订制一系列控件也是一种不可避免的情势.WPF在控制方面分为俩种:用户控件和自定义控件.相信看过前面章节的就明白他们俩者之间 ...
- WPF设计界面不执行代码
一般在我们在设计WPF XAML界面时,XAML 引用一些后端的类.比如UserControl.Converter.MVVM,引用 xmlns:ALLUserControl="clr-nam ...
随机推荐
- DHCP与配置命令
1. DHCP简介 2. DHCP主要用途 3. 使用DHCP的好处 4.DHCP经典应用模式 5.DHCP交互过程 DHCP的IP地址自动获取工作原理 6.DHCP中继 应用场景 工作原理 ...
- 【剑指offer】51.构建乘积数组
51.构建乘积数组 知识点:数组: 题目描述 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0] * A[1] *... * A[i ...
- 微信小程序云开发-数据库-商品列表数据显示N条数据
一.wxml文件 在wxml文件中,写页面和点击事件,添加绑定事件limitGoods 二.js文件 在js文件中写limitGoods(),使用.limit(3)表示只显示3条数据
- 小程序框架WePY 从入门到放弃踩坑合集
小程序框架WePY 从入门到放弃踩坑合集 一点点介绍WePY 因为小程序的语法设计略迷, 所以x1 模块化起来并不方便, 所以x2 各厂就出了不少的框架用以方便小程序的开发, 腾讯看到别人家都出了框架 ...
- 第五篇--Chorme浏览器主页被篡改
解决方法:关闭谷歌浏览器,右击桌面快捷方式,查看属性,然后将target后面的网址删掉.并且任务栏的google打开方式,最好也把流氓网址删掉.之后就正常了.
- Idea快捷键 累积大全
分类 Editing 这个 Searching/Replcae Navigation Atl +1 打开和关闭左侧p ...
- Chrome添加 测试设备 手机iPhone x系列手机
一.F12,点击下图中的Edit,添加设备 二.添加名为iPhone XR的设备,设备配置如下 三.其中User agent string如下 Mozilla/5.0 (iPhone; CPU iPh ...
- js问题记录
1.aixos请求响应302重定向时无法获取返回数据, 解决方法:在请求头中添加 headers: { 'X-Requested-With': 'XMLHttpRequest' },
- 通过jstack日志分析和问题排查
简介 jstack用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁.死循环 ...
- .net core连接Liunx上MS SQL Server
场景 由于业务要求,需要对甲方的一个在SQL Server上的财务表进行插入操作.研究了半天,因为一个小问题折腾了很久. 过程 .net core端: 1. 利用EF,就需要的导入相关的Nuget包, ...