wpf之依赖属性

什么是依赖附加属性

依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值。依赖属性可支持WPF中的样式设置、数据绑定、继承、动画及默认值。

如果要定义依赖属性,必须满足下面三个条件:

1 所属对象必须是依赖对象(依赖对象就是说必须要继承自DependencyObject,在wpf中大部分)

2 必须是静态+readonly

3 不是通过new而是通过注册来完成定义的

下面展示一个简单的依赖属性定义:

   public partial class MyDependency : UserControl
    {
        public MyDependency()
        {
            InitializeComponent();
        }
         
        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        /// <summary>
        /// 定义一个依赖属性,不是通过new生成,而是通过n注册生成的,
        /// 注意第一个参数之所以是MyProperty是因为依赖属性默认后面是Property来结尾,是默认的一个约束,
        /// 
        /// </summary>
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(MyDependency), new PropertyMetadata(0));
         

    }

 

至于依赖附加属性,看下面一个这个例子:

<UserControl x:Class="MyWpf.MyGrid"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyWpf"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <!--指定宽高,剩余的会自动占满-->
            <RowDefinition Height="90"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <!--可以指定行列-->
        <Border Background="Red" Grid.Row="0"></Border>
        <Border Background="Blue" Grid.Row="1"></Border>
        <Border Background="SandyBrown" Grid.Column="1" Grid.Row="0"></Border>
        <Border Background="RosyBrown" Grid.Column="1" Grid.Row="1"></Border>
    </Grid> 
</UserControl>

 

<Border>标签这是没有Row这个属性的,但是通过Grid中的这个属性附加到了Border上,使得Border标签也有了Row属性。使用附加属性,属性定义在与其使用的类不同的类上。这通常用于布局。

依赖附加属性关心的不是拥有者是不是依赖对象,关心的是被附加的对象。

定义一个依赖附加属性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

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

        /// <summary>
        /// 定义一个依赖属性,不是通过new生成,而是通过n注册生成的,
        /// 注意第一个参数之所以是MyProperty是因为依赖属性默认后面是Property,
        /// 
        /// </summary>
        public static readonly DependencyProperty MyTestProperty =
            DependencyProperty.RegisterAttached("MyTest", typeof(int), typeof(MyDependency), new PropertyMetadata(0));
        /// <summary>
        /// 注意必须是Get+附加属性的名称,默认的后缀Property不用写在这里
        /// </summary>
        /// <param name="dependency"></param>
        /// <returns></returns>
        public string GetMyTest(DependencyObject dependency)
        {
            return (string)dependency.GetValue(MyTestProperty);
        }

        public void SetMyTest(DependencyObject dependency,string value)
        {
            dependency.SetValue(MyTestProperty,value);
        } 
    } 

}

 

依赖属性和依赖附加属性的区别

 1 承载对象的区别

一个是需要拥有者必须是依赖对象,依赖附加属性则要求被附加对象是依赖对象

2 定义方法的区别

一个是Regist,一个是RegisterAttached

3 包装(封装)

 依赖属性使用属性包装器,但是依赖附加属性使用的是方法包装

 

依赖附加属性使用场景

 1 绑定中转

我们以登录界面为例,其中PasswordBox控件,Password属性不是依赖对象,所以没法进行Binding绑定,所以需要创建依赖附加属性来解决。

 依赖附加属性关心的是被附加的对象是不是依赖对象,下面的PasswordBox是继承自依赖对象的,所以可以添加依赖附加属性。

 界面:

<UserControl x:Class="MyWpf.DependencyAttachedProperty"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyWpf"
             xmlns:c="clr-namespace:MyWpf.CommonService"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="60"></RowDefinition>
            <RowDefinition Height="60"></RowDefinition>
        </Grid.RowDefinitions>
        <Button Content="提交"  Grid.Row="1" Command="{Binding GetPasswordCommand}"></Button>
        <TextBox Text="{Binding UserName}" ></TextBox>
        <PasswordBox Password="" Grid.Row="0"  c:PaswwordHelper.Password="{Binding Password}"></PasswordBox>
         
        
    </Grid>
</UserControl>

 

后台:

namespace MyWpf
{
    /// <summary>
    /// DependencyAttachedProperty.xaml 的交互逻辑
    /// </summary>
    public partial class DependencyAttachedProperty : UserControl
    {
        public DependencyAttachedProperty()
        {
            InitializeComponent();
            this.DataContext = new Person();
        }
    }

    public class Person
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public Person()
        {
            UserName = "哈哈";
            Password = "123";
        }

        private CommandBase _getPasswordCommand;

        public CommandBase GetPasswordCommand
        {
            get
            {
                if (_getPasswordCommand == null)
                {
                    _getPasswordCommand = new CommandBase();
                    _getPasswordCommand.DoExecute = new Action<object>(
                        (o) => { MessageBox.Show(this.Password); }
                        );
                }

                return _getPasswordCommand;

            }
            set { _getPasswordCommand = value; }
        }


    }

    public class CommandBase : ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            DoExecute?.Invoke(parameter);
        }

        public Action<object> DoExecute { get; set; }
    }



}

 

 

帮助类:

namespace MyWpf.CommonService
{
    public class PaswwordHelper
    {
        /// <summary>
        /// 定义一个依赖属性,不是通过new生成,而是通过n注册生成的, 
        /// 注意第三个参数是typeof(PaswwordHelper),这个依赖附加属性是属于这个类的
        /// </summary>
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.RegisterAttached("Password", typeof(string),  typeof(PaswwordHelper), 
new PropertyMetadata(new PropertyChangedCallback(OnBindedPasswordChanged))); /// <summary> /// 注意必须是Get+附加属性的名称,默认的后缀Property不用写在这里 /// </summary> /// <param name="dependency"></param> /// <returns></returns> public static string GetPassword(DependencyObject dependency) { return (string)dependency.GetValue(PasswordProperty); } public static void SetPassword(DependencyObject dependency, string value) { dependency.SetValue(PasswordProperty, value); } /// <summary> /// 回调函数 /// </summary> /// <param name="obj"></param> /// <param name="e"></param> private static void OnBindedPasswordChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var passwordBox = obj as System.Windows.Controls.PasswordBox; if (passwordBox != null) { //PasswordBox的Password属性赋值 passwordBox.Password = e.NewValue == null ? string.Empty : e.NewValue.ToString(); } } } }

 

 结果:

 

 可以看到,我们明显在界面上已经做过密码更改了,但是Password还是之前的旧密码,所以我们可以再建一个附加属性来联动更改:

namespace MyWpf.CommonService
{
    public class PaswwordHelper
    {
        /// <summary>
        /// 定义一个依赖属性,不是通过new生成,而是通过n注册生成的, 
        /// 注意第三个参数是typeof(PaswwordHelper),这个依赖附加属性是属于这个类的
        /// </summary>
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.RegisterAttached("Password", typeof(string), typeof(PaswwordHelper),
new PropertyMetadata(new PropertyChangedCallback(OnBindedPasswordChanged))); /// <summary> /// 注意必须是Get+附加属性的名称,默认的后缀Property不用写在这里 /// </summary> /// <param name="dependency"></param> /// <returns></returns> public static string GetPassword(DependencyObject dependency) { return (string)dependency.GetValue(PasswordProperty); } public static void SetPassword(DependencyObject dependency, string value) { dependency.SetValue(PasswordProperty, value); } #region public static readonly DependencyProperty AttachedProperty = DependencyProperty.RegisterAttached("Attached", typeof(string), typeof(PaswwordHelper),

new PropertyMetadata(new PropertyChangedCallback(OnAttachedChanged))); /// <summary> /// 注意必须是Get+附加属性的名称,默认的后缀Property不用写在这里 /// </summary> /// <param name="dependency"></param> /// <returns></returns> public static string GetAttached(DependencyObject dependency) { return (string)dependency.GetValue(AttachedProperty); } public static void SetAttached(DependencyObject dependency, string value) { dependency.SetValue(AttachedProperty, value); } #endregion /// <summary> /// 依赖附加属性值更改的时候的回调函数 /// </summary> /// <param name="obj"></param> /// <param name="e"></param> private static void OnBindedPasswordChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var passwordBox = obj as System.Windows.Controls.PasswordBox; passwordBox.PasswordChanged -= Pb_PasswordChanged; if (!isUpdating ) { passwordBox.Password = e.NewValue == null ? string.Empty : e.NewValue.ToString(); } passwordBox.PasswordChanged += Pb_PasswordChanged; } private static void OnAttachedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var passwordBox = obj as PasswordBox; if (passwordBox != null) { passwordBox.PasswordChanged += Pb_PasswordChanged; } } static bool isUpdating = false; private static void Pb_PasswordChanged(object sender, RoutedEventArgs e) { PasswordBox pb = sender as PasswordBox; isUpdating = true; SetPassword(pb, pb.Password); isUpdating = false; } } }

 

 

界面:

<UserControl x:Class="MyWpf.DependencyAttachedProperty"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyWpf"
             xmlns:c="clr-namespace:MyWpf.CommonService"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="60"></RowDefinition>
            <RowDefinition Height="60"></RowDefinition>
        </Grid.RowDefinitions>
        <Button Content="提交"  Grid.Row="1" Command="{Binding GetPasswordCommand}"></Button>
        <TextBox Text="{Binding UserName}" ></TextBox>
        <!-- c:PaswwordHelper.Attached="true"    没什么特别的意思,主要是给个值能够触发这个依赖属性就行,
        主要是为了绑定上密码更改的事件PasswordChanged
       UpdateSourceTrigger=PropertyChanged 意味着当目标控件值发生变化时,源数据立马更新,不是失去焦点之后才更新-->
        <PasswordBox Password="" Grid.Row="0"  
                     c:PaswwordHelper.Password="{Binding Password,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
       c:PaswwordHelper.Attached="true"    ></PasswordBox>

    </Grid>
</UserControl>

 

 

 

 

 

posted @ 2021-10-29 22:08  安静点--  阅读(796)  评论(0编辑  收藏  举报