gpt4 book ai didi

循序渐进介绍基于CommunityToolkit.Mvvm和HandyControl的WPF应用端开发(3)--自定义用户控件

转载 作者:我是一只小鸟 更新时间:2023-09-13 15:07:05 37 4
gpt4 key购买 nike

在我们创建界面元素的时候,不管在Vue3+ElementPlus的前端上,还是Winform桌面端上,都是会利用自定义用户控件来快速重用一些自定义的界面内容,对自定义用户控件的封装处理,也是我们开发WPF应用需要熟悉的一环。本篇随笔继续深入介绍介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发,主要针对自定义用户控件的封装和使用做一些介绍.

1、自定义用户控件的应用场景

在我们使用原生的WPF控件的时候,有时候发现常规的原生控件不够好看,或者功能达不到要求,就需要进行一定程度上的二次封装处理,也就是自定义控件的开发场景.

例如我们前面介绍到的用户信息的查询界面,我们没有找到一个输入数值范围的控件,如对于年龄等类似的属性,我们需要一个区间的查询处理,可以保留为空,或者最小、最大值之间进行查询,如下界面所示.

由于WPF没有这样的原生控件,我们需要的话,就需要使用常规的数值或者文本控件来进行处理,如果多次有这样的内容,封装为自定义控件,让她简单的使用,是最为优雅的方式.

我们看到控件的外观如下所示.

  。

2、自定义控件的开发代码

我们可以用Grid布局来进行处理,包括两个TextBlock和两个文本的控件界面,我们创建自定义控件后,在Xaml定义好布局信息.

                        <
                        
                          UserControl
    x:Class
                        
                        =
                        
                          "
                        
                        
                          WHC.SugarProject.WpfUI.Controls.NumericRange
                        
                        
                          "
                        
                        
                          
    xmlns
                        
                        =
                        
                          "
                        
                        
                          http://schemas.microsoft.com/winfx/2006/xaml/presentation
                        
                        
                          "
                        
                        
                          
    xmlns:x
                        
                        =
                        
                          "
                        
                        
                          http://schemas.microsoft.com/winfx/2006/xaml
                        
                        
                          "
                        
                        
                          
    xmlns:d
                        
                        =
                        
                          "
                        
                        
                          http://schemas.microsoft.com/expression/blend/2008
                        
                        
                          "
                        
                        
                          
    xmlns:hc
                        
                        =
                        
                          "
                        
                        
                          https://handyorg.github.io/handycontrol
                        
                        
                          "
                        
                        
                          
    xmlns:local
                        
                        =
                        
                          "
                        
                        
                          clr-namespace:WHC.SugarProject.WpfUI.Controls
                        
                        
                          "
                        
                        
                          
    xmlns:mc
                        
                        =
                        
                          "
                        
                        
                          http://schemas.openxmlformats.org/markup-compatibility/2006
                        
                        
                          "
                        
                        
                           Name="NumericRangeControl" 
                        
                        
                          
    d:Background
                        
                        =
                        
                          "
                        
                        
                          Transparent
                        
                        
                          "
                        
                        
                          
    d:DesignHeight
                        
                        =
                        
                          "
                        
                        
                          32
                        
                        
                          "
                        
                        
                          
    d:DesignWidth
                        
                        =
                        
                          "
                        
                        
                          150
                        
                        
                          "
                        
                        
                          
    d:Foreground
                        
                        =
                        
                          "
                        
                        
                          White
                        
                        
                          "
                        
                        
                          
    mc:Ignorable
                        
                        =
                        
                          "
                        
                        
                          d
                        
                        
                          "
                        
                        >
    <Grid MinWidth=
                        
                          "
                        
                        
                          150
                        
                        
                          "
                        
                        >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width=
                        
                          "
                        
                        
                          Auto
                        
                        
                          "
                        
                         />
            <ColumnDefinition Width=
                        
                          "
                        
                        
                          *
                        
                        
                          "
                        
                         />
            <ColumnDefinition Width=
                        
                          "
                        
                        
                          Auto
                        
                        
                          "
                        
                         />
            <ColumnDefinition Width=
                        
                          "
                        
                        
                          *
                        
                        
                          "
                        
                         />
        </Grid.ColumnDefinitions>
        <
                        
                          TextBlock
            Grid.Column
                        
                        =
                        
                          "
                        
                        
                          0
                        
                        
                          "
                        
                        
                          
            Margin
                        
                        =
                        
                          "
                        
                        
                          10,0,10,0
                        
                        
                          "
                        
                        
                          
            VerticalAlignment
                        
                        =
                        
                          "
                        
                        
                          Center
                        
                        
                          "
                        
                        
                           Text="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" 
                        
                        />
        <
                        
                          TextBox
            x:Name
                        
                        =
                        
                          "
                        
                        
                           txtStart 
                        
                        
                          "
                        
                        
                          
            Grid.Column
                        
                        =
                        
                          "
                        
                        
                          1
                        
                        
                          "
                        
                        
                          
            Margin
                        
                        =
                        
                          "
                        
                        
                          5
                        
                        
                          "
                        
                        
                           Text="{Binding Path=StartValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" 
                        
                         />
        <
                        
                          TextBlock
            Grid.Column
                        
                        =
                        
                          "
                        
                        
                          2
                        
                        
                          "
                        
                        
                          
            Margin
                        
                        =
                        
                          "
                        
                        
                          5,0,5,0
                        
                        
                          "
                        
                        
                          
            VerticalAlignment
                        
                        =
                        
                          "
                        
                        
                          Center
                        
                        
                          "
                        
                        
                          
            Text
                        
                        =
                        
                          "
                        
                        
                          ~
                        
                        
                          "
                        
                         />
        <
                        
                          TextBox
            x:Name
                        
                        =
                        
                          "
                        
                        
                           txtEnd 
                        
                        
                          "
                        
                        
                          
            Grid.Column
                        
                        =
                        
                          "
                        
                        
                          3
                        
                        
                          "
                        
                        
                          
            Margin
                        
                        =
                        
                          "
                        
                        
                          5
                        
                        
                          "
                        
                        
                           Text="{Binding Path=EndValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" 
                        
                         />
    </Grid>
</UserControl>
                      

其中绑定动态属性的地方,我们使用下面代码 。

                         Text="{Binding Path=
                        
                           StartValue 
                        
                        , Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" 
                      

当然也可以使用Element的标记方式,这种我们需要设置用户自定义控件名称为Name=“***”,如上面的代码设置为.

                        Name="
                        
                           NumericRangeControl 
                        
                        "
                      

这样我们就可以通过自定义控件的ElementName来定位绑定的属性了,等同于如下代码.

                        
                          <
                        
                        
                          TextBox
            
                        
                        
                          x:Name
                        
                        
                          ="txtStart"
                        
                        
                          
            Grid.Column
                        
                        
                          ="1"
                        
                        
                          
            Margin
                        
                        
                          ="5"
                        
                        
                          
            Text
                        
                        
                          ="
                        
                        
                           {Binding Path=StartValue, Converter={StaticResource NumericConverter},  ElementName=NumericRangeControl  } 
                        
                        
                          "
                        
                        
                          />
                        
                      

前面我们介绍了该控件包含了的一些属性,如StartValue、EndValue、以及文本说明Text等,这些是在用户控件后台代码里面进行定义的自定义依赖属性的,我们来看看代码.

 如我们增加一个StartValue,那么同时需要增加一个StartValueProperty的自定义依赖属性.

                                /// 
                        
                          <
                        
                        
                          summary
                        
                        
                          >
                        
                        
                          
        /// 开始值
        /// 
                        
                        
                          </
                        
                        
                          summary
                        
                        
                          >
                        
                        
                          
        public decimal? 
                            StartValue  
                          
        {
            get { return (decimal?)GetValue(StartValueProperty); }
            set { SetValue(StartValueProperty, value); }
        }

        public static readonly DependencyProperty 
                            StartValueProperty  
                          = DependencyProperty.Register(
            nameof(StartValue), typeof(decimal?), typeof(NumericRange),
            new FrameworkPropertyMetadata(default(decimal?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(
                            OnStartValuePropertyChanged  
                          )));
                        
                      

同时,这个属性的变化,会触发一个控件路由的事件OnStartValuePropertyChanged ,如下所示.

                        
                          private
                        
                        
                          static
                        
                        
                          void
                        
                        
                           OnStartValuePropertyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
        {
            
                        
                        
                          if
                        
                         (d 
                        
                          is
                        
                        
                           not NumericRange control)
                
                        
                        
                          return
                        
                        
                          ;

            
                        
                        
                          if
                        
                         (control != 
                        
                          null
                        
                        
                          )
            {
                
                        
                        
                          var
                        
                         oldValue = (
                        
                          decimal
                        
                        ?)e.OldValue;  
                        
                          //
                        
                        
                           旧的值
                        
                        
                          var
                        
                         newValue = (
                        
                          decimal
                        
                        ?)e.NewValue; 
                        
                          //
                        
                        
                           更新的新的值
                        
                        
                          var
                        
                         args = 
                        
                          new
                        
                        
                           RoutedPropertyChangedEventArgs 
                        
                        <
                        
                          decimal
                        
                        ?>
                        
                          (oldValue, newValue);
                args.RoutedEvent 
                        
                        =
                        
                           NumericRange.ValueChangedEvent;
                control.RaiseEvent(args);
                control.ValueChangedCommand
                        
                        ?.Execute(
                        
                          null
                        
                        
                          );
            }
        }
                        
                      

除了触发路由事件外,我们可以给该控件定义一个Command 命令,类似按钮的命令处理,绑定后就可以接受到相关的通知了。Command的定义如下代码所示.

                        
                          ///
                        
                        
                          <summary>
                        
                        
                          ///
                        
                        
                           数量改变命令
        
                        
                        
                          ///
                        
                        
                          </summary>
                        
                        
                          public
                        
                        
                          static
                        
                        
                          readonly
                        
                         DependencyProperty 
                        
                           ValueChangedCommandProperty 
                        
                        =
                        
                          
            DependencyProperty.Register(
                        
                        
                          "
                        
                        
                           ValueChangedCommand 
                        
                        
                          "
                        
                        , 
                        
                          typeof
                        
                        (ICommand), 
                        
                          typeof
                        
                        (NumericRange), 
                        
                          new
                        
                         PropertyMetadata(
                        
                          default
                        
                        
                          (ICommand)));

        
                        
                        
                          ///
                        
                        
                          <summary>
                        
                        
                          ///
                        
                        
                           数量改变命令
        
                        
                        
                          ///
                        
                        
                          </summary>
                        
                        
                          public
                        
                        
                           ICommand 
                            ValueChangedCommand  
                          
        {
            
                        
                        
                          get
                        
                         { 
                        
                          return
                        
                        
                           (ICommand)GetValue(ValueChangedCommandProperty); }
            
                        
                        
                          set
                        
                        
                           { SetValue(ValueChangedCommandProperty, value); }
        }
                        
                      

3、自定义控件的使用

自定义控件开发好后,使用也是很简单的,需要在页面或者窗口的定义部分,增加控件的命名空间,便于引用自定义控件,如下代码所示.

                         xmlns:Controls="clr-namespace:
                        
                          WHC.SugarProject.WpfUI.Controls
                        
                        "
                      

这样我们在使用的时候,就和其他原生控件的使用差不多了。如下是在页面中使用的Xaml代码.

                        
                          <
                        
                        
                          Controls:NumericRange
      
                        
                        
                           EndValue 
                        
                        
                          ="
                        
                        
                          {Binding ViewModel.PageDto.
                           AgeEnd 
                          , UpdateSourceTrigger=PropertyChanged}
                        
                        
                          "
                        
                        
                           StartValue 
                        
                        
                          ="
                        
                        
                          {Binding ViewModel.PageDto.
                           AgeStart 
                          , UpdateSourceTrigger=PropertyChanged}
                        
                        
                          "
                        
                        
                           Text 
                        
                        
                          ="年龄"
                        
                        
                           ValueChangedCommand 
                        
                        
                          ="
                        
                        
                          {Binding ViewModel.
                           SearchCommand 
                          }
                        
                        
                          "
                        
                        
                          />
                        
                      

我们可以看到自定义控件的属性的绑定,和其他控件的属性绑定一致的,而且我们这里定义了一个Command: ValueChangedCommand 。

我们可以通过这个命令接收控件变化的通知。这样就可以正常的实现我们所需要的处理功能了.

另外,自定义控件的输入框,一般会在失去焦点后触发命令处理,我们也可以让文本输入框在输入后回车触发命令处理,我们增加一个KeyDown的事件处理,如下代码所示.

                        
                          <
                        
                        
                          TextBox
            
                        
                        
                          x:Name
                        
                        
                          ="txtStart"
                        
                        
                          
            Grid.Column
                        
                        
                          ="1"
                        
                        
                          
            Margin
                        
                        
                          ="5"
                        
                        
                          
            KeyDown
                        
                        
                          ="
                            txtStartEndValue_KeyDown  
                          "
                        
                        
                          
            Text
                        
                        
                          ="
                        
                        
                          {Binding Path=StartValue, Converter={StaticResource NumericConverter}, ElementName=NumericRangeControl}
                        
                        
                          "
                        
                        
                          />
                        
                        
                          <
                        
                        
                          TextBlock
            
                        
                        
                          Grid.Column
                        
                        
                          ="2"
                        
                        
                          
            Margin
                        
                        
                          ="5,0,5,0"
                        
                        
                          
            VerticalAlignment
                        
                        
                          ="Center"
                        
                        
                          
            Text
                        
                        
                          ="~"
                        
                        
                          />
                        
                        
                          <
                        
                        
                          TextBox
            
                        
                        
                          x:Name
                        
                        
                          ="txtEnd"
                        
                        
                          
            Grid.Column
                        
                        
                          ="3"
                        
                        
                          
            Margin
                        
                        
                          ="5"
                        
                        
                          
            KeyDown
                        
                        
                          ="
                            txtStartEndValue_KeyDown  
                          "
                        
                        
                          
            Text
                        
                        
                          ="
                        
                        
                          {Binding Path=EndValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}
                        
                        
                          "
                        
                        
                          />
                        
                      

让回车切换到下一个焦点即可.

                        
                          private
                        
                        
                          void
                        
                         txtStartEndValue_KeyDown(
                        
                          object
                        
                        
                           sender, KeyEventArgs e)
        {
            
                        
                        
                          if
                        
                        (e.Key ==
                        
                           Key.Enter)
            {
                
                        
                        
                          var
                        
                         textBox = sender 
                        
                          as
                        
                        
                           System.Windows.Controls.TextBox;
                
                        
                        
                          if
                        
                        (textBox != 
                        
                          null
                        
                        
                          )
                {
                    
                        
                        
                          //
                        
                        
                          切换焦点会触发值更新命令
                        
                        
                    textBox.MoveFocus(
                        
                          new
                        
                        
                           TraversalRequest(FocusNavigationDirection.Next));
                }
            }
        }
                        
                      

至此我们就完成了完美的控件处理事件了.

编译后,我们就可以在工具栏中看到用户自定义控件的列表了,可以直接拖动它到页面进行使用.

 至此,我们就实现了自定义控件在页面上的使用了,非常简单.

  。

 当然,我们也可以组合一些面板,来实现更加复杂的控件呈现方式,可以设计一些图表、文本内容的综合展示,如下是其中的一个控件的多层展示.

根据不同的图标、内容,背景色、以及一些集合形状的叠加,就可以设计出非常好看的单个用户控件,然后动态设置,就可以很好的实现不同的内容展示.

  。

最后此篇关于循序渐进介绍基于CommunityToolkit.Mvvm和HandyControl的WPF应用端开发(3)--自定义用户控件的文章就讲到这里了,如果你想了解更多关于循序渐进介绍基于CommunityToolkit.Mvvm和HandyControl的WPF应用端开发(3)--自定义用户控件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com