代码之家  ›  专栏  ›  技术社区  ›  Cheeso

wpf:什么区分依赖属性和常规clr属性?

  •  5
  • Cheeso  · 技术社区  · 14 年前

    在WPF中,什么是“依赖属性”?

    我读过微软的 Dependency Properties Overview 但这对我来说并不是真正的沉沦。那篇文章的部分内容是:

    样式和模板是使用依赖属性的两个主要激励方案。样式对于设置定义应用程序用户界面(UI)的属性特别有用。样式通常在XAML中定义为资源。样式与属性系统交互,因为它们通常包含特定属性的“setter”以及基于其他属性的实时值更改属性值的“triggers”。

    然后示例代码是:

    <Style x:Key="GreenButtonStyle">
      <Setter Property="Control.Background" Value="Green"/>
    </Style>
    ....
    <Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>
    

    但我不明白这有什么特别之处。它只是暗示,当我设置 Style 在给定样式的按钮上,我实际上正在设置 Background 含蓄地?这就是问题的症结所在吗?

    4 回复  |  直到 11 年前
        1
  •  8
  •   Reed Copsey    14 年前

    在WPF中,什么是“依赖属性”?

    为了成为依赖属性,该属性实际上必须定义为 DependencyProperty 静态地,在类上。依赖属性系统与标准的clr属性非常不同。

    但是,依赖属性的处理方式非常不同。类型定义依赖属性 静态地 ,并提供默认值。在需要实例之前,运行时实际上不会为其生成值。这提供了一个好处——在请求某个类型之前,该属性不存在,因此您可以拥有大量的属性而不需要开销。

    这就是使样式工作属性的原因,但对于允许附加属性、通过可视化树的属性“继承”以及WPF所依赖的许多其他内容也很重要。

    例如,以 DataContext 依赖属性。通常,您设置 数据上下文 窗口或用户控件的依赖项属性。默认情况下,该窗口中的所有控件“继承”其父级的 数据上下文 ProepRy自动执行,允许您为控件指定数据绑定。使用标准的clr属性,您需要为窗口中的每个控件定义该DataContext,以便使绑定正常工作。

        2
  •  27
  •   Robert Rossney    14 年前

    下面是对依赖属性如何工作的解释,我一直希望有人能为我写。它是不完整的,很可能是错误的,但它将帮助您对它们进行足够的理解,以便您能够掌握所阅读的文档。

    依赖属性是通过 DependencyObject 班级。它们可以(通常也可以)看起来非常类似于CLR属性,但它们不是。这就导致了他们的第一个困惑。依赖属性实际上由两个组件组成。

    下面是一个例子:

    Document 是的属性 RichTextBox 对象。它是一个真实的clr属性。也就是说,它有一个名称、一个类型、一个getter和一个setter,就像其他任何clr属性一样。但与“正常”特性不同的是, 多格式文本框 属性不仅仅在实例中获取和设置私有值。在内部,它是这样实现的:

    public FlowDocument Document
    {
       get { return (FlowDocument)GetValue(DocumentProperty); }
       set { SetValue(DocumentProperty, value); }
    }
    

    当你准备好 文件 ,您传入的值将传递给 SetValue 连同 DocumentProperty .什么是 那个 ?以及如何 GetValue 得到它的价值?为什么…?

    首先是什么。在上定义了静态属性 多格式文本框 命名 文档属性 . 声明此属性时,执行如下操作:

    public static DependencyProperty DocumentProperty = DependencyProperty.Register(
        "Document",
        typeof(FlowDocument), 
        typeof(RichTextBox));
    

    这个 Register 在本例中,方法告诉依赖属性系统 多格式文本框 -类型,而不是实例-现在有一个名为 文件 类型的 FlowDocument . 此方法将此信息存储在某个地方。其中,确切地说,是一个对我们隐藏的实现细节。

    当二传手为 文件 产权要求 赋值 , the 赋值 方法查看 文档属性 参数,验证它是否确实是属于 多格式文本框 value 是正确的类型,然后将其新值存储在某个地方。文件 依赖对象 在这个实现细节上是含糊其辞的,因为您实际上不需要知道它。在我对这些东西如何工作的心理模型中,我假设有一个类型的属性 Dictionary<DependencyProperty, object> 这是私人的 依赖对象 ,因此派生类(如 多格式文本框 )看不见,但是 方法 赋值 可以更新它。但谁知道呢,也许是僧侣写在羊皮纸上的。

    无论如何,这个值现在被称为“局部值”,也就是说它是一个局部值 多格式文本框 就像普通的财产一样。

    要点 全部的 这是:

    1. clr代码不需要知道属性是依赖属性。它看起来和其他任何房产一模一样。你 可以 呼叫 方法 赋值 要获取和设置它,但是除非您正在使用依赖属性系统做一些事情,否则您可能不需要这样做。
    2. 与普通属性不同,它所属的对象以外的其他对象可以参与获取和设置它。(可以想象,你可以通过反射来做到这一点,但是反射是缓慢的。查字典很快。)
    3. 这个东西——依赖属性系统——本质上位于一个对象和它的依赖属性之间。它能做到 各种各样的东西 .

    什么东西?好吧,让我们看看一些用例。

    结合。 绑定到属性时,它必须是依赖属性。这是因为 Binding 对象实际上没有在目标上设置属性,它调用 赋值 在目标对象上。

    风格。 将对象的依赖属性设置为新值时, 赋值 告诉样式系统您已经这样做了。这就是触发器的工作原理:依赖属性系统告诉他们,它们不会发现属性的值通过魔法发生了变化。

    动态资源。 如果你像这样写XAML Background={DynamicResource MyBackground} ,您可以更改 MyBackground 资源和引用它的对象的背景将被更新。这也不是魔术;动态资源调用 赋值 .

    动画。 动画通过操纵属性值来工作。这些必须是依赖属性,因为动画正在调用 赋值 去对付他们。

    变更通知。 注册依赖属性时,还可以指定一个函数 赋值 将在设置属性值时调用。

    价值继承。 注册依赖属性时,可以指定它参与属性值继承。当你打电话 方法 要获取对象的依赖属性的值, 方法 查看是否有本地值。如果没有,它会沿着父对象链向上遍历 他们的 该属性的本地值。

    你可以这样设置 FontFamily 在一 Window 神奇的是(我经常使用这个词)窗口中的每个控件都使用新字体。而且,这就是为什么你可以在一个窗口中有数百个控件,而不需要每个控件都有一个 字体 用于跟踪其字体的成员变量(因为它们没有局部值),但仍可以设置 字体 在任何一个控件上(因为Seekrit隐藏的值字典 依赖对象 有)

        3
  •  9
  •   Rhys    14 年前

    理解依赖性属性试图解决的问题可能是有帮助的。

    如果我们将绑定、动画和变更事件模型放在一边,正如在其他答案中讨论的那样,其好处是内存使用率,因此可以扩展到在一个窗口中承载数千个WPF对象。

    如果一个窗口包含1000个 Label 每个对象 标签 普通的物体 Foreground , Background , FontFamily ,请 FontSize , FontWeight 等等,传统上这会消耗内存,因为每个属性都有一个私有的支持字段来存储值。

    大多数应用程序只会更改一些属性,其中大部分属性将保留为默认值。基本上非常浪费和冗余的信息(每个对象在内存中只保存相同的默认值)

    这就是依赖属性不同的地方。

    // Lets register the Dependency Property with a default value of 20.5
    public static readonly DependencyProperty ColumnWidthProperty =
        DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MyWPFControl), new UIPropertyMetadata(20.5, ColWitdhPropChanged));
    
    public double ColumnWidth
    {
      get { return (double)GetValue(ColumnWidthProperty); }
      set { SetValue(ColumnWidthProperty, value); }
    }
    

    没有私人支持字段。在注册依赖项属性时,可以指定默认值。所以在大多数情况下 GetValue 是仅存储一次以覆盖 标签 对象跨越应用程序的所有窗口。

    当使用 SetValue 它将非默认值存储在由对象实例标识的集合中,并在随后的所有操作中返回 方法 电话。

    因此,此存储方法将只为已从默认值更改的WPF对象的属性消耗内存。即仅与默认值的差异。

        4
  •  0
  •   Swab.Jat    11 年前

    一个简单的/基本的区别-更改通知:对依赖项属性的更改会在更改时在UI中反映/刷新,而clr属性则不会。

    <Window x:Class="SampleWPF.MainWindow"
            x:Name="MainForm"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:SampleWPF"
            Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
            Background="OrangeRed"
            >
        <StackPanel DataContext="{Binding ElementName=MainForm}">
            <!-- Bind to Dependency Property -->
            <Label Name="txtCount1" FontWeight="Bold" FontSize="20" Content="{Binding ElementName=MainForm, Path=Count1, Mode=OneWay}" />
    
            <!-- Bind to CLR Property -->
            <Label Name="txtCount2" Content="{Binding ElementName=MainForm, Path=Count2, Mode=OneWay}"></Label>
    
            <!-- Bind to Dependency Property (Using DataContext declared in StackPanel) -->
            <Label Name="txtCount3" FontWeight="Bold" FontSize="20" Content="{Binding Count1}" />
    
            <!-- Child Control binding to Dependency Property (Which propagates down element tree) -->
            <local:UserControl1 />
    
            <!-- Child Control binding to CLR Property (Won't work as CLR properties don't propagate down element tree) -->
            <local:UserControl2 />
    
            <TextBox Text="{Binding ElementName=txtCount1, Path=Content}" ></TextBox>
            <TextBox Text="{Binding ElementName=txtCount2, Path=Content}" ></TextBox>
    
            <Button Name="btnButton1" Click="btnButton1_Click_1">Increment1</Button>
            <Button Name="btnButton2" Click="btnButton1_Click_2">Increment2</Button>
        </StackPanel>
    </Window>
    
    <UserControl x:Class="SampleWPF.UserControl1"
                 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" 
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <StackPanel>
            <Label Content="{Binding Count1}" ></Label>
            <!--
            <Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Count1}"></Label>
            -->
        </StackPanel>
    </UserControl>
    
    <UserControl x:Class="SampleWPF.UserControl2"
                 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" 
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <StackPanel>
            <Label Content="{Binding Count2}" ></Label>
            <!--
            <Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Count2}"></Label>
            -->
        </StackPanel>
    </UserControl>
    

    以及后面的代码(声明clr和dependency属性):

        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 SampleWPF
        {
            /// <summary>
            /// Interaction logic for MainWindow.xaml
            /// </summary>
            public partial class MainWindow : Window
            {
                public static readonly DependencyProperty Count1Property;
                private int _Count2 = 2;
                public int Count2
                {
                    get { return _Count2; }
                    set { _Count2 = value; }
                }
                public MainWindow()
                {
                    return;
                }
                static MainWindow()
                {
                    // Register the property
                    MainWindow.Count1Property = 
                        DependencyProperty.Register("Count1",
                        typeof(int), typeof(MainWindow),
                        new FrameworkPropertyMetadata(1,
                        new PropertyChangedCallback(OnCount1Changed)));
                }
                // A .NET property wrapper (optional)
                public int Count1
                {
                    get { return (int)GetValue(MainWindow.Count1Property); }
                    set { SetValue(MainWindow.Count1Property, value); }
                }
                // A property changed callback (optional)
                private static void OnCount1Changed(
                  DependencyObject o, DependencyPropertyChangedEventArgs e) {
    
                }
                private void btnButton1_Click_1(object sender, RoutedEventArgs e)
                {
                    Count1++;
                }
                private void btnButton1_Click_2(object sender, RoutedEventArgs e)
                {
                    Count2++;
                }
            }
        }
    

    依赖属性提供的另一个特性是值继承-顶级元素中的值集沿着元素树传播-以下示例取自 http://en.csharp-online.net “window”标记上声明的fontSize和fontStyle将应用于下面的所有子元素:

    <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
      FontSize="30" FontStyle="Italic"
      Background="OrangeRed">
      <StackPanel>
        <Label FontWeight="Bold" FontSize="20" Foreground="White">
          WPF Unleashed (Version 3.0)
        </Label>
        <Label>© 2006 SAMS Publishing</Label>
        <Label>Installed Chapters:</Label>
        <ListBox>
          <ListBoxItem>Chapter 1</ListBoxItem>
          <ListBoxItem>Chapter 2</ListBoxItem>
        </ListBox>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
          <Button MinWidth="75" Margin="10">Help</Button>
          <Button MinWidth="75" Margin="10">OK</Button>
        </StackPanel>
        <StatusBar>You have successfully registered this product.</StatusBar>
      </StackPanel>
    </Window>
    

    参考文献: http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1 http://en.csharp online.net/wpf_concepts%e2%80%94property_value_inheritance