WPF提供的控件在实际工作中并不能完全满足不同的设计要求。此时,我们需要设计自定义的控件。
LZ在这里总结了一些自己的想法,特点如下:
- Coupling
- UITemplate
- Behaviour
- Function Package
例如,我们经常在项目中使用调音器音量条,并写一个自定义控件来模拟调音器音量条。
自定义控件Singnallight,实现功能
- 从外部范围接收0~100的值
- 接收值实时显示
- 0~50数值范围为绿色,50~85为黄色,85~100为红色,无数值为棕色
- 可以在父控件上拖动控件
首先New WPF Application Project,将两个button放在Ui上,代码:
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.创建自定义控制器Singnalllightt
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.复制UITemplatetetet
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.接受值判断,Singnalllight通过IValueConverter和overider实现 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