Страницы

Поиск по вопросам

понедельник, 6 января 2020 г.

Окно редактирования настроек

#c_sharp #wpf #mvvm


public class Setting
{
    public string Setting1 {get;set;}
}
public class SettinngViewModel: ViewModelBase
{
    public SettingViewModel(Setting settings)
    {
        _settings = settings;
    }

    Setting _settings {get; private set;}

    public string Setting1
    {
        get {return setting1;}
        set {setting1=value; OnPropertyChanged("Setting1")}
    }
}


собственно вопрос в следующем: есть главная форма из которой вызывается данное окно
редактирования настроек

public MainViewModel
{
    public MainViewModel()
    {
        SettingsCommand = new RelayCommand(x=>SettingsMethod());
    }

    var currentSettings = //здесь хранятся текущие настройки

    public ICommand SettingsCommand {get; private set;}

    private void SettingsMethod()
    {
        var view = new SettingsView();
        view.DataContext = new SettingsViewModel(currentSettings);
        view.Show();
    }
}


ввиду того что settings это ссылочный тип то при изменении в окне настроек изменения
сразу отражаются в главной форме, а я бы хотел что бы это было только после подтверждения(например
пользователь нажимает кнопку Применить)

помогите реализовать соответствующую команду
    


Ответы

Ответ 1



Смотрите. Если вы редактируете настройки, вы редактируете, понятно, копию настроек, а не оригинал. В WinForms это скрывалось за тем фактом, что применение изменений происходило во View, но с WPF/MVVM правильный подход такой. Рассмотрим случай, когда у вас нет VM-объекта, отвечающего за настройки, который поддерживает свои поля в актуальном состоянии. Тогда вам нужен VM-объект «редактируемые настройки» (SettingsEditorVM), который при старте считывает свои свойства из модельного объекта, но не синхронизирует их с моделью при изменениях. Он также выставляет команду «окончить редактирование», по приходу которой проверяет настройки на правильность, и если они в порядке, записывает результат в модельный объект. Для случая, когда у вас уже есть VM-объект, отвечающий за настройки (SettingsVM), который поддерживает свои поля в актуальном состоянии, вам нужно всё равно завести ещё один VM-объект «редактируемые настройки» (SettingsEditorVM), который сможет загрузить данные (например, в конструкторе) из SettingsVM, и по команде закончить редактирование. Этим можно пользоваться так: public class SettinngEditorVM : ViewModelBase { public SettinngEditorVM(SettingVM settingsVM) { FinishedEditing = new AwaitableCommand(); CancelledEditing = new AwaitableCommand(); Setting1 = settingsVM.Setting1; } string setting1; public string Setting1 { get { return setting1; } set { setting1 = value; OnPropertyChanged("Setting1"); CheckCorrectness(); } } void CheckCorrectness() { bool ok = Setting1 != null; FinishedEditing.CanExecuteInternal = ok; } public AwaitableCommand FinishEditing { get; private set; } public AwaitableCommand CancelledEditing { get; private set; } public async Task Edit() { using (var cts = new CancellationTokenSource()) { var finished = FinishEditing.TillActivation(CancellationToke.None); var cancelled = CancelledEditing.TillActivation(CancellationToke.None); var winner = await Task.WhenAny(finished, cancelled); cts.Cancel(); await winner; return winner == finished; } } } с таким вызовом: var editorVM = new SettingsEditorVM(editorVM); var task = editorVM.Edit(); var editorWindow = new EditorWindow() { DataContext = editorVM }; editorWindow.Show(); var succeeded = await task; if (succeeded) settingsVM.LoadFrom(editorVM); Заметьте, что код у SettingsEditorVM и SettingsVM практически одинаков, так что эти два класса стоит объединить в один. Вот код AwaitableCommand: class AwaitableCommand : ICommand { bool canExecute; // не придумал названия получше public bool CanExecuteInternal { get { return canExecute; } set { if (canExecute == value) return; canExecute = value; if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs()); } } public bool CanExecute(object parameter) { return canExecute; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { foreach (var tcs in subscribers) tcs.TrySetResult(true); subscribers.Clear(); } List> subscribers = new List>(); public async Task TillActivation(CancellationToken ct) { var tcs = new TaskCompletionSource(); subscribers.Add(tcs); using (ct.Register(() => tcs.TrySetCanceled())) await tcs.Task; } }

Комментариев нет:

Отправить комментарий