简体   繁体   English

在构造函数中设置DataContext = this和在WPF中绑定到{RelativeSource Self}之间的区别?

[英]Difference between setting DataContext=this in constructor and Binding to {RelativeSource Self} in WPF?

Next code works as expected: 下一个代码按预期工作:

AskWindow.xaml: AskWindow.xaml:

<Window
    x:Class='AskWPF.AskWindow'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    >

<DataGrid ItemsSource="{Binding SimpleItems}" />

</Window>

AskWindow.xaml.cs: AskWindow.xaml.cs:

namespace AskWPF {

public class SimpleRow {
    private string firstColumn;
    private string secondColumn;

    public SimpleRow(string first, string second) {
        firstColumn = first;
        secondColumn = second;
    }

    public string FirstColumn {
        get { return firstColumn; }
        set { firstColumn = value; }
    }

    public string SecondColumn {
        get { return secondColumn; }
        set { secondColumn = value; }
    }
}

public partial class AskWindow : Window {

    private ObservableCollection<SimpleRow> simpleItems;

    public AskWindow() {
        InitializeComponent();
        DataContext = this;

        simpleItems = new ObservableCollection<SimpleRow>();
        simpleItems.Add(new SimpleRow("row 0, column 0", "row 0, column 1"));
        simpleItems.Add(new SimpleRow("row 1, column 0", "row 1, column 1"));
    }

    public ObservableCollection<SimpleRow> SimpleItems {
        get { return simpleItems; }
    }
}

}

But if set DataContext='{Binding RelativeSource={RelativeSource Self}}' in Window tag and comment line DataContext=this we get an empty window. 但是,如果在Window标记中设置DataContext='{Binding RelativeSource={RelativeSource Self}}' ,并在注释行DataContext=this得到一个空窗口。 Why? 为什么?

AskWindow.xaml: AskWindow.xaml:

<Window .... DataContext='{Binding RelativeSource={RelativeSource Self}}'>

    <DataGrid ItemsSource="{Binding SimpleItems}" />

</Window>

AskWindow.xaml.cs: AskWindow.xaml.cs:

...
public AskWindow() {
    InitializeComponent();
    // DataContext = this;

    simpleItems = new ObservableCollection<SimpleRow>();
    simpleItems.Add(new SimpleRow("row 0, column 0", "row 0, column 1"));
    simpleItems.Add(new SimpleRow("row 1, column 0", "row 1, column 1"));
}
...

I suspect it has to do with how and when certain kinds of bindings are evaluated. 我怀疑这与评估某些种类的绑定的方式和时间有关。 In the latter case i think that the binding may retrieve the value of the collection property while it still is null, then you change the property (by setting the field) without firing any change notification for the affected property. 在后一种情况下,我认为绑定可以在仍为null的情况下检索collection属性的值,然后更改属性(通过设置字段),而不会触发受影响属性的任何更改通知。

Would recommend to move the InitializeComponent call to the end of the constructor or to at least set the field beforehand. 建议将InitializeComponent调用移到构造函数的末尾,或者至少预先设置字段。

Usually i use a readonly field and just initialize it right away: 通常我使用一个只读字段,并立即对其进行初始化:

private readonly ObservableCollection<Data> collection =
    new ObservableCollection<Data>();
public ObservableCollection<Data> Collection { get { return collection ; } }

Here is my guess. 这是我的猜测。 In both cases at one point your Collection is null. 在两种情况下,您的Collection都为空。 To be precise right after InitializeComponent. 确切地说,在InitializeComponent之后。 At this point the initial databinding got the data, but no datacontext. 此时,初始数据绑定获得了数据,但没有数据上下文。 Now by setting the DataContext your property gets raised and every binding related to it, gets invalidated and refreshed. 现在,通过设置DataContext,可以引发属性以及与之相关的每个绑定,并使其无效和刷新。 Here is my guessing part, the reason it works is that the binding to the ItemsSource is deferred therefore it works to just set the collection in the next line. 这是我的猜测部分,它起作用的原因是推迟了对ItemsSource的绑定,因此它只能在下一行中设置集合。

So in short: Setting the Datacontext will retrigger the binding. 简而言之:设置Datacontext将重新触发绑定。 But in your RelativeSource example your binding worked from the beginning but the collection was null and you never told wpf to refetch the binding. 但是在您的RelativeSource示例中,绑定从一开始就起作用,但是集合为null,并且您从未告诉过wpf重新获取绑定。 If you directly initialize your collection it should work fine. 如果您直接初始化集合,它应该可以正常工作。

Actually the binding is correct and it works also. 实际上,绑定是正确的,并且也可以使用。 In order the screen to be updated the binding has to receive notifications that something changed. 为了更新屏幕,绑定必须接收有关某些更改的通知。 A binding first evaluates and then listens for notifications. 绑定首先评估,然后侦听通知。 In your second version the binding first evaluates when InitializeComponent is run, but there are no values at that moment so you see nothing. 在第二个版本中,绑定首先评估何时运行InitializeComponent,但是此时没有任何值,因此您什么也看不到。 After that the values are created but the binding does not reevaluate because no notifications are sent. 此后,将创建值,但不会重新评估绑定,因为不会发送任何通知。

So yes one solutuion would be to initialize the collection prior to InitializeComponent. 因此,一种解决方案是在InitializeComponent之前初始化集合。

...
private ObservableCollection<SimpleRow> simpleItems = new ObservableCollection<SimpleRow>();
...

Another solution would be stupid and an overkill to notify the binding that something was changed. 另一个解决方案是愚蠢的,并且过高地通知绑定已更改某些内容。

Just a note, probably this is for learning purposes because the UI should not be mixed up with the model. 请注意,这可能是出于学习目的,因为UI不应与模型混淆。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM