如何在“设置”反序列化时更新WPF UI;对象
本文关键字:WPF 更新 UI 对象 反序列化 设置 | 更新日期: 2023-09-27 18:01:39
我的目标是这样做:用户选择设置文件,读取设置并相应地更新UI。储蓄显然也是可能的。
我的程序目前不是WPF/XAML,现在这样做意味着在需要新的设置时需要大量的重复和额外的工作。
所以有人告诉我WPF/XAML是可行的,我研究了一下,很喜欢它,但我仍然不确定如何做我想要的。WPF/XAML的优势当然是数据绑定,但如果我想读取整个设置文件,我可能会用新的设置对象替换旧的设置对象。我可以让WPF程序对此作出反应并根据某些给定的数据绑定更新字段吗?
我最感兴趣的是这是否是一个好的设计,如果不是-什么是。
当然可以。首先,您的设置对象应该实现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将自动更新。您对文本框的更改将被写回您的设置。这也可以通过添加某种取消和保存工作流来改进,这样即使用户点击取消,设置也不会改变。参考IEditableObject
和BindingGroup
希望这能给你一个大致的概念,它是如何工作的
我是这样做的,使用一个非常简单的例子,类似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的常见实现的问题,它将CanExecute
和Execute
的执行委托给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绑定更新的替代方法,但我更喜欢保持简单。