Как обновить WPF UI при десериализации объекта «настройки» - PullRequest
3 голосов
/ 11 июля 2011

Моя цель состоит в том, чтобы сделать это: пользователь выбирает файл настроек, настройки считываются и пользовательский интерфейс обновляется соответственно. Сохранение также должно быть возможным, очевидно.

Моя программа в настоящее время не WPF / XAML, и выполнение этого сейчас будет означать много повторений и добавленную работу, когда требуются новые настройки.

Итак, кто-то сказал мне, что WPF / XAML - это путь, я посмотрел на него и мне понравилось, но я все еще не уверен, как делать то, что я хочу. Преимущество WPF / XAML - это, конечно, привязка данных, но если я захочу прочитать весь файл настроек, я бы, вероятно, заменил старый объект настроек новым. Можно ли заставить WPF-программу реагировать на это и обновлять поля в соответствии с некоторыми привязками данных?

Меня больше всего интересует, хороший ли это дизайн, а если нет - то, что есть.

Ответы [ 2 ]

2 голосов
/ 11 июля 2011

Конечно, вы можете.Прежде всего, ваш объект настроек должен реализовывать интерфейс INotifyPropertyChanged.Это в основном добавляет событие, которое вызывается каждый раз, когда вы вызываете установщик свойства.Таким образом, привязка к свойствам вне зависимости работает в обоих направлениях.Вам не нужен этот интерфейс.Но если вы хотите, чтобы ваши изменения после первого набора (где все свойства читались) были отражены в пользовательском интерфейсе, вам нужен этот интерфейс.

Я обычно использую базовый класс для этого

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");}
 }
}

Теперь для пользовательского интерфейса DataBinding всегда связан с набором DataContext.

<Window
    x:Class="MyApp.MainWindow">

<StackPanel>
  <TextBox Text="{Binding UserName}"/>
</StackPanel>

</Window>

Текстовое поле будет пытатьсячтобы получить его значение из текущего заданного контекста данных из свойства «Имя пользователя».Теперь, чтобы установить DataContext, мы можем сделать это в коде основного окна.

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

Если вы в любой момент измените свой Datacontext, пользовательский интерфейс обновится автоматически.И ваши изменения в текстовом поле будут записаны обратно в ваши настройки.Это также можно улучшить, добавив некоторый вид отмены и сохранения рабочего процесса, чтобы ваши настройки не изменились, если пользователь нажал кнопку «Отмена».См. IEditableObject и BindingGroup.

Надеюсь, это даст вам приблизительное представление о том, как это работает.

0 голосов
/ 11 июля 2011

Вот как я бы это сделал, используя очень простой пример в 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 у меня также будет ICommand для загрузки и сохранения конфигурации. Здесь есть множество вопросов о стандартной реализации ICommand, которая делегирует выполнение CanExecute и Execute в ViewModel.

В вашем пользовательском интерфейсе вы просто привязываетесь к публичным свойствам вашей модели конфигурации через 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}" />

Поскольку модель конфигурации хранится в открытом DependencyProperty ViewModel, при его замене пользовательский интерфейс автоматически обновляется с новыми значениями. Конечно, вы можете использовать INotifyPropertyChanged в качестве альтернативного метода уведомления пользовательского интерфейса об обновлениях привязки, но я предпочитаю сделать его простым.

...