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 ...
随机推荐
- 从零学习SpringSecurity
一.简介 SpringSecurity是一个功能强大且高度可定制的身份验证和访问控制框架,和spring项目整合更加方便. 二.核心功能 认证(Authentication):指的是验证某个用户能否访 ...
- python import 导入两个模块同时有同一名称的方法如何调用 ?
from moudule import *(这种方法不推荐) 一般不推荐使用"from 模块 import"这种语法导入指定模块内的所有成员,因为它存在潜在的风险. 比如同时导入 ...
- SyntaxError: unexpected EOF while parsing成功解决
报错在eval()函数: 我加了个 if 判断是否为空,就可以正常运行了!
- 手把手教你在Modelarts平台上进行视频推理
摘要:为了方便小伙伴们进行视频场景的AI应用开发,Modelarts推理平台将视频推理场景中一些通用的流程抽取出来预置在基础镜像中,小伙伴们只需要简单地编写预处理及后处理脚本,便可以像开发图片类型的A ...
- Appearance-Based Loop Closure Detection for Online Large-Scale and Long-Term Operation
Abstract: 本文提出一种用于大规模的长期回环检测,基于一种内存管理方法:限制用于回环检测的位置数目,以满足实时性要求. introduction: 大场景存在的最关键问题:随着场景增大,回环检 ...
- React组件三大属性之 props
React组件三大属性之 props 理解1) 每个组件对象都会有props(properties的简写)属性2) 组件标签的所有属性都保存在props中 作用1) 通过标签属性从组件外向组件内传递变 ...
- informix错误代码小结
informix错误代码小结 所有错误可以用finderr+错误代码查到,英文的,这里中文注释便于理解. -100 错误的描述:C-ISAM错误:向具有唯一索引的字段加入一个重复值. 系统的操作:该 ...
- 《手把手教你》系列技巧篇(十一)-java+ selenium自动化测试-元素定位大法之By tag name(详细教程)
1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍By ClassName.看到ID,NAME这些方法的讲解,小伙伴们和童鞋们应该知道,要做好Web自动化测试,最好是需要了 ...
- Echarts的应用实践
Echarts官网:https://echarts.apache.org/ echarts是百度推出的,使用JavaScript实现的开源可视化库,可以提供直观.可定制化的数据可视化图表,包括折线图. ...
- [NOI 2021] 轻重边 题解
提供一种和不太一样的树剖解法(一下考场就会做了qwq),尽量详细讲解. 思路 设重边为黑色,轻边为白色. 首先,先将边的染色转化为点的染色(即将 \(u\) 节点连向父节点的边的颜色转化为 \(u\) ...