在实际工作中,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>

实现效果:

 
分类: .Net

WPF设计自定义控件的更多相关文章

  1. 在WPF中自定义控件

    一, 不一定需要自定义控件在使用WPF以前,动辄使用自定义控件几乎成了惯性思维,比如需要一个带图片的按钮,但在WPF中此类任务却不需要如此大费周章,因为控件可以嵌套使用以及可以为控件外观打造一套新的样 ...

  2. 在WPF中自定义控件(3) CustomControl (上)

    原文:在WPF中自定义控件(3) CustomControl (上) 在WPF中自定义控件(3) CustomControl (上)                              周银辉 ...

  3. 在WPF中自定义控件(1)

    原文:在WPF中自定义控件(1)    在WPF中自定义控件(1):概述                                                   周银辉一, 不一定需要自定 ...

  4. 在WPF中自定义控件(3) CustomControl (下)

    原文:在WPF中自定义控件(3) CustomControl (下)   在WPF中自定义控件(3) CustomControl (下)                                 ...

  5. 在WPF中自定义控件(2) UserControl

    原文:在WPF中自定义控件(2) UserControl 在WPF中自定义控件(2) UserControl                                               ...

  6. 在WPF设计工具Blend2中制作立方体图片效果

    原文:在WPF设计工具Blend2中制作立方体图片效果 ------------------------------------------------------------------------ ...

  7. [转]在WPF中自定义控件 UserControl

    在这里我们将将打造一个UserControl(用户控件)来逐步讲解如何在WPF中自定义控件,并将WPF的一些新特性引入到自定义控件中来.我们制作了一个带语音报时功能的钟表控件, 效果如下: 在VS中右 ...

  8. WPF 杂谈——自定义控件

    如果只是使用现有的WPF控件的话,是很难满足当前社会多复杂的业务.所以用户自己订制一系列控件也是一种不可避免的情势.WPF在控制方面分为俩种:用户控件和自定义控件.相信看过前面章节的就明白他们俩者之间 ...

  9. WPF设计界面不执行代码

    一般在我们在设计WPF XAML界面时,XAML 引用一些后端的类.比如UserControl.Converter.MVVM,引用 xmlns:ALLUserControl="clr-nam ...

随机推荐

  1. C语言:位运算符

    异或        ^     两个二进制位相同结果为0:不相同结果为1              1^1=0    1^0=1   0^1=1  0^0=1 按位或    |      两个二进制位 ...

  2. C语言:数的保存 原码 反码 补码

    a=6                                     a=-18 a 的原码就是0000 0000 0000 0110        1000 0000 0001 0010 ...

  3. 高校表白App-团队冲刺第一天

    今天要做什么 今天要再次重新的好好学一下Activity的生命周期,简单的写一个Activity,熟悉Activity的线程. 遇到的问题 在点击事件发生时,在activity进行finish()后, ...

  4. IDEA工具-快捷键整理

    在使用IDEA编辑器的过程中如果能够熟练的使用快捷键将大大的提高工作的效率,以下列为IDEA编辑器使用频率比较高的快捷键 Ctrl+E:显示最近编辑的文件列表 Ctrl+P:显示方法的参数信息 Ctr ...

  5. 什么是 BPMN ?为什么我们要用 BPMN 和工作流 ?

    BPMN 和 Activiti 介绍 工作流介绍 在任何行业和企业中,都有各种各样的流程,例如: 请假流程 报销流程 入职流程 离职流程 出差流程 等等-- 就算你自己没有设计过工作流,那么你每天肯定 ...

  6. DNS Bind日志详述

    简介 在默认情况下,BIND9 把日志消息写到 /var/log/messages 文件中,而这些日志消息是非常少的,主要就是启动,关闭的日志记录和一些严重错误的消息:而将调试日志信息写入 BIND ...

  7. C++第四十八篇 -- 字符串分离方法

    举例:Test_Bluetooth.exe -param_split Test_Bluetooth.cpp #include "pch.h" #include <iostre ...

  8. 一键设置WPS_Office_2019专业版的定时自动备份的批处理文件

    一键设置WPS_Office_2019专业版的定时自动备份的批处理文件 rem ================================================ rem 一键设置WPS ...

  9. 数据结构与算法 java描述 第一章 算法及其复杂度

    目录 数据结构与算法 java描述 笔记 第一章 算法及其复杂度 算法的定义 算法性能的分析与评价 问题规模.运行时间及时间复杂度 渐进复杂度 大 O 记号 大Ω记号 Θ记号 空间复杂度 算法复杂度及 ...

  10. 字节跳动Android春招,三轮面试,夺命连环问,心态崩了

    我是春招参加字节面试的,现在已经入职俩月啦,当时没有及时记录下来拖到现在...我尽量回忆当时的内容希望能帮到大家. 投的部门是深圳字节影像,不得不说这个部门的效率,上午投下午就接到hr的电话约面试时间 ...