在我们创建界面元素的时候,不管在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

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

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

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

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

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

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

  1. 基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发

    我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码 ...

  2. 基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts

    在我们做各种应用的时候,我们可能都会使用到图表统计,以前接触过一些不同的图表控件,在无意中发现了图表控件Highcharts,其强大的功能和丰富的互动效果,令人难以忘怀.本篇主要介绍在Web开发中使用 ...

  3. WPF MVVM从入门到精通6:RadioButton等一对多控件的绑定

    原文:WPF MVVM从入门到精通6:RadioButton等一对多控件的绑定   WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录窗口 WPF MVVM ...

  4. iOS开发UI篇—UIScrollView控件介绍

    iOS开发UI篇—UIScrollView控件介绍 一.知识点简单介绍 1.UIScrollView控件是什么? (1)移动设备的屏幕⼤大⼩小是极其有限的,因此直接展⽰示在⽤用户眼前的内容也相当有限 ...

  5. iOS开发UI篇—UITableview控件简单介绍

    iOS开发UI篇—UITableview控件简单介绍 一.基本介绍 在众多移动应⽤用中,能看到各式各样的表格数据 . 在iOS中,要实现表格数据展示,最常用的做法就是使用UITableView,UIT ...

  6. SharePoint用户控件编写的简单介绍

    转:http://www.it165.net/design/html/201204/1131.html 我们开发中,通常需要写各种各样的部件来实现我们的展示或者功能,下面就介绍下刚刚接触的QuickP ...

  7. SharePoint 用户控件编写的简单介绍

    我们开发中,通常需要写各种各样的部件来实现我们的展示或者功能,下面就介绍下刚刚接触的QuickPart+用户控件的方式,算是自己的学习笔记,也和大家交流下心得. 1. 新建Web应用程序 2. 在项目 ...

  8. WPF Prism MVVM 中 弹出新窗体. 放入用户控件

    原文:WPF Prism MVVM 中 弹出新窗体. 放入用户控件 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq_37214567/artic ...

  9. (转)基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts

    http://www.cnblogs.com/wuhuacong/p/3736564.html 在我们做各种应用的时候,我们可能都会使用到图表统计,以前接触过一些不同的图表控件,在无意中发现了图表控件 ...

  10. WPF 用户控件的自定义依赖属性在 MVVM 模式下的使用备忘

    依赖属性相当于扩充了 WPF 标签的原有属性列表,并可以使用 WPF 的绑定功能,可谓是十分方便的:用户控件则相当于代码重用的一种方式:以上几点分开来还是比较好理解的,不过要用到MVVM 模式中,还是 ...

随机推荐

  1. 如何用ReadWriteLock实现一个通用的缓存中心?

    摘要:在并发场景中,Java SDK中提供了ReadWriteLock来满足读多写少的场景. 本文分享自华为云社区<[高并发]基于ReadWriteLock开了个一款高性能缓存>,作者:冰 ...

  2. .NET Core 程序实现 Windows 系统 Development、Staging、Production 三种环境的无感部署

    〇.前言 日常开发中,程序的环境切换是相当频繁的了,如果不同环境中的某些参数不同,那就需要每次编辑之前手动进行修改,比较麻烦,效率低下. 本文将以 .NET Core WebAPI 项目的配置方法为例 ...

  3. 逍遥自在学C语言 | for循环详解

    前言 C语言中的循环结构时,for循环是最常用的一种.它允许重复执行一段代码,直到满足特定条件为止. 本文将详细介绍for循环的用法,并提供相关的可编译运行的C代码示例. 一.人物简介 第一位闪亮登场 ...

  4. 掌握Python文件操作:从基础到高阶的全方位探索

    在本篇博客中,我们将全面.深入地探讨Python中的文件操作.文件操作在Python编程中是不可或缺的一部分,它包含了打开.读取.写入和关闭文件等各种操作.我们将从基础的文件操作讲解到高级的文件处理技 ...

  5. 【.NET 深呼吸】全代码编写WPF程序

    学习 Code 总有这样一个过程:入门时候比较依赖设计器.标记语言等辅助工具:等到玩熟练了就会发现纯代码写 UI 其实更高效.而且,纯代码编写也是最灵活的.Windows Forms 项目是肯定可以全 ...

  6. 花了一周时间,总算把mysql的加锁搞清楚了,再也不怕间隙锁和next-key了

    接触mysql都知道在mysql中有很多锁,共享锁(S).排他锁(X).间隙锁(gap).next-key,当然还有意向锁.表锁等.今天不讲别的,专门来看下innodb引擎下的锁是什么样子的. 现在有 ...

  7. 快速上手 vercel,手把手教你白嫖部署上线你的个人项目

    一.关于 vercel Vercel 是一个云服务平台,支持静态网站(纯静态页面,比如现在base utils 文档也是基于vercel)和动态网站的应用部署.预览和上线.如果你用过 GitHub P ...

  8. RocketMq5.0 任意延迟时间 TimerMessageStore 源码解析

    TimerMessageStore 简略介绍 延迟队列 rmq_sys_wheel_timer 指定时间的延迟消息.会先投递到 rmq_sys_wheel_timer 队列中 然后由 TimerMes ...

  9. Nginx获取用户真实IP

    Nginx获取用户真实IP地址 本人在一次项目中,使用Nginx需要获取到用户IP,本来可以很常规的获取的,可现实往往不常规,项目是前后端分离的,部署时,前端使用了Nginx进行了代理并转发,后端也使 ...

  10. 正交实例二(不规则数据的测试:也就是因子数和水平数不是正好满足正交表)allpairs的使用即下载

    allpairs工具的使用 作用可以针对不规则的数据生成用例 下载地址: 链接:https://pan.baidu.com/s/1SgvciN427z_WRzA5QG5eJg ** 提取码:52yj* ...