如何在“设置”反序列化时更新WPF UI;对象

本文关键字:WPF 更新 UI 对象 反序列化 设置 | 更新日期: 2023-09-27 18:01:39

我的目标是这样做:用户选择设置文件,读取设置并相应地更新UI。储蓄显然也是可能的。

我的程序目前不是WPF/XAML,现在这样做意味着在需要新的设置时需要大量的重复和额外的工作。

所以有人告诉我WPF/XAML是可行的,我研究了一下,很喜欢它,但我仍然不确定如何做我想要的。WPF/XAML的优势当然是数据绑定,但如果我想读取整个设置文件,我可能会用新的设置对象替换旧的设置对象。我可以让WPF程序对此作出反应并根据某些给定的数据绑定更新字段吗?

我最感兴趣的是这是否是一个好的设计,如果不是-什么是。

如何在“设置”反序列化时更新WPF UI;对象

当然可以。首先,您的设置对象应该实现INotifyPropertyChanged接口。它会添加一个事件,每次你调用属性setter时都会调用它。这样,绑定到不依赖的属性就可以双向工作。你并不真的需要那个界面。但是,如果您希望在第一组(读取所有属性的地方)之后的更改反映在ui中,则需要该接口。

通常使用

的基类
public class PropertyChangedNotifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { mPropertyChanged += value; }
        remove { mPropertyChanged -= value; }
    }
    protected virtual void RaisePropertyChanged(string aPropertyName)
    {
        PropertyChangedEventHandler handler = mPropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(aPropertyName);
            handler(this, e);
        }
    }
    private PropertyChangedEventHandler mPropertyChanged;
}

你的设置现在应该从这个类派生。

class MySettings : PropertyChangedNotifier
{
 public string UserName
 {
  get{return mUserName;}
  set{mUserName=value; RaisePropertyChanged("UserName");}
 }
}

现在对于ui, DataBinding总是与集合DataContext相关。

<Window
    x:Class="MyApp.MainWindow">
<StackPanel>
  <TextBox Text="{Binding UserName}"/>
</StackPanel>
</Window>

文本框将尝试从属性"UserName"中获取当前设置的数据上下文的值。现在我们可以在后面的主窗口代码中设置DataContext。

public MainWindow()
{
    InitializeComponent();
    DataContext = ReadMyUserSettings();
}

如果您随时更改数据上下文,ui将自动更新。您对文本框的更改将被写回您的设置。这也可以通过添加某种取消和保存工作流来改进,这样即使用户点击取消,设置也不会改变。参考IEditableObjectBindingGroup

希望这能给你一个大致的概念,它是如何工作的

我是这样做的,使用一个非常简单的例子,类似c#的伪代码使用MVVM模式。

首先,我要定义我的模型,它定义了我的配置,并被序列化/反序列化。我更喜欢使用NetDataContractSerializer。

[DataContract]
public sealed class Person
{
    [DataMember]
    public string Name {get;set;}
    [DataMember]
    public int Age {get;set;}
}

我的ViewModel将有一个公共属性来保存这个配置的当前实例

public sealed class ViewModel : DependencyObject
{
    #region Person
    /// <summary>
    /// The <see cref="DependencyProperty"/> for <see cref="Person"/>.
    /// </summary>
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(
            PersonPropertyName,
            typeof(Person),
            typeof(ViewModel),
            new UIPropertyMetadata(null));
    /// <summary>
    /// The name of the <see cref="Person"/> <see cref="DependencyProperty"/>.
    /// </summary>
    public const string PersonPropertyName = "Person";
    /// <summary>
    /// The Person
    /// </summary>
    public string Person
    {
        get { return (Person)GetValue(PersonProperty ); }
        set { SetValue(PersonProperty , value); }
    }
    #endregion      
    // snip

在我的ViewModel中,我也有一个加载和保存配置的命令。这里有很多关于iccommand的常见实现的问题,它将CanExecuteExecute的执行委托给ViewModel。

在您的UI中,您只需通过ViewModel绑定到配置模型的公共属性。

<Window x:Class="Herp.DerpWindow" SnipXamlForBrevity="true">
  <Window.DataContext>
    <ViewModel xmlns="clr-namespace:Herp" />
  </Window.DataContext>
  <!-- and later... -->
  <Label>The Person in Question:</Label>
  <TextBlock Text="{Binding Person.Name}" />
  <Label>Age</Label>
  <TextBlock Text="{Binding Person.Age}" />

因为配置模型保存在ViewModel的公共DependencyProperty中,所以当您替换它时,UI会自动更新为新值。当然,您可以使用INotifyPropertyChanged作为通知UI绑定更新的替代方法,但我更喜欢保持简单。