WPF入门到跪下 第九章 用户控件与自定义控件

2024-01-10 14:28

本文主要是介绍WPF入门到跪下 第九章 用户控件与自定义控件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在WPF中想要进行个性化处理,主要可以通过三个方面来实现:控件模板(控件模板、数据模板、数据容器模板)、用户控件(UserControl)、自定义控件(CustomControl)。

用户控件-UserControl

在这里插入图片描述

一、简单使用

创建用户控件

<UserControl ....>......
</UserControl>

创建完成后会出现用户控件的xaml文件,打开后看到这个xaml文件中的顶级元素为UserControl

UserControl的用法与Window是一样的,区别在于UserControl元素可以被Window元素包含,而Window元素只能作为顶级元素存在。

用户控件数据的常规处理

MyUserControl.xaml中


<Grid><StackPanel><Button Height="30"/><TextBox Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor} ,FallbackValue=0}" Height="30"/></StackPanel>
</Grid>

MyUserControl.xaml.cs中

public partial class MyUserControl : UserControl
{public string Value{get { return (string)GetValue(ValueProperty); }set { SetValue(ValueProperty, value); }}public static readonly DependencyProperty ValueProperty =DependencyProperty.Register("Value", typeof(string), typeof(MyUserControl), new PropertyMetadata(default));public MyUserControl(){InitializeComponent();}
}

MainWindow.xaml中

<Window.DataContext><local:MainViewModel/>
</Window.DataContext>
<Grid><StackPanel><local:MyUserControl Value="{Binding Value}"/></StackPanel>
</Grid>

上面这一段用户控件的编写及使用是想说明一下对于用户控件中的数据,应该通过这种相对关系,找到自身的依赖属性进行数据绑定,从而让数据从外界传递到用户控件的内部。用户控件的内部应该保持完整的封装,就像C#的函数定义一样,对外提供接口,隐藏内部细节。


再看看下面这种做法:

UserControl.xaml中

<TextBox Text="{Binding Value}" Height="30"/>

MainWindow.xaml中

<local:MyUserControl"/>

这种做法,用户控件跟外界耦合了,万一在窗体使用时DataContext中没有Value属性,就出问题了。

特点

用户控件注重复合控件即控件的组合使用,可以根据控件开发人员自己的意愿进行功能处理,非常灵活。

用户控件主要有如下特点:

  • 多个现有控件的组合,组成一个可复用的控件组。
  • Xaml和后台代码组成,绑定非常紧密。
  • 窗体使用用户控件后,不支持对用户控件模板、样式的重写。
  • 继承自UserControl类型

二、用户控件的资源调用

UserControl是一个内容控件,可以通过Template属性的ControlTemplate对控件内容进行,同时在UserControl元素中的内容都会统一存放到ContentPresenter元素中,可以利用其进行模板样式的复用,具体用法如下。

创建资源字典

创建资源字典DefaultToolBarTemplate.xaml,编写ControlTemplate,其重点在于利用ContentPresenter元素来对UserControl的内容进行布局。

<ResourceDictionary ......><ControlTemplate TargetType="UserControl" x:Key="ToolBarTemplate"><Grid><Grid.RowDefinitions><RowDefinition Height="50"/><RowDefinition/></Grid.RowDefinitions><TextBlock Text="这里是复用内容,哪个UserControl使用都是OK的"/><!--下面就是内容布局--><ContentPresenter Grid.Row="1"/></Grid></ControlTemplate>
</ResourceDictionary>

在UserControl中引用资源

<UserControl ......><UserControl.Resources><ResourceDictionary Source="/Assets;component/Styles/DefaultToolBarTemplate.xaml"/></UserControl.Resources><UserControl.Template><!--引用资源,复用模板--><StaticResource ResourceKey="ToolBarTemplate"/></UserControl.Template><Grid><!--这里就是用户控件自己的内容了--></Grid>
</UserControl>

三、继承处理

在实际项目中,很多时候会创建多个用户控件来完成不同的功能,但是这些控件中往往有部分功能是冗余的,如下列示例代码:

TestUserControlA

    <UserControl ......><Grid Background="Green"><Button Content="删除" Width="100" Height="50" Click="Button_Click"/></Grid></UserControl>
public partial class TestUserControlA : UserControl
{public TestUserControlA(){InitializeComponent();}public ICommand DeleteCommand{get { return (ICommand)GetValue(DeleteCommandProperty); }set { SetValue(DeleteCommandProperty, value); }}public static readonly DependencyProperty DeleteCommandProperty =DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(TestUserControlA), new PropertyMetadata(default));public object CommandParameter{get { return (object)GetValue(CommandParameterProperty); }set { SetValue(CommandParameterProperty, value); }}public static readonly DependencyProperty CommandParameterProperty =DependencyProperty.Register("CommandParameter", typeof(object), typeof(TestUserControlA), new PropertyMetadata(default));private void Button_Click(object sender, RoutedEventArgs e){DeleteCommand?.Execute(CommandParameter);}
}

TestUserControlB

<UserControl ......><Grid Background="Green"><Button Content="删除" Width="100" Height="50" Click="Button_Click"/></Grid>
</UserControl>
public partial class TestUserControlB : UserControl
{public TestUserControlB(){InitializeComponent();}public ICommand DeleteCommand{get { return (ICommand)GetValue(DeleteCommandProperty); }set { SetValue(DeleteCommandProperty, value); }}public static readonly DependencyProperty DeleteCommandProperty =DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(TestUserControlB), new PropertyMetadata(default));public object CommandParameter{get { return (object)GetValue(CommandParameterProperty); }set { SetValue(CommandParameterProperty, value); }}public static readonly DependencyProperty CommandParameterProperty =DependencyProperty.Register("CommandParameter", typeof(object), typeof(TestUserControlB), new PropertyMetadata(default));private void Button_Click(object sender, RoutedEventArgs e){DeleteCommand?.Execute(CommandParameter);}
}

观察上述代码,可以发现TestUserControlA和TestUserControlB中的两个依赖属性其含义是相同的,这个时候可以考虑进行抽取到父类,来消除冗余,抽取之后结果如下:

父类代码

public class ComponentBase:UserControl{public ICommand DeleteCommand{get { return (ICommand)GetValue(DeleteCommandProperty); }set { SetValue(DeleteCommandProperty, value); }}public static readonly DependencyProperty DeleteCommandProperty =DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(ComponentBase), new PropertyMetadata(default));public object CommandParameter{get { return (object)GetValue(CommandParameterProperty); }set { SetValue(CommandParameterProperty, value); }}public static readonly DependencyProperty CommandParameterProperty =DependencyProperty.Register("CommandParameter", typeof(object), typeof(ComponentBase), new PropertyMetadata(default));}

TestUserControlA

<local:ComponentBase x:Class="Components.TestUserControlA" ......><Grid Background="Green"><Button Content="删除" Width="100" Height="50" Click="Button_Click"/></Grid></local:ComponentBase>
public partial class TestUserControlA : ComponentBase
{public TestUserControlA(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e){DeleteCommand?.Execute(CommandParameter);}
}

TestUserControlB

<local:ComponentBase x:Class="Components.TestUserControlB" ......><Grid Background="Green"><Button Content="删除" Width="100" Height="50" Click="Button_Click"/></Grid></local:ComponentBase>
public partial class TestUserControlB : ComponentBase
{public TestUserControlB(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e){DeleteCommand?.Execute(CommandParameter);}
}

需要注意的是,TestUserControlA和TestUserControlB 的XAML代码中的UserConrol元素换成了父类的ComponentBase元素,这是应为在进行部分类(partial)合并时必须是相同的基类。

自定义控件-CustomControl

创建自定义控件
在这里插入图片描述
创建完成后,项目中会新增一个MyCustomControl.cs文件和一个含有Generic.xaml的Themes文件夹。

MyCustomControl.cs文件用于处理自定义控件的逻辑,Generic.xaml文件则负责界面显示。

主题文件

思考一个问题,当我们在编辑界面上右键Button按钮编辑样式-编辑副本,然后xaml文件中会出来Button的默认模板代码,这些默认模板是从哪里来的?其实就是从Button的Generic.xaml文件获取的。其实准确来说,WPF会先根据当前运行系统从对应的模板文件中获取默认模板,如果对应的系统模板文件中没有对应的控件模板,才会从控件的Generic.xaml文件中获取默认模板。

试试右键ComboBox控件->编辑样式->编辑副本,然后在xaml文件的Window元素的属性中会发现多出了一个属性,这个就是根据系统引入的模板文件,即当前系统主题。

在这里插入图片描述

需要注意的是,如果xaml文件中引入了如上主题,应用如果放到win7系统,由于win7没有这个主题文件,会导致应用崩溃。

逻辑处理

当逻辑里需要界面对象参与时,可以在后台代码中通过FindName函数来获取对应的界面对象。

FindName(string name):根据指定的元素名称获取对应的元素对象,不存在时返回null

public class MyCustomControl : Control
{
......private void LogicTest(){object btn = FindName("btn_Name");}
}

也可以通过重写父类ControlOnApplyTemplate函数,在函数中通过GetTemplateChild函数获取对象。

GetTemplateChild(string Name):在实例化的可视化树中获取指定名称的元素对象,不存在时返回null

public override void OnApplyTemplate()
{var buttonElement = GetTemplateChild("btn_One") as Button;base.OnApplyTemplate();
}

特点

实现自定义控件需要注重控件对象的功能,必须遵守WPF的控件规则。

自定义控件有如下特点:

  • 可以完全自定义实现一个控件或者继承现有控件进行功能扩展并添加新功能。
  • 通过后台代码(逻辑控制)和Generic.xaml(样式模板)进行组合来完成控件实现。
  • 继承自Control类型。

这篇关于WPF入门到跪下 第九章 用户控件与自定义控件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/591111

相关文章

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

Mysql中的用户管理实践

《Mysql中的用户管理实践》:本文主要介绍Mysql中的用户管理实践,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录13. 用户管理13.1 用户 13.1.1 用户信息 13.1.2 创建用户 13.1.3 删除用户 13.1.4 修改用户

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

WinForms中主要控件的详细使用教程

《WinForms中主要控件的详细使用教程》WinForms(WindowsForms)是Microsoft提供的用于构建Windows桌面应用程序的框架,它提供了丰富的控件集合,可以满足各种UI设计... 目录一、基础控件1. Button (按钮)2. Label (标签)3. TextBox (文本框

使用WPF实现窗口抖动动画效果

《使用WPF实现窗口抖动动画效果》在用户界面设计中,适当的动画反馈可以提升用户体验,尤其是在错误提示、操作失败等场景下,窗口抖动作为一种常见且直观的视觉反馈方式,常用于提醒用户注意当前状态,本文将详细... 目录前言实现思路概述核心代码实现1、 获取目标窗口2、初始化基础位置值3、创建抖动动画4、动画完成后

详解如何在SpringBoot控制器中处理用户数据

《详解如何在SpringBoot控制器中处理用户数据》在SpringBoot应用开发中,控制器(Controller)扮演着至关重要的角色,它负责接收用户请求、处理数据并返回响应,本文将深入浅出地讲解... 目录一、获取请求参数1.1 获取查询参数1.2 获取路径参数二、处理表单提交2.1 处理表单数据三、

POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能

《POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能》ApachePOI是一个流行的Java库,用于处理MicrosoftOffice格式文件,提供丰富API来创建、读取和修改O... 目录前言:Apache POIEasyPoiEasyExcel一、EasyExcel1.1、核心特性

Python中模块graphviz使用入门

《Python中模块graphviz使用入门》graphviz是一个用于创建和操作图形的Python库,本文主要介绍了Python中模块graphviz使用入门,具有一定的参考价值,感兴趣的可以了解一... 目录1.安装2. 基本用法2.1 输出图像格式2.2 图像style设置2.3 属性2.4 子图和聚

CentOS和Ubuntu系统使用shell脚本创建用户和设置密码

《CentOS和Ubuntu系统使用shell脚本创建用户和设置密码》在Linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设置密码,本文写了一个shell... 在linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设