依赖属性详解,持续更新
个人理解,依赖属性,就是可以绑定东西的属性,支持样式、动画。本身WPF控件就有很多依赖属性,比如TextBox 控件的Text就是依赖属性。
1.简单例子,为按钮类增加一个依赖属性
using System.Windows; using System.Windows.Controls; namespace WpfApp1 { public class CustomButton : Button { //CLR“包装器” public bool IsRed { get { return (bool)GetValue(IsRedProperty); } set { SetValue(IsRedProperty, value); } } //注册依赖属性 public static readonly DependencyProperty IsRedProperty = DependencyProperty.Register( "IsRed", typeof(bool), typeof(CustomButton), new PropertyMetadata(false)); } }
在XML中使用
<Window x:Class="WpfApp1.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:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <!--样式:本地CustomButton类--> <Style TargetType="local:CustomButton"> <!--属性触发器--> <Style.Triggers> <!--当IsRed==True时触发背景属性为Red--> <Trigger Property="IsRed" Value="True"> <Setter Property="Background" Value="Red" /> </Trigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <GroupBox> <DockPanel> <!--设置自定义按钮类的依赖实行IsRed="True",那么关联的样式背景色就会显示--> <local:CustomButton IsRed="True" Content="依赖属性" Width="100" Height="20"/> </DockPanel> </GroupBox> </Grid> </Window>
2.附加属性
2-1自带的附加属性
<!--Grid.Row="1, DockPanel.Dock="Top"都是WPF自带的附加属性--> <DockPanel Grid.Row="1"> <TextBox DockPanel.Dock="Top" >Enter text</TextBox> </DockPanel>
2-2自定义附加属性,我们定义一个附加属性代表所有控件都可以使用它。
下面的应用场景是鼠标悬停在控件上会有提示类似ToolTip,这么做的原因是并非所有的控件都有ToolTip这个属性,所以要定义一个。
// 附加属性通常放在静态类中
public static class TooltipService { // 1. 声明并注册附加属性 public static readonly DependencyProperty HelpTextProperty = DependencyProperty.RegisterAttached( "HelpText", // 属性名称 typeof(string), // 属性类型 typeof(TooltipService), // 拥有者类型 new PropertyMetadata( // 元数据 null, // 默认值 OnHelpTextChanged // 属性值变化时的回调函数 ) ); // 2. 定义 Get 访问器 (必须是静态方法,命名规范: Get+属性名) public static string GetHelpText(DependencyObject obj) { return (string)obj.GetValue(HelpTextProperty); } // 3. 定义 Set 访问器 (必须是静态方法,命名规范: Set+属性名) public static void SetHelpText(DependencyObject obj, string value) { obj.SetValue(HelpTextProperty, value); } // 4. 属性值变化时的处理方法 private static void OnHelpTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // 可以在这里添加属性值变化时的逻辑 // 例如:自动设置控件的 ToolTip if (d is FrameworkElement element) { element.ToolTip = e.NewValue; } } }
<!--以下时自定义附加属性 帮助提示--> <StackPanel Margin="20" Grid.Row="2"> <!-- 使用我们定义的附加属性,这里代替ToolTip属性--> <TextBox local:TooltipService.HelpText="请输入用户名" Width="200" Height="30" Margin="5"/> <Button local:TooltipService.HelpText="点击提交表单" Content="提交" Width="100" Height="30" Margin="5"/> <CheckBox local:TooltipService.HelpText="勾选表示同意条款" Content="同意条款" Margin="5"/> </StackPanel>
3.集合类型依赖属性
WPF自带的比如 DataGrid.ItemsSource,Items的有ListBox、ComboBox、ListView、Menu、TreeView、TabControl等
那么自定义一个集合型依赖属性,下面的示例定义一个依赖属性集合,绑定在ListBox的ItemsSource上,然后按钮进行添加或删除的例子
namespace 集合依赖属性 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // DataContext = this; // 初始化集合 Tasks = new ObservableCollection<TaskItem> { new TaskItem { Name = "学习WPF", IsCompleted = true }, new TaskItem { Name = "理解依赖属性", IsCompleted = false }, new TaskItem { Name = "实现集合类型依赖属性", IsCompleted = false } }; } // 定义集合类型的依赖属性 public static readonly DependencyProperty TasksProperty = DependencyProperty.Register( "Tasks", typeof(ObservableCollection<TaskItem>), typeof(MainWindow), new PropertyMetadata(null)); public ObservableCollection<TaskItem> Tasks { get { return (ObservableCollection<TaskItem>)GetValue(TasksProperty); } set { SetValue(TasksProperty, value); } } private void AddTask_Click(object sender, RoutedEventArgs e) { Tasks.Add(new TaskItem { Name = $"新任务 {Tasks.Count + 1}", IsCompleted = false }); } private void RemoveTask_Click(object sender, RoutedEventArgs e) { if (Tasks.Count > 0) Tasks.RemoveAt(Tasks.Count - 1); } } // 简单的数据模型 public class TaskItem { public string Name { get; set; } public bool IsCompleted { get; set; } } }
<Window x:Class="集合依赖属性.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:集合依赖属性" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <local:MainWindow/> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBlock Text="任务列表" FontSize="16" FontWeight="Bold" Margin="10"/> <ListBox Grid.Row="1" ItemsSource="{Binding Tasks}" Margin="10"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <CheckBox IsChecked="{Binding IsCompleted}" Margin="0,0,10,0"/> <TextBlock Text="{Binding Name}" VerticalAlignment="Center"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10"> <Button Content="添加任务" Click="AddTask_Click" Margin="5" Padding="10,5"/> <Button Content="移除任务" Click="RemoveTask_Click" Margin="5" Padding="10,5"/> </StackPanel> </Grid> </Window>
4.依赖属性的回调
注册依赖属性的时候添加的回调方法,属性更改时先执行验证回调,在执行强制回调,最后执行值改变回调
public class CustomControl : Control { // 注册依赖属性 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", // 属性名 typeof(double), // 属性类型 typeof(CustomControl), // 拥有者类型 new PropertyMetadata( // 元数据 0.0, // 默认值 OnValueChanged, // 值变更回调(最后触发) CoerceValue // 强制回调(第2触发触发) ),ValidateValue // 验证回调(最先触发) ); // CLR属性包装器 public double Value { get { return (double)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } // 值变更回调 private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = d as CustomControl; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; control.OnValueChanged(oldValue, newValue); } protected virtual void OnValueChanged(double oldValue, double newValue) { // 触发事件或更新UI(如显示值变更日志) Console.WriteLine($"Value changed from {oldValue} to {newValue}"); } // 强制回调:确保值在0-100之间 private static object CoerceValue(DependencyObject d, object baseValue) { double value = (double)baseValue; if (value < 0) return 0.0; if (value > 100) return 100.0; return value; } // 验证回调 private static bool ValidateValue(object value) { double val = (double)value; return !double.IsNaN(val); // 不允许NaN值 } }
<StackPanel Margin="10"> <!-- 显示依赖属性值:绑定值 值的来自自定义控件 --> <TextBlock Text="{Binding Value, ElementName=customControl, StringFormat=Value: {0:F2}}"/> <!-- 自定义控件 --> <local:CustomControl x:Name="customControl" Value="50" Margin="0,10,0,10" Height="39"/> <!-- 修改依赖属性值(触发回调)Slider可以修改值,因为绑定时双向的 --> <Slider Minimum="0" Maximum="100" Value="{Binding Value, ElementName=customControl, Mode=TwoWay}"/> </StackPanel>
浙公网安备 33010602011771号