WPF:依赖属性与常规 CLR 属性有何区别?

发布于 2024-08-22 22:04:50 字数 691 浏览 2 评论 0原文

在 WPF 中,“依赖属性”到底意味着什么?

我阅读了 Microsoft 的依赖属性概述,但我并没有真正理解它。该文章的部分内容是:

样式和模板是使用依赖属性的两个主要动机场景。样式对于设置定义应用程序用户界面 (UI) 的属性特别有用。样式通常在 XAML 中定义为资源。样式与属性系统交互,因为它们通常包含特定属性的“设置器”,以及根据另一个属性的实时值更改属性值的“触发器”。

然后示例代码是这样的:

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

但我不明白这有什么特别之处。这是否意味着,当我将按钮上的 Style 设置为给定样式时,我实际上是在隐式设置 Background ?这是问题的关键吗?

In WPF, what, really, does it mean to be a "dependency property"?

I read Microsoft's Dependency Properties Overview, but it's not really sinking in for me. In part that article says:

Styles and templates are two of the chief motivating scenarios for using dependency properties. Styles are particularly useful for setting properties that define application user interface (UI). Styles are typically defined as resources in XAML. Styles interact with the property system because they typically contain "setters" for particular properties, as well as "triggers" that change a property value based on the real-time value for another property.

And then the example code is this:

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

But I'm not getting what is special about this. Does it just imply, that when I set Style on the button to the given style, that I am actually setting Background implicitly? Is that the crux of it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

饮湿 2024-08-29 22:04:50

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

依赖属性是类似属性的值,可以通过 DependencyObject 类的方法获取和设置。它们看起来(而且通常确实)非常像 CLR 属性,但事实并非如此。这就是关于他们的第一个令人困惑的事情。依赖属性实际上由几个组件组成。

下面是一个示例:

DocumentRichTextBox 对象的属性。这是真正的 CLR 属性。也就是说,它有一个名称、一个类型、一个 getter 和一个 setter,就像任何其他 CLR 属性一样。但与“普通”属性不同,RichTextBox 属性不仅仅获取和设置实例内的私有值。在内部,它是这样实现的:

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

当您设置 Document 时,您传入的值将与 DocumentProperty 一起传递到 SetValue。那是什么?GetValue 如何获取它的值?还有……为什么?

首先是什么。 RichTextBox 上定义了一个名为 DocumentProperty 的静态属性。声明此属性时,其操作如下:

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

在本例中,Register 方法告诉依赖属性系统 RichTextBox(类型,而不是实例)现在具有名为 Document 且类型为 FlowDocument 的依赖属性。此方法将此信息存储在...某处。确切地说,我们隐藏了一个实现细节。

Document 属性的 setter 调用 SetValue 时,SetValue 方法会查看 DocumentProperty 参数,验证它是否是实际上是属于 RichTextBox 的属性,并且 value 是正确的类型,然后将其新值存储在某个地方。 DependencyObject 的文档对此实现细节含糊其辞,因为您实际上并不需要了解它。在我关于这些东西如何工作的心智模型中,我假设有一个 Dictionary 类型的属性是 DependencyObject 私有的,因此派生类(例如 >RichTextBox)看不到它,但 GetValueSetValue 可以更新它。但谁知道呢,也许这是修道士写在羊皮纸上的。

无论如何,这个值现在是所谓的“本地值”,也就是说它是这个特定 RichTextBox 的本地值,就像普通属性一样。

所有的要点是:

  1. CLR 代码不需要知道属性是依赖属性。它看起来和任何其他财产一模一样。您可以调用GetValueSetValue来获取和设置它,但是除非您正在使用依赖属性系统进行某些操作,否则您可能不会这样做不需要。
  2. 与普通属性不同,它所属的对象以外的其他东西可以参与获取和设置它。 (可以想象,您可以通过反射来做到这一点,但是反射很慢。在字典中查找内容很快。)
  3. 这个东西 - 也就是依赖属性系统 - 本质上位于对象及其依赖属性之间。它可以做各种事情

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

绑定。当您绑定到属性时,它必须是依赖属性。这是因为 Binding 对象实际上并不在目标上设置属性,而是在目标对象上调用 SetValue

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

动态资源。如果您编写像Background={DynamicResource MyBackground}这样的XAML,则可以更改MyBackground资源的值以及引用它的对象会被更新。这也不是魔法。动态资源调用SetValue

动画。动画通过操纵属性值来工作。这些必须是依赖属性,因为动画正在调用 SetValue 来获取它们。

更改通知。注册依赖属性时,您还可以指定 SetValue 在设置属性值时将调用的函数。

值继承。注册依赖属性时,可以指定它参与属性值继承。当您调用 GetValue 获取对象依赖属性的值时,GetValue 会查找是否存在本地值。如果没有,它会向上遍历父对象链,查看该属性的本地值。

这就是您可以在 Window 上设置 FontFamily 的方式,并且神奇地(我经常使用这个词)窗口中的每个控件都使用新字体。另外,您可以在一个窗口中拥有数百个控件,而无需每个控件都具有 FontFamily 成员变量来跟踪其字体(因为它们没有本地值),但您仍然可以在任意一个控件上设置 FontFamily(因为每个 DependencyObject 都具有eekrit 隐藏值字典)。

Here's the explanation for how dependency properties work that I always wished someone had written for me. It's incomplete and quite possibly wrong, but it will help you develop enough of an understanding of them that you can will be able to grasp the documentation that you read.

Dependency properties are property-like values that are get and set via methods of the DependencyObject class. They can (and generally do) look very much like CLR properties, but they're not. And this gets to the first confusing thing about them. A dependency property is really made up of a couple of components.

Here's an example:

Document is a property of the RichTextBox object. It's a real CLR property. That is to say, it's got a name, a type, a getter, and a setter, just like any other CLR property. But unlike "normal" properties, the RichTextBox property doesn't merely get and set a private value inside the instance. Internally, it's implemented like this:

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

When you set Document, the value you passed in gets passed to SetValue, along with DocumentProperty. And what is that? And how does GetValue get its value? And ...why?

First the what. There's a static property defined on the RichTextBox named DocumentProperty. When this property is declared, it's done like this:

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

The Register method, in this case, tells the dependency property system that RichTextBox - the type, not the instance - now has a dependency property named Document of type FlowDocument. This method stores this information...somewhere. Where, exactly, is an implementation detail that's hidden from us.

When the setter for the Document property calls SetValue, the SetValue method looks at the DocumentProperty argument, verifies that it's really a property that belongs to RichTextBox and that value is the right type, and then stores its new value...somewhere. The documentation for DependencyObject is coy on this implementation detail, because you don't really need to know it. In my mental model of how this stuff works, I assume there's a property of type Dictionary<DependencyProperty, object> that's private to the DependencyObject, so derived classes (like RichTextBox) can't see it but GetValue and SetValue can update it. But who knows, maybe it's written on parchment by monks.

At any rate, this value is now what's called a "local value," which is to say it's a value that's local to this specific RichTextBox, just like an ordinary property.

The point of all this is:

  1. CLR code doesn't need to know that a property is a dependency property. It looks exactly like any other property. You can call GetValue and SetValue to get and set it, but unless you're doing something with the dependency property system, you probably don't need to.
  2. Unlike a normal property, something other than the object that it belongs to can be involved in getting and setting it. (You could do this with reflection, conceivably, but reflection is slow. Looking things up in dictionaries is fast.)
  3. This something - which is the dependency property system - essentially sits between an object and its dependency properties. And it can do all kinds of things.

What kinds of things? Well, let's look at some use cases.

Binding. When you bind to a property, it has to be a dependency property. This is because the Binding object doesn't actually set properties on the target, it calls SetValue on the target object.

Styles. When you set an object's dependency property to a new value, SetValue tells the style system that you've done so. That's how triggers work: they don't find out that a property's value has changed through magic, the dependency property system tells them.

Dynamic resources. If you write XAML like Background={DynamicResource MyBackground}, you can change the value of the MyBackground resource, and the background of the object referencing it gets updated. This isn't magic either; the dynamic resource calls SetValue.

Animations. Animations work by manipulating property values. Those have to be dependency properties, because the animation is calling SetValue to get at them.

Change notification. When you register a dependency property, you can also specify a function that SetValue will call when it sets the property's value.

Value inheritance. When you register a dependency property, you can specify that it participate in property value inheritance. When you call GetValue to get the value of an object's dependency property, GetValue looks to see if there's a local value. If there's not, it traverses up the chain of parent objects looking at their local values for that property.

This is how it is that you can set the FontFamily on a Window and magically (I'm using that word a lot) every control in the window uses the new font. Also, it's how it is that you can have hundreds of controls in a window without each of them having a FontFamily member variable to track their font (since they don't have local values) but you can still set the FontFamily on any one control (because of the seekrit hidden dictionary of values that every DependencyObject has).

浮生面具三千个 2024-08-29 22:04:50

在 WPF 中,“依赖属性”到底意味着什么?

为了成为依赖属性,该属性实际上必须定义为 DependencyProperty,静态地,在类上。依赖属性系统与标准 CLR 属性有很大不同。

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

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

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

In WPF, what, really, does it mean to be a "dependency property"?

In order to be a dependency property, the property must actually be defined as a DependencyProperty, statically, on the class. The dependency property system is very different than a standard CLR property.

Dependency properties are handled very differently, though. A type defines a dependency property statically, and provides a default value. The runtime actually doesn't generate a value for an instance until it's needed. This provides one benefit - the property doesn't exist until requested for a type, so you can have a large number of properties without overhead.

This is what makes the styling work property, but is also important to allow attached properties, property "inheritance" through the visual tree, and many other things WPF relies on.

For example, take the DataContext dependency property. Typically, you set the DataContext dependency property for a Window or a UserControl. All of the controls within that Window, by default, "inherit" their parent's DataContext proeprty automatically, which allows you to specify data bindings for controls. With a standard CLR property, you'd need to define that DataContext for every control in the window, just to get binding to work properly.

奢欲 2024-08-29 22:04:50

了解依赖属性试图解决什么问题可能会有所帮助。

如果我们将绑定、动画和更改事件模型放在一边,就像在其他答案中讨论的那样,好处是内存使用量,从而可以在窗口中托管数千个 WPF 对象的可扩展性。

如果一个窗口包含 1000 个 Label 对象,每个 Label 对象具有通常的 ForegroundBackgroundFontFamilyFontSizeFontWeight 等,传统上这会消耗内存,因为每个属性都有一个私有支持字段来存储值。

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

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

// 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 返回的值是仅存储一次的默认值,以覆盖应用程序所有窗口中 Label 对象的所有实例。

当使用 SetValue 设置依赖属性时,它会将非默认值存储在对象实例标识的集合中,并在所有后续 GetValue 调用中返回。

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

It may be helpful to understand what problem the dependency property is trying to solve.

If we put the Binding, Animation and the Change Event model to one side as they've been discussed in other answers, the benefit is memory usage and thus scalability to host many thousand WPF objects in a window.

If a window contains 1000 Label objects with each Label object having the usual Foreground, Background, FontFamily, FontSize, FontWeight, etc., then traditionally this would consume memory because each property would have a private backing field to store the value.

Most applications will change only a few properties, the majority of which will be left at their default values. Basically very wasteful and redundant information (each object just holding the same default values in memory)

This is where dependency properties are different.

// 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); }
}

There is no private backing field. When the dependency property is registered a default value can be specified. So in most cases the returned value from GetValue is the default value that has only been stored the once to cover all instances of the Label object across all windows of your application.

When a dependency property is set using the SetValue it stores the non-default value in a collection identified by the object instance, to be returned in all subsequent GetValue calls.

This storage method will therefore only consume memory for the properties of the WPF objects that have changed from the default value. i.e. only the differences from the default value.

拥抱我好吗 2024-08-29 22:04:50

一个简单/根本的区别 - 更改通知:对依赖项属性的更改会在 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 和依赖属性):

    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 -数据绑定-第1部分
http://en.csharp-online.net/WPF_Concepts%E2%80%94Property_Value_Inheritance

A simple/fundamental difference - Change Notification: Changes to Dependency Properties are reflected/refreshed in UI on changes whereas CLR properties don't.

<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>

And the code behind here (To declare the CLR and Dependency property):

    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++;
            }
        }
    }

Another feature provided by Dependency Properties is value inheritance - value set in top level elements propagates down the element tree - In following example taken from http://en.csharp-online.net, FontSize and FontStyle declared on "Window" tag is applied to all child elements underneath:

<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>

References:
http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1
http://en.csharp-online.net/WPF_Concepts%E2%80%94Property_Value_Inheritance

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文