初学者 - 对WPF中的绑定和资源感到困惑

时间:2010-08-28 16:01:08

标签: wpf binding

我正在尝试学习WPF,但我发现很难理解绑定,“资源”事物和对象创建。 我的背景是C ++ / MFC和C#-Winforms。

我的问题:

  1. 我在XAML中看到的大多数示例(在MSDN和我读过的其他两本WPF书籍中)都在绑定表达式中使用StaticResource。这些是否与静态成员有关?或者这只是一个误导性的名字?当作为StaticResource对任何对象进行引用时,它何时被实例化?

  2. 据我所知,StaticResources与app / window / control等“Resources”部分中定义的“thing”一起使用。
    现在,这些资源部分对我来说非常混乱。 究竟是什么?根据我在MFC中的经验,这些是图标,字符串等。但是,从我看到的所有示例来看,在WPF中,这些似乎基本上是一个“倾销场”。 (a)标记中的各种全局对象定义(样式,数据模板等) (b)标记中的各种全局对象实例化 我对么?这让我感到非常凌乱 它主要涉及在XAML中学习各种半DSL(用于定义样式,定义数据模板,创建对象等),并将它们粘贴在同一个地方。 我一直在想像手工编辑MFC中的资源文件(.rc)。至少那些部分是完全分开的,每个资源的语法都相对简单。

  3. 要理解前面两个问题:当我在参考资料部分定义一个对象实例,然后从StaticResource绑定中引用它时,它究竟何时被实例化? MSDN说(在“如何:使数据可用于XAML中的绑定”中):

  4.   

    你可以制作这个对象的一种方法   可用于绑定是定义它   作为资源

    然而,这不是很清楚。他们的意思是什么?他们的意思是创造?它们是否意味着连接到绑定子系统?什么时候该对象创建? 从一个简单的例子开始,我看到WPF似乎在尝试附加绑定时为我创建了这个对象。这更令人困惑。

    编辑: 在下面的karmicpuppet澄清之后,我仍然对如何与Binding相关联感到困惑。 假设我有我的资源:

    <local:Person x:Key="MyPerson" Name="Title"/> 
    

    (其中Person是一个名为Name的属性的类),然后在窗口中我有:

    <TextBlock Text="{Binding Source={StaticResource MyPerson}, Path=Name}"/> 
    

    1)这是做什么的?它是否经历了相同的步骤 - 搜索资源然后将其应用于Text属性? MyPerson对象是在创建Window时创建的,还是稍后创建的? 2)我是否必须使用绑定机制绑定到Name属性?我不能像你上面用myBrush那样直接绑定它吗?为什么我不能这样做?

    <TextBlock Text="{StaticResource MyPerson, Path=Name}"/> 
    

    这只是框架方面的短视吗?我想我在这里失踪很多,但我似乎无法理解是什么......

    3)我尝试使用DynamicResource,但我对我采取的每一步都感到很困惑。  a)在我的单个Window类的代码中添加了一个带有DependencyProperty的DependencyObject(这个DependencyObject是否必要?)

    public class SomeText : DependencyObject
    {
        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(string), typeof(SomeText), new UIPropertyMetadata(0));
    }
    

    b)在Windows.Resources中添加了一个实例(这对于DynamicResource是必要的吗?MSDN似乎说不,但如果是这样的话,我无法弄清楚如何在XAML中进行下一步)

    c)我试过了两个:

    Text="{Binding Source={DynamicResource HeaderText}, Path=Header}"
    

    这给了我一个例外,

    Text="{DynamicResource HeaderText}"
    

    但是我无法理解将路径放到Header属性的哪个位置。

    这是我最近第五次尝试使用WPF,每次我都被这些看似简单的事情所困扰。我已经阅读了2本书,我真的试图理解MSDN的文章,但它们根本没有帮助。

2 个答案:

答案 0 :(得分:38)

首先,整体评论:

WPF很难学。这很难学,因为有几个不同的基本新概念,你必须同时了解。你现在正在进行的斗争是,你试图同时学习至少三种不同的东西:

  • XamlReader(特别是标记扩展名)如何将XAML反序列化为对象。
  • FrameworkElement的资源词典是如何工作的。
  • 数据绑定的工作原理。

这样的事情:

<TextBox Text="{Binding Source={StaticResource MyPerson}, Path=Name}"/>

同时参与(至少)三种截然不同的技术。这些技术都设计得尽可能灵活,这只会让初学者更加困惑。绑定源几乎可以是任何东西的想法:很难掌握。标记扩展是一种支持递归的特殊序列化格式的想法:原理简单到足以理解,但是当你第一次开始使用真实世界的例子时,有点莫名其妙。资源字典几乎可以包含任何内容的想法,以及资源搜索算法本质上使资源可以继承:再次,在概念上非常简单,但当你试图找出数据绑定和XAML时容易丢失线程同时。

令人沮丧的是,因为概念上简单的东西 - “我想将这个控件绑定到我创建的对象的属性” - 要求你在XAML中实际表达之前理解很多东西。

唯一的解决方案是耐心等待,并确保您尽可能地了解最低级别的事物。当你看到这个:

{StaticResource MyPerson}

您应该能够思考,“这将调用StaticResource标记扩展处理程序,该处理程序在反序列化XAML时使用键MyPerson从资源字典中检索对象。

起初这是极具挑战性的。我已经专业开发了35年的软件,我发现WPF是我所学到的最具挑战性的技术平台。但所有这些东西都难以学习,因为它具有令人难以置信的功能和灵活性。学习它的回报是巨大的。

解决karmicpuppet没有的几个问题:

  

根据我在MFC中的经验[资源]是图标,字符串等。

这没有改变。您仍然可以在WPF中创建资源文件,并在运行时将它们加载到对象中。有很多不同的方法 - 您可以在资源编辑器中创建资源并通过Properties.Resources对象加载它们,您可以将图像文件(例如)添加到项目中,将它们编译为资源,以及使用他们的URI加载它们,还有很多其他方法我不知道。

FrameworkElement通过其资源词典可用的资源是另一回事。好吧,有点。这是一个例子:

<Window.Resources>
   <Image x:Key="MyImage" Source="images/myimage.png"/>
</Window.Resources>

这将创建一个Image对象,并使用Window键将其添加到MyImage的资源字典中。然后,您可以通过StaticResource标记扩展名引用该对象在XAML中,或代码中的FindResource方法。

在XAML 中的Source元素上设置Image属性使XamlReader使用ResourceManager从中读取图像数据project在创建Image对象时在运行时编译资源。

在实践中,这远不像您第一次学习WPF时那样令人困惑。我永远不会得到ResourceManager加载的资源和资源字典中存储的资源混合在一起。

  

该对象何时已创建

XamlReader读取元素时,将创建由XAML元素定义的任何对象。所以这个:

<Window.Resources>
   <local:Person x:Key="MyPerson"/>
</Window.Resources>

实例化一个新的Person对象,并将其添加到Window的资源字典中,密钥为MyPerson。这完全相当于在Window的代码隐藏中执行此操作:

AddResource("MyPerson", new Person());

那你为什么不在代码隐藏中做呢?有两个原因:

首先,它是一致的。如果在XAML中定义所有资源,则只需查看XAML文件即可查找资源。如果在XAML和代码隐藏中定义它们,则必须查看两个位置。

其次,IDE了解您在XAML中定义的资源。如果您输入

<TextBox Text="{Binding {StaticResource MyPerson}, Path=Name}"/>

在您的XAML中,如果您没有在资源字典的层次结构中的某个位置定义密钥为MyPerson的资源,IDE将通知您。但它不知道您在代码中添加的资源,因此即使资源实际上可以在运行时找到,IDE也会将其报告为问题。

答案 1 :(得分:14)

以这种方式思考:所有FrameworkElements(Windows,按钮,其他控件等)以及Application对象都包含一个Dictionary of Resources。每当您在XAML中定义资源时,如下所示:

<Window>
  <Window.Resources>
    <SolidColorBrush x:Key="myBrush" Color="Red"/>
    <DataTemplate x:Key"myTemplate">
      <!--Template definition here -->
    </DataTemplate>
  </Window.Resources>
</Window>

就像在代码中做这样的事情:

class Window
{
  void Window()
  {
    this.Resources.Add("myBrush", new SolidColorBrush(Brushes.Red));
    this.Resources.Add("myTemplate", new DataTemplate());
  }
}

您可以将各种对象作为资源。您希望在整个应用程序中重复使用的任何内容,都可以将其定义为资源。

现在,当您使用“{StaticResource}”时,如下所示:

<Button Background="{StaticResource myBrush}"/>

这就像告诉WPF搜索相应的“myBrush”资源并将其应用于Background属性。会发生什么是WPF将首先在Button的资源字典中搜索资源,如果找不到它将搜索其父级,然后搜索其父级的父级,依此类推,直至应用程序的资源。

“StaticResource”中的“静态”事物只是将其与名为“DynamicResource”的其他类型的资源查找区分开来。这两者之间的差异在this link中得到了解答。

当应用于Binding时,它也以相同的方式工作。比如说,你的XAML中有以下资源:

<local:Person x:Key="MyPerson" Name="Title"/>

并将其用作:

<TextBlock Text="{Binding Source={StaticResource MyPerson}, Path=Name}"/>

在这种情况下,会发生的事情是这样的:

Binding b = new Binding();
b.Source = FindResource("MyPerson");
b.Path = "Name";
[TextBlock].SetBinding(TextBlock.TextProperty, b);

同样,XAML中的“{StaticResource}”标记告诉WPF搜索相应的资源并将其设置为a属性的值。在这种情况下,属性是Binding的“Source”属性。

这是基础知识。希望你能找到这个有用的