c# - 刷新 WPF 中子控件的 DataContext

标签 c# wpf xaml inheritance datacontext

我们有一个客户端-服务器应用程序,需要动态构建 View 。服务器将 XAML 字符串与数据 (Dctionary< string, string>) 一起发送到客户端,然后客户端将根据收到的 Xaml 字符串构建 View 并将数据绑定(bind)到 View 。

以下是示例 XAML 字符串:

     <StackPanel>
        <TextBox>
            <TextBox.Text>
                <Binding RelativeSource="{{RelativeSource Self}}" Path="DataContext"   
                         Converter="{{StaticResource fieldBindingConverter}}" ConverterParameter="ID_Id"
                         UpdateSourceTrigger="PropertyChanged">
                 </Binding>
            </TextBox.Text>
        </TextBox> 

        <TextBox>
            <TextBox.Text>
                <Binding RelativeSource="{{RelativeSource Self}}" Path="DataContext"   
                         Converter="{{StaticResource fieldBindingConverter}}" ConverterParameter="ID_Name"
                         UpdateSourceTrigger="PropertyChanged">
                 </Binding>
            </TextBox.Text>
        </TextBox>        
     </StackPanel>

数据如下所示:

new Dictionary<string, string>
    {
        {"ID_Id", "1"},
        {"ID_Name", "John"}
    };

客户端将使用 XamlReader.Load() 构建 View ,并创建一个窗口来托管它作为内容。客户端还将接收到的数据分配给Window.DataContext

 window.DataContext = dictionaryData;

由于两个 TextBox 继承了 Window 的 DataContext,因此 Text 属性绑定(bind)到 Dictionary。 绑定(bind)转换器“fieldBindingConverter”使用具有 key 的 ConverterParameter 从字典中获取正确的值。

因此,当第一次构建 View 时,两个文本框将相应地显示“1”和“John”。

当新数据到达客户端时就会出现问题

    new Dictionary<string, string>
    {
        {"ID_Id", "2"},
        {"ID_Name", "Peter"}
    };

通过重置托管窗口的 DataContext 不会使 TextBox 上的绑定(bind)自行刷新

window.DataContext = newDictionaryData;

事实上,TextBox 的 DataContext 仍然缓存旧的数据值。

似乎 TextBox 在首次初始化时仅获取其父 DataContext 的副本,然后仅在之后使用该本地副本。

在这种情况下,拥有一个 ViewModel 并实现 INotifyPropertyChanged 似乎并不容易,因为键“ID_XX”在不同的 View 之间可能会有所不同,并且很难为这种动态性质定义一个模型类(我可以错了)。

如果每次新数据到达时创建一个新的托管窗口(并设置 DataContext),它就会正常工作,因为所有 TextBox 的 DataContext 都会为新托管窗口派生新数据。

有谁知道如何让 TextBox“刷新”其 DataContext 以获取父窗口上设置的新数据并“刷新”绑定(bind)?

最佳答案

在 WPF 中,我们通常不会将 WindowDataContext 设置为这样的一种数据类型对象...但是它 可能。相反,我们通常创建一个特定的类,其中包含需要显示的所有属性,并且正如您所提到的,实现 INotifyPropertyChanged 接口(interface)。在您的例子中,我们将有一个 Staff 类型的属性,我们可以在 UI 中绑定(bind)到它:

public string Staff
{
    get { return staff; }
    set { staff = value; NotifyPropertyChanged("Staff"); }
}

然后在 XAML 中:

<Window>
    <StackPanel>
        <TextBox Text="{Binding Staff.Id}"/>
        <TextBox Text="{Binding Staff.Name}"/>
    </StackPanel>
</Window>

在这个小示例中,这并不是绝对必要的,但在较大的项目中,可能还会有其他数据要显示。如果将 Window.DataContext 设置为 Staff 数据类型的一个实例,那么您会发现显示其他数据(例如 的集合)会很棘手。 Staff 对象。同样,最好更新 Staff 属性,它会通知界面更新 UI,而不是更新 DataContext 属性,因为它不会更新 UI '连接到接口(interface)。

它是通过 INotifyPropertyChanged 接口(interface)发出的属性更改通知,该接口(interface)会在属性发生更改时更新或“刷新”(如您所说)UI 控件中的值。因此,您的答案是实现此接口(interface),并确保在属性值发生更改时调用 INotifyPropertyChanged.PropertyChangedEventHandler

更新>>>

哇!你真的没在听,是吗?在 WPF 中,我们不会“刷新”DataContext,一旦属性发生更改,INotifyPropertyChanged 接口(interface)就会“刷新”UI。这是解决此问题的一种可能方法:

public class CustomObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    ...
}

...

Dictionary<string, string> data = new Dictionary<string, string>
{
    {"ID_Id", "1"},
    {"ID_Name", "John"}
    ...
};

...

// Implement the `INotifyPropertyChanged` interface on this property
public ObservableCollection<CustomObject> CustomObjects { get; set; }

...

然后您可以像这样填充您的CustomObject:

CustomObjects = new ObservableCollection<CustomObject>();
CustomObject customObject = new CustomObject();
foreach (KeyValuePair<string, string> entry in data)
{
    if (entry.Key == "ID_Id") // Assuming this always comes first
    {
        customObject = new CustomObject();
        customObject.Id = int.Parse(entry.Value);
    }
    ...
    else if (entry.Key == "ID_Name") // Assuming this always comes lasst
    {
        customObject.Name = entry.Value;
        customObjects.Add(customObject);
    }
}

...

<ListBox ItemsSource="{Binding CustomObjects}">
    <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type DataTypes:CustomObject}">
            <StackPanel>
                <TextBox Text="{Binding Id}"/>
                ...
                <TextBox Text="{Binding Name}"/>
            </StackPanel>
        </DataTemplate DataType="{x:Type DataTypes:CustomObject}">
    </ListBox.ItemTemplate>
</Window>

现在你可以争辩说你因为这个原因不能这样做,或者你因为这个原因不想这样做,但在一天结束时,你必须这样做像这样的东西可以解决你的问题。

关于c# - 刷新 WPF 中子控件的 DataContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19267924/

相关文章:

c# - 如何通过Windows API设置.wav文件的位深度? (C#)

c# - 如果静态只读成员调用静态方法获取值,是否同步完成?

由 c# silverlight wcf mysql

wpf - 闪烁动画 WPF

c# - 从 Excel VSTO 和 Win Form 运行 WPF 应用程序

c# - 调试二叉树

c# - 使用 IEventAggregator 的 Caliburn Micro Stack 溢出异常

wpf - DataBound时如何使DXGrid控件正确更新

.net - 为 WPF 中的绑定(bind)公开子控件依赖属性

c# - 如何更改 ViewCell 中的高度