C# Wpf自定义控件简单实现密码框(PasswordBox)明密文切换、清除功能及数据绑定(二)

一、自定义控件封装

Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性。

1、依赖属性DependencyProperty

(1) 依赖属性原型

/* 将其他类型添加为已注册的依赖对象所有者,从而为依赖属性提供依赖属性元数据使其存在于提供的所有者类型上。
 * ownerType: 要作为此依赖属性所有者添加的类型。
 * typeMetadata: 在依赖属性存在于所提供的类型上时对其进行限定的元数据。
 */
public DependencyProperty AddOwner(Type ownerType);
public DependencyProperty AddOwner(Type ownerType, PropertyMetadata typeMetadata);

/* 表示可通过诸如样式、数据绑定、动画和继承等方法设置的属性。
 * 方法描述: 使用指定的属性名称、属性类型、所有者类型、属性元数据和属性的值验证回叫来注册依赖属性。
 * name: 属性名称
 * propertyType: 属性类型
 * typeMetadata: 依赖属性的属性元数据
 * validateValueCallback: 对回调的引用,除了典型的类型验证之外,该引用还应执行依赖属性值的任何自定义验证。
 */
public static DependencyProperty Register(string name, Type propertyType, Type ownerType);

public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata);

public static DependencyProperty Register (string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback);


/* 在抽象类上注册附加属性。 此附加属性是枚举类型属性,注册会添加验证回调,以验证所提供的值是否为枚举值。
 * 方法描述: 使用指定的属性类型、所有者类型、属性元数据和属性的值验证回调来注册附加属性。
 * name: 属性名称
 * propertyType: 属性类型
 * typeMetadata: 依赖属性的属性元数据。 这可以包括默认值和其他特征。
 * validateValueCallback: 对回调的引用,除了典型的类型验证之外,该引用还应执行依赖属性值的任何自定义验证。
 */
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType);

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata);

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback);
/* 官方注解
 * 附加属性是由可扩展应用程序标记语言定义的属性概念, (XAML) 。 WPF 将附加属性作为依赖项属性实现。 由于 WPF 附加属性是依赖属性,因此它们可以应用元数据,常规属性系统可以使用这些元数据来实现报表布局特征等操作。
 */

/* 为此依赖属性(当它位于指定的对象实例上时)返回元数据。
 * dependencyObjectType: 一个特定对象,该对象记录需要其中的依赖属性元数据的依赖项对象类型。
 * forType: 要从中检索依赖属性元数据的特定类型。
 * dependencyObject: 一个依赖对象,检查了其类型,以便确定元数据应来自依赖属性的哪个类型特定版本。
 */
public PropertyMetadata GetMetadata(DependencyObjectType dependencyObjectType);
public PropertyMetadata GetMetadata(Type forType);
public PropertyMetadata GetMetadata(DependencyObject dependencyObject);

这里笔者仅提取了几处常用的依赖属性的使用方法,详请类方法请见:DependencyProperty 类 (System.Windows) | Microsoft Learn

(2) 自定义控件资源

<!-- 定义CheckBox 预置属性及模板相关样式 -->
<Style TargetType="CheckBox" x:Key="CheckBoxStyle">
		<Setter Property="FontFamily" Value="../Assets/Fonts/#iconfont"></Setter>
		<Setter Property="HorizontalAlignment" Value="Right"></Setter>
		<Setter Property="VerticalAlignment" Value="Center"></Setter>
		<Setter Property="Margin" Value="0 0 5 0"></Setter>
		<Setter Property="Template">
				<Setter.Value>
						<ControlTemplate TargetType="CheckBox">
								<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Content}"
														Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Margin}" />
						</ControlTemplate>
				</Setter.Value>
		</Setter>

</Style>

注意,此处FontFamily属性是通过 IconFont字体图标动态进行加载,具体实现方式请参考:

C#使用IconFont字体图标的动态加载(&#xe格式转换)_XBMY的博客-CSDN博客

在WPF中使用iconfont-爱码网

(3) 密码框属性样式

a. TextBox可见样式

<!-- 定义密码框使用的TextBox 预置属性可见样式 -->
<Style TargetType="TextBox" x:Key="PasswordVisibleStyle">
		<Setter Property="FontWeight" Value="Regular"></Setter>
		<Setter Property="FocusVisualStyle" Value="{x:Null}" />
		<Setter Property="Foreground" Value="#303133"></Setter>
		<Setter Property="Template">
				<Setter.Value>
						<ControlTemplate TargetType="TextBox">
								<Border BorderBrush="#dcdfe6" CornerRadius="4" BorderThickness="1"
												Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Background}">
										<Grid>
												<Grid.ColumnDefinitions>
														<ColumnDefinition></ColumnDefinition>
														<ColumnDefinition Width="Auto"></ColumnDefinition>
														<ColumnDefinition Width="Auto"></ColumnDefinition>
												</Grid.ColumnDefinitions>

												<ScrollViewer x:Name="PART_ContentHost" BorderThickness="0" VerticalAlignment="Center"
																			FontSize="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= FontSize}" IsTabStop="False" Margin="2"
																			Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= Background}" />
												<!-- 清空内容 -->
												<CheckBox Grid.Column="1" Style="{StaticResource CheckBoxStyle}" Content="&#xe60a;" Foreground="#e6e6e6" 
																	IsChecked="{Binding (local:CustPasswordBox.IsCleared),
														RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}" 
																	Visibility ="{Binding (local:CustPasswordBox.ClearVisibility),
														RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}" />

												<!-- 显示内容 -->
												<CheckBox Grid.Column="2" Style="{StaticResource CheckBoxStyle}" Content="&#xe60d;" Foreground="#bfbfbf"
																	IsChecked="{Binding (local:CustPasswordBox.IsChecked),
														RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}" />
										</Grid>
								</Border>
						</ControlTemplate>
				</Setter.Value>
		</Setter>
</Style>

b. TextBox隐藏样式

<!-- 定义密码框使用的TextBox 预置属性隐藏样式 -->
<Style TargetType="PasswordBox" x:Key="PasswordBoxCollapsedStyle">
		<Setter Property="FontWeight" Value="Regular" />
		<Setter Property="FocusVisualStyle" Value="{x:Null}" />
		<Setter Property="Foreground" Value="#303133" />
		<Setter Property="Template">
				<Setter.Value>
						<ControlTemplate TargetType="PasswordBox">
								<Border BorderBrush="#dcdfe6" CornerRadius="4" BorderThickness="1"
												Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Background}">
										<Grid>
												<Grid.ColumnDefinitions>
														<ColumnDefinition></ColumnDefinition>
														<ColumnDefinition Width="Auto"></ColumnDefinition>
														<ColumnDefinition Width="Auto"></ColumnDefinition>
												</Grid.ColumnDefinitions>

												<ScrollViewer x:Name="PART_ContentHost" BorderThickness="0" VerticalAlignment="Center"
																			FontSize="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=FontSize}"
																			IsTabStop="False" Margin="2"
																			Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Background}" />
												<!-- 清空内容 -->
												<CheckBox Grid.Column="1" Style="{StaticResource CheckBoxStyle}" Content="&#xe60a;" Foreground="#e6e6e6"
																	IsChecked="{Binding (local:CustPasswordBox.IsCleared),
														RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}" 
																	Visibility ="{Binding (local:CustPasswordBox.ClearVisibility),
														RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}"/>

												<!-- 隐藏内容 -->
												<CheckBox Grid.Column="2" Style="{StaticResource CheckBoxStyle}" Content="&#xe60c;" Foreground="#bfbfbf"
																	IsChecked="{Binding (local:CustPasswordBox.IsChecked),
														RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}"/>

										</Grid>
								</Border>
								<ControlTemplate.Triggers>
										<Trigger Property="IsEnabled" Value="false">
												<Setter Property="Background" Value="#ebeef5"></Setter>
										</Trigger>
								</ControlTemplate.Triggers>
						</ControlTemplate>
				</Setter.Value>
		</Setter>
</Style>

c. PasswordBox封装

<Grid>
		<TextBox Style="{StaticResource PasswordVisibleStyle}" 
							Visibility="{Binding (local:CustPasswordBox.TBoxVisibility),RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}"
							Text="{Binding (local:CustPasswordBox.Password),RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>

		<PasswordBox Style="{StaticResource PasswordBoxCollapsedStyle}" 
									Visibility="{Binding (local:CustPasswordBox.PwdVisibility),RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}" 
									local:PasswordBoxHelper.Attach="True" local:PasswordBoxHelper.Password="{Binding (local:CustPasswordBox.Password),
				RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},Mode=TwoWay,
				UpdateSourceTrigger=PropertyChanged}">

		</PasswordBox>
</Grid>

 此处,传递的相关属性如下:

  • ScrollViewer.Background属性,Border及ScrollViewer对象有受该属性影响;
  • ScrollViewer.FontSize属性
  • CheckBox.IsChecked属性:引用CustPasswordBox.IsCleared依赖
  • CheckBox.Visibility属性:引用CustPasswordBox.ClearVisibility依赖
  • TextBox.Visibility属性:引用CustPasswordBox.TBoxVisibility依赖
  • TextBox.Text属性:引用CustPasswordBox.Password依赖
  • PasswordBox.Visibility属性:引用CustPasswordBox.PwdVisibility依赖

附加属性绑定:

  • PasswordBoxHelper.Attach属性
  • PasswordBoxHelper.Password属性:引用CustPasswordBox.Password依赖

(4) 依赖属性定义

/// <summary>
/// CustPasswordBox.xaml 的交互逻辑
/// </summary>
public partial class CustPasswordBox : UserControl
{
	public CustPasswordBox()
	{
			InitializeComponent();
	}

	/// <summary>
	/// 清除显示属性
	/// </summary>
	public static readonly DependencyProperty ClearVisibilityProperty = DependencyProperty.Register(
			"ClearVisibility", typeof(Visibility), typeof(CustPasswordBox), new PropertyMetadata(Visibility.Collapsed));

	/// <summary>
	/// 叉叉图标状态(隐藏/隐藏)字段
	/// </summary>
	public Visibility ClearVisibility
	{
			get => (Visibility)GetValue(ClearVisibilityProperty);
			set => SetValue(ClearVisibilityProperty, value);
	}

	/// <summary>
	/// 密码属性
	/// </summary>
	public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register(
			"Password", typeof(string), typeof(CustPasswordBox), new PropertyMetadata((s, e) =>
			{
					var sender = s as CustPasswordBox;

					//根据密码框是否有内容来显示叉叉图标
					sender.ClearVisibility = !string.IsNullOrEmpty(sender.Password) ? Visibility.Visible : Visibility.Collapsed;

			}));

	/// <summary>
	/// 密码字段
	/// </summary>
	public string Password
	{
			get => (string)GetValue(PasswordProperty);
			set => SetValue(PasswordProperty, value);
	}

	/// <summary>
	/// 清空元数据的事件属性
	/// </summary>
	public static readonly DependencyProperty IsClearedProperty = DependencyProperty.Register(
	"IsCleared", typeof(bool), typeof(CustPasswordBox), new PropertyMetadata((s, e) =>
	{
			var sender = s as CustPasswordBox;
			sender.Password = "";
	}));

	/// <summary>
	/// 清空元数据字段
	/// </summary>
	public bool IsCleared
	{
			get => (bool)GetValue(IsClearedProperty);
			set => SetValue(IsClearedProperty, value);
	}

	/// <summary>
	/// TBoxVisibility明文属性
	/// </summary>
	public static readonly DependencyProperty TBoxVisibilityProperty = DependencyProperty.Register(
			"TBoxVisibility", typeof(Visibility), typeof(CustPasswordBox));

	/// <summary>
	/// 显示或隐藏明文字段
	/// </summary>
	public Visibility TBoxVisibility
	{
			get => (Visibility)GetValue(TBoxVisibilityProperty);
			set => SetValue(TBoxVisibilityProperty, value);
	}

	/// <summary>
	/// 密文属性
	/// </summary>
	public static readonly DependencyProperty PwdVisibilityProperty = DependencyProperty.Register(
			"PwdVisibility", typeof(Visibility), typeof(CustPasswordBox));

	/// <summary>
	/// 显示或隐藏密文字段
	/// </summary>
	public Visibility PwdVisibility
	{
			get => (Visibility)GetValue(PwdVisibilityProperty);
			set => SetValue(PwdVisibilityProperty, value);
	}


	/// <summary>
	/// IsChecked事件属性
	/// </summary>
	public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register(
			"IsChecked", typeof(bool), typeof(CustPasswordBox), new PropertyMetadata((s, e) =>
			{
					var cpb = s as CustPasswordBox;
					if ((bool)e.NewValue)
					{
							//Ischecked为True时
							cpb.TBoxVisibility = Visibility.Visible;
							cpb.PwdVisibility = Visibility.Collapsed;
					}
					else
					{
							//Ischecked为False时
							cpb.TBoxVisibility = Visibility.Collapsed;
							cpb.PwdVisibility = Visibility.Visible;
					}
			}));

	/// <summary>
	/// IsChecked字段
	/// </summary>
	public bool IsChecked
	{
			get => (bool)GetValue(IsCheckedProperty);
			set => SetValue(IsCheckedProperty, value);
	}
}

 二、UserControl控件调用

1、DataContext数据源配置

/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
		private readonly PasswordBoxViewModel PwdViewModel;

		public MainWindow()
		{
				InitializeComponent();
				PwdViewModel = new PasswordBoxViewModel();
				DataContext = PwdViewModel;
		}
}

/// <summary>
/// 密码框模型
/// </summary>
public class PasswordBoxViewModel : INotifyPropertyChanged
{
		public event PropertyChangedEventHandler PropertyChanged;

		public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
		{
				PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
		}

		private string _password;
		public string Password
		{
				get => _password;
				set
				{
						_password = value;
						RaisePropertyChanged();
				}
		}
}

2. XAML前端调用

<Window xmlns:BaseUI="clr-namespace:WpfAppDemo.BaseUI"  
            x:Class="WpfAppDemo.MainWindow"
			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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
			xmlns:local="clr-namespace:WpfAppDemo"
			d:DataContext="{d:DesignInstance Type=local:PasswordBoxViewModel}"
			mc:Ignorable="d"
			WindowStyle="None" WindowStartupLocation="CenterScreen"
			AllowsTransparency="True" Background="Transparent"
			Title="MainWindow" Height="200" Width="350" >
	
	<Border BorderBrush="#20333333" BorderThickness="1" CornerRadius="8" Background="White" Margin="0">
			<Border.Effect>
					<DropShadowEffect BlurRadius="8" Color="#20333333" ShadowDepth="0"></DropShadowEffect>
			</Border.Effect>
			<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
					<TextBlock Text="自定义密码框:" VerticalAlignment="Center"></TextBlock>
					<BaseUI:CustPasswordBox Password="{Binding Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" 
																	Height="24" Width="200"></BaseUI:CustPasswordBox>
			</StackPanel>
	</Border>
</Window>

挑重点,其中BaseUI:CustPasswordBox需xmlns:BaseUI="clr-namespace:WpfAppDemo.BaseUI"作为引用条件,d:DataContext="{d:DesignInstance Type=local:PasswordBoxViewModel}"则为数据源前端绑定,可自动生成。

控件效果:

--END--

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在WPF中,我们可以通过自定义控件实现消息提示功能。 首先,我们可以创建一个继承自ContentControl的自定义控件,例如命名为MessageTipControl。在这个控件中,我们可以定义一个Label或TextBlock用于显示提示信息。同时,可以为该控件添加一些样式和动画以提升用户体验。 接下来,我们可以为MessageTipControl增加一些附加属性,例如MessageText和MessageType。这些属性可以用来设置提示信息的文本内容和类型,帮助我们在使用自定义控件时更加便捷地进行设置。 然后,我们可以为自定义控件添加一些额外的功能,例如自动隐藏、设置展示时长等等。我们可以使用定时器或动画来控制控件的显示和隐藏,并可以根据需要定义一些事件或回调函数供外部调用。 最后,在需要显示消息提示的地方,我们可以引用自定义的MessageTipControl,并通过设置附加属性来显示特定的提示信息。例如可以通过在XAML中添加如下代码来完成: ``` <local:MessageTipControl MessageText="提示信息" MessageType="Success"/> ``` 当然,我们也可以通过动态绑定或代码的方式来实现消息提示的文本内容和类型的动态更新。 通过以上的步骤,我们就可以非常方便地使用自定义控件实现消息提示功能了。当需要显示提示时,我们只需调用相应的方法或设置相应的属性,控件就能自动显示出消息提示,并根据设定的时间自动隐藏。这样可以使得我们的应用界面更加美观和用户友好。 ### 回答2: 使用WPF自定义控件可以很方便地实现消息提示功能。首先需要创建一个继承自WPF的控件的类,例如命名为MessageBox。在该类中,可以定义需要展示的消息内容、样式、位置等属性。通过重写OnRender方法,可以在控件上绘制提示消息的样式。 在OnRender方法中,可以使用WPF的绘图API绘制背景、边、文本等。可以通过依赖属性设置消息的文本内容、背景色、字体样式等。除此之外,可以使用WPF提供的动画效果来实现消息提示的弹出、消失的效果。 在使用该自定义控件时,只需要在XAML文件中引用命名空间,并实例化MessageBox控件即可。可以通过绑定消息内容属性来动态更改展示的消息内容。 此外,还可以定义一些方法,例如Show方法,用于控制消息提示的显示与隐藏。可以设置动画效果实现消息提示的平滑弹出和淡出。 总结起来,使用WPF自定义控件可以很方便地实现消息提示功能。可以通过自定义控件的属性和方法来控制消息内容、样式和展示方式。这样可以在WPF应用程序中灵活地使用消息提示,提升用户体验。 ### 回答3: 使用WPF自定义控件可以很方便地实现消息提示功能。以下是实现步骤: 1. 创建自定义控件类:创建一个继承自WPF控件基类的自定义控件类,可以命名为"MessageTip"。在该类中定义控件的外观样式和交互行为。 2. 定义依赖属性:为了能够在使用该控件时设置消息内容,需要在"MessageTip"类中定义一个依赖属性,例如"Message"。依赖属性可以支持数据绑定和样式设置。 3. 控件模板:在自定义控件的类中,可以使用"ControlTemplate"属性定义控件的外观模板。可以使用XAML语言编写模板,在模板中指定消息内容的显示位置和样式。 4. 触发器和动画:可以使用触发器和动画效果来控制消息提示的显示和隐藏。例如,可以在用户鼠标悬停在控件上时显示消息,然后使用动画效果进行展开或淡入的动画效果,最后可以设置一定的时间后自动隐藏消息。 5. 使用自定义控件:在WPF应用程序中,可以像使用其他控件一样使用自定义控件。在XAML中使用控件名字,并设置相关属性,例如设置"Message"属性来显示具体的消息内容。 通过以上步骤,就可以使用WPF自定义控件实现消息提示功能自定义控件具有良好的可复用性,可以在多个应用程序中进行使用,并且可以根据需要进行扩展和定制。此外,通过WPF数据绑定功能,还可以通过绑定不同的数据源来显示不同的消息内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值