Страницы

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

четверг, 14 февраля 2019 г.

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

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 это ссылочный тип то при изменении в окне настроек изменения сразу отражаются в главной форме, а я бы хотел что бы это было только после подтверждения(например пользователь нажимает кнопку Применить)
помогите реализовать соответствующую команду


Ответ

Смотрите.
Если вы редактируете настройки, вы редактируете, понятно, копию настроек, а не оригинал. В 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; } }

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

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