zoukankan      html  css  js  c++  java
  • wpf 写个简单的控件吧

    今天看到了老赵的一片博客:编写一个“绑定友好”的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

  • 相关阅读:
    SpringBoot学习:整合shiro(身份认证和权限认证),使用EhCache缓存
    帝国备份王出错
    spring boot整合mybatis+mybatis-plus
    Druid连接池简介和配置
    thinkphp生成的验证码不显示问题解决
    分布式文件系统-FastDFS
    Spring Security OAuth2 Demo
    spring cloud-给Eureka Server加上安全的用户认证
    spring cloud 报错Error creating bean with name 'hystrixCommandAspect' ,解决方案
    分布式唯一ID极简教程
  • 原文地址:https://www.cnblogs.com/xiaokang088/p/2570132.html
Copyright © 2011-2022 走看看