今天看到了老赵的一片博客:编写一个“绑定友好”的WPF控件

文章里面遇到的问题,蛮有意思,后面的评论,非常精彩,没看过的,推荐看一下。

由于我也做过类似的需求,所以,贴出我当时的做法和现在的想法,仅仅是笔记,没有其他意思。

当时的需求类似这样子,实际做的效果当然比这个漂亮的多。

看到这个UI的第一反应就是,封装一个控件,把slider包进去,很简单的吧。

当时的做法,在CS代码里面封装几个DP,绑定就完事儿了呀。

<UserControl x:Class="WpfControlTest.UCSlider"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="250" Height="45"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  x:Name="main"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  ><Grid><TextBlock  Text="{Binding ElementName=main,Path=Text}" Margin="0,0,0,0" Foreground="Black" VerticalAlignment="Top"  HorizontalAlignment="Left"/><TextBlock  Text="{Binding  ElementName=sl,Path=Value,StringFormat=0}" Foreground="Black" HorizontalAlignment="Right"/><Slider   Name="sl"   Width="{Binding Path=Width,ElementName=main}" VerticalAlignment="Bottom" Cursor="Hand"  Maximum="{Binding ElementName=main,Path=Maximum}" Minimum="{Binding ElementName=main,Path=Minimum}" Value="{Binding ElementName=main,Path=SliderValue}" /></Grid>
</UserControl>

跟老赵那个类似,实际的使用场景也就是这么回事,cs代码当时根本就没想到用MVVM,基本没有逻辑,没必要分离。
只是需要绑定,所以DP少不了的。比较简单,要细看的自己展开。

CS code

 1  /// <summary>
 2     /// when value changed,use eventhandle to notify
 3     /// </summary>
 4     /// <param name="value"></param>
 5     public delegate void CustomSliderEventHandle(double value);
 6
 7     public partial class UCSlider : UserControl
 8     {
 9         public UCSlider()
10         {
11             InitializeComponent();
12             sl.ValueChanged += new RoutedPropertyChangedEventHandler<double>(sl_ValueChanged);
13         }
14
15         #region event
16         public event CustomSliderEventHandle ValueChange;
17
18         void sl_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
19         {
20             if (ValueChange != null)
21                 ValueChange(sl.Value);
22         }
23         #endregion
24
25         #region Text
26         public string Text
27         {
28             get { return (string)GetValue(TextProperty); }
29             set { SetValue(TextProperty, value); }
30         }
31
32         public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UCSlider));
33         #endregion
34
35         #region Maximum
36         public double Maximum
37         {
38             get { return (double)GetValue(MaximumProperty); }
39             set { SetValue(MaximumProperty, value); }
40         }
41
42         public static DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(UCSlider));
43         #endregion
44
45         #region Minimum
46         public double Minimum
47         {
48             get { return (double)GetValue(MinimumProperty); }
49             set { SetValue(MinimumProperty, value); }
50         }
51
52         public static DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(UCSlider));
53         #endregion
54
55         #region SliderValue
56         public double SliderValue
57         {
58             get { return (double)GetValue(SliderValueProperty); }
59             set { SetValue(SliderValueProperty, value); }
60         }
61
62         public static DependencyProperty SliderValueProperty = DependencyProperty.Register("SliderValue", typeof(double), typeof(UCSlider));
63         #endregion
64     }

没有用MVVM,所以比老赵那个简单了不少。
到了调用的地方,貌似是搞个ViewModel的时候,其实只是用到了NotifyPropertyChanged,因为这个玩起来爽啊。

UI:

 <StackPanel HorizontalAlignment="Left" Margin="10,10,0,0" ><UC:UCSlider Text="Brightness" Maximum="100" Minimum="0"  SliderValue="{Binding Path=Brightness,Mode=TwoWay}"  ></UC:UCSlider><UC:UCSlider Text="Contrast" Maximum="100" Minimum="0"  SliderValue="{Binding Path=Contrast,Mode=TwoWay}"/><UC:UCSlider Text="Hue" Maximum="2" Minimum="-2"  SliderValue="{Binding Path=Hue,Mode=TwoWay}"/></StackPanel>

CS:

CS Code

 1  /// <summary>
 2     /// Interaction logic for MainWindow.xaml
 3     /// </summary>
 4     public partial class MainWindow : Window
 5     {
 6         TurningViewModel _turningViewModel = new TurningViewModel() { Hue = 1, Brightness = 20, Contrast = 2 };
 7         public MainWindow()
 8         {
 9             InitializeComponent();
10             this.DataContext = _turningViewModel;
11             _turningViewModel.PropertyChanged += new PropertyChangedEventHandler(_turningViewModel_PropertyChanged);
12         }
13
14         void _turningViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
15         {
16             Debug.WriteLine(string.Format("PropertyName:{0}", e.PropertyName));
17         }
18
19         private void get_Click(object sender, RoutedEventArgs e)
20         {
21             Debug.WriteLine(string.Format("Hue:{0}", _turningViewModel.Hue));
22             Debug.WriteLine(string.Format("Brightness:{0}", _turningViewModel.Brightness));
23             Debug.WriteLine(string.Format("Contrast:{0}", _turningViewModel.Contrast));
24         }
25     }
26
27     public class TurningViewModel : INotifyPropertyChanged
28     {
29         #region Hue
30         private double _hue;
31         public double Hue
32         {
33             get { return _hue; }
34             set
35             {
36                 if (_hue != value)
37                 {
38                     _hue = value;
39                     NotifyPropertyChanged("Hue");
40                 }
41             }
42         }
43         #endregion
44
45         #region Brightness
46         private double _brightness;
47         public double Brightness
48         {
49             get { return _brightness; }
50             set
51             {
52                 if (_brightness != value)
53                 {
54                     _brightness = value;
55                     NotifyPropertyChanged("Brightness");
56                 }
57             }
58         }
59         #endregion
60
61         #region Contrast
62         private double _contrast;
63         public double Contrast
64         {
65             get { return _contrast; }
66             set
67             {
68                 if (_contrast != value)
69                 {
70                     _contrast = value;
71                     NotifyPropertyChanged("Contrast");
72                 }
73             }
74         }
75         #endregion
76
77         void NotifyPropertyChanged(string propertyName)
78         {
79             if (PropertyChanged != null)
80             {
81                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
82             }
83         }
84
85         public event PropertyChangedEventHandler PropertyChanged;
86     }

基本上就结束了呀。
如果要把这个控件做成自定义控件,继承自Control的,

XAML:

View Code

 1  <Style TargetType="{x:Type local:CCSlider}">
 2     <Setter Property="Template">
 3       <Setter.Value>
 4         <ControlTemplate TargetType="{x:Type local:CCSlider}">
 5           <Border Background="{TemplateBinding Background}"
 6                             BorderBrush="{TemplateBinding BorderBrush}"
 7                             BorderThickness="{TemplateBinding BorderThickness}">
 8             <Grid Width="200" Height="45">
 9               <TextBlock  Text="{TemplateBinding Text}" Margin="0,0,0,0" Foreground="Black" VerticalAlignment="Top"  HorizontalAlignment="Left"/>
10               <TextBlock  Text="{Binding  ElementName=sl,Path=Value,StringFormat=0}" Foreground="Black" HorizontalAlignment="Right"/>
11               <Slider   Name="sl"   Width="{Binding Path=Width,ElementName=main}" VerticalAlignment="Bottom" Cursor="Hand"
12                           Maximum="{TemplateBinding Maximum}"
13                           Minimum="{TemplateBinding Minimum}"
14                           Value="{Binding Path=SliderValue,Mode=TwoWay,RelativeSource={RelativeSource Mode=TemplatedParent}}" />
15             </Grid>
16           </Border>
17         </ControlTemplate>
18       </Setter.Value>
19     </Setter>
20   </Style>

CS 代码和 上面那个一样的。

View Code

 1     public class CCSlider : Control
 2     {
 3         static CCSlider()
 4         {
 5             DefaultStyleKeyProperty.OverrideMetadata(typeof(CCSlider), new FrameworkPropertyMetadata(typeof(CCSlider)));
 6         }
 7
 8
 9         #region Text
10         public string Text
11         {
12             get { return (string)GetValue(TextProperty); }
13             set { SetValue(TextProperty, value); }
14         }
15
16         public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CCSlider));
17         #endregion
18
19         #region Maximum
20         public double Maximum
21         {
22             get { return (double)GetValue(MaximumProperty); }
23             set { SetValue(MaximumProperty, value); }
24         }
25
26         public static DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(CCSlider));
27         #endregion
28
29         #region Minimum
30         public double Minimum
31         {
32             get { return (double)GetValue(MinimumProperty); }
33             set { SetValue(MinimumProperty, value); }
34         }
35
36         public static DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(CCSlider));
37         #endregion
38
39         #region SliderValue
40         public double SliderValue
41         {
42             get { return (double)GetValue(SliderValueProperty); }
43             set { SetValue(SliderValueProperty, value); }
44         }
45
46         public static DependencyProperty SliderValueProperty = DependencyProperty.Register("SliderValue", typeof(double), typeof(CCSlider));
47         #endregion
48     }

顺便唠叨:

1. 控件自定义有两种:

一种是和逻辑相关的,里面可以搞搞ViewModel,分离一下。如果分离,DataContext必须指明,要不然和老赵的那个错误类似了。如果逻辑不复杂,就没那个必要了,直接balala一搞就完事儿了。
如果业务粒度分的细,逻辑比较单纯的,还是不要费那个事儿了,毕竟ViewModel的代码量大啊,我承认自己懒。
这个ASP.net的模板页面套子页面一个道理。
这一种用UserControl搞搞,方便,感觉就是为了分割逻辑用的。

另外一种是和逻辑无关的,例如三态按钮,要大量复用,或者要做类似RadioButton的效果,只有一个被选中。
那就继承自Control吧。

这个区分方法也不是严格定义的,看实际需求吧。

2.再来说MVVM,关于这个东东的争论貌似也不少,快赶上 JAVA和C#的争论了。
不少的WPF开发者,潜意识里面,随便写个东东都MVVM,不管大大小小都搞个ViewModel。
有时候.xaml.cs里面几乎啥都没放,ViewModel的代码倒是急剧膨胀,何苦啊。
MVVM的理论解释,还是有微软的说法,等等,都在MSDN里面,不再贴出来了。
只说说自己的体会:

刚开始搞WPF的时候,从ASP.net转过来的,WinForm也玩过,习惯了代码搞定一切。
于是乎,套用,几乎一切都是代码搞定,因为这个相对熟悉,而且调试方便啊。
后来项目凑合着搞完了,开始静下心来看看WPF,看看MVVM,看看绑定啊等等东西。|
也做了Demo,发现如果熟悉XAML,挺好,一句可以代替CS代码好多句,清爽,简练,不熟悉,苦逼的事情很多。
MVVM,第一次用是在  数据的增删改,新增家具,要填写一堆的属性,家具列表,修改家具...
按照之前的做法,写个类,一个一个属性的绑定,保存的时候,一个一个的获取...,你懂的,十几个属性,会死人的。
用MVVM,只要在XAML里面绑定数据,UI逻辑需要访问控件的,在.xaml.cs中写一下,数据加工逻辑不会涉及控件的都在ViewModel中,获取数据的时候直接取ViewModel。
这是我发现的最最适用的场合。

我想到最初做Winform的时候,有个叫DataGrid(名字不太确定)的控件,直接可以编辑表格内容,排序,增删改都集成了。
当时是医药销售行业,老大们把业务逻辑封装在存储过程中,UI就直接拿DataGrid,拖过来,给个数据源,保存的时候取一下数据,完事儿了。
并且卖钱了,还不少,这就是MS最牛叉的地方,直接用,直接卖钱。

数据增删改,是直接产生效益的地方,一般的销售类软件都需要这个功能,微软貌似就在这里下功夫,Asp.net 和Winfor都是,WPF的MVVM也是。

再回头说说做的第一个项目,后来准备重构,弄个MVVM上去,发现不爽。
这个项目类似涂鸦墙,文字、图片、Flash等等可以随意的放在画布上,而且可以移动、旋转、缩放。
原因是数据加工逻辑基本没有,就是保存,提取。逻辑集中在UI上,最要命的是移动、旋转、缩放,而且还组合操作。
如果我用MVVM,倒也可以,但是还是用CS代码控制比较爽。

说说现在的看法:
举个例子,ListView,数据加载\加工的逻辑在ViewModel,双击列头排序和双击条目跳转这种逻辑在.xaml.cs中。
再举个例子,带有复选框文件树,用TreeView实现的,勾选子结点,父节点被勾选还是咋的,纯粹是数据层面的事情,那就在ViewModel中实现。
某个节点被选中了,要获取当前的选中项数据,在Xaml.cs中显然方便一点,在ViewModel中检测PropertyChange当然也可以。

数据相关的在ViewModel中,需要访问控件的在.xaml.cs中,当然了,有时候也可以做一下均衡。
只有后者的,不实现MVVM。
再扯远点,就是数据驱动开发了。

其实就一句话:根据实际业务情况决定和重构。

Demo

wpf 写个简单的控件吧相关推荐

  1. WindowsXamlHost:在 WPF 中使用 UWP 的控件(Windows Community Toolkit)

    原文 WindowsXamlHost:在 WPF 中使用 UWP 的控件(Windows Community Toolkit) Windows Community Toolkit 再次更新到 5.0. ...

  2. 【小沐学C#】WPF中嵌入web网页控件(WebBrowser、WebView2、CefSharp)

    文章目录 1.简介 1.1 WPF简介 1.2 WPF 体系结构 1.3 WPF入门开发 2.WebBrowser 2.1 WebBrowser特点 2.2 WebBrowser常用的属性.方法和事件 ...

  3. WPF 实现 DataGrid/ListView 分页控件

    原文:WPF 实现 DataGrid/ListView 分页控件 在WPF中,通常会选用DataGrid/ListView进行数据展示,如果数据量不多,可以直接一个页面显示出来.如果数据量很大,200 ...

  4. WPF实现炫酷Loading控件

    原文: WPF实现炫酷Loading控件 Win8系统的Loading效果还是很不错的,网上也有人用CSS3等技术实现,研究了一下,并打算用WPF自定义一个Loading控件实现类似的效果,并可以让用 ...

  5. 转载几篇别人写的皮肤类控件的技术文章

    转载几篇别人写的皮肤类控件的技术文章 原连接:http://blog.sina.com.cn/s/blog_4c3538470100ezhu.html 实现控件的透明背景 很多情况下,我们需要控件 的 ...

  6. 编程小问题系列(2)——为什么WPF里MediaElement等视频控件不起作用

    为什么WPF里MediaElement等视频控件不起作用? 非常可能的原因是因为没有安装Microsoft Windows Media Player 10或者10以上的播放器,MSDN文档里 就写有下 ...

  7. UWP入门(八)--几个简单的控件

    UWP入门(八)--几个简单的控件 原文:UWP入门(八)--几个简单的控件 每天看几个,要不聊几天我就可以看完啦,加油! 看效果 1. CheckBox <TextBlock Grid.Row ...

  8. WPF不同线程之间的控件的访问

    WPF不同线程之间的控件是不同访问的,为了能够访问其他线程之间的控件,需要用Dispatcher.Invoke执行一个新的活动即可. 例如: public void SetNotes(string n ...

  9. 【C#】wpf自定义calendar日期选择控件的样式

    原文:[C#]wpf自定义calendar日期选择控件的样式 首先上图看下样式 原理 总览 ItemsControl内容的生成 实现 界面的实现 后台ViewModel的实现 首先上图,看下样式 原理 ...

  10. WPF自定义LED风格数字显示控件

    WPF自定义LED风格数字显示控件 原文:WPF自定义LED风格数字显示控件 版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP1119 ...

最新文章

  1. python嵌套字典取值_python嵌套字典比较值与取值的实现示例
  2. java底层编程_万字长文!从底层开始带你了解并发编程,彻底帮你搞懂Java锁!
  3. iOS LLDB console debug总结
  4. xlwt设置excel字体、对齐方式、边框、颜色、背景色
  5. 自动生成的sitemap.html乱码,任意网站添加生成sitemap地图
  6. CheckBoxList 赋值问题
  7. VSCode拓展插件推荐(HTML、Node、Vue、React开发均适用)
  8. 分治法的经典问题——大整数相乘
  9. tar打包时忽略svn目录
  10. java项目图片保存在服务器,idea2019配置,Javaweb项目实现上传图片保存到本地文件文件夹,Tomcat服务器...
  11. Mac工具 shimo 无法正常使用(macOS 系统版本问题)
  12. Python Flask 微信验证
  13. c语言编程运动会分数统计系统,东华大学数据结构设计C语言运动会分数统计系统...
  14. php 获取array的长度_php中获取数组长度的方法
  15. 回溯算法(持续更新)
  16. 新浪微博模拟登陆并发文
  17. Linux 文件名和路径的最大长度
  18. js插件+UploadFile类实现图片的批量上传
  19. 绿云系统打印服务器开启,如何开启打印机云打印服务器
  20. KMIP1.0和1.1之间的详细区别

热门文章

  1. Matlab简单教程:条件分支
  2. js如何监听元素事件是否被移除_javascript:什么是js事件?(上)
  3. 全网首发:JDK绘制文字:五、字体上下文产生流程
  4. JAVA如何动态加载jar,并调用对应的函数?
  5. UOS桌面专业版下载链接
  6. CSDN排名规则变化略谈
  7. IT行业上盘与碟的区别
  8. java数组函数_Java数组
  9. oracle复制数据库文件,Oracle备份与恢复系列(四)复制数据库 使用原有的控制文件...
  10. html表格上下居中 w3c,DIV+CSS实现表格功能(不支垂直居中)