Страницы

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

четверг, 9 января 2020 г.

Открытие второго окна в паттерне MVVM

#c_sharp #wpf #net #mvvm


Как ПРАВИЛЬНО реализовать открытие второго окна из кнопки на первом окне с использованием
паттерна MVVM?
На сети конкретного примера я не нашел, а во многих проектах открытие окна идет нарушая
принципы данного паттерна...
    


Ответы

Ответ 1



Если разберетесь в коде, то попробуйте DialogService. Простите, нет много времени еще более подробно ответить. Пользоваться можно так: //просто какие-то входные данные var settingsWindowRequest = new SettingsLocalEvent() {}; //вид диалога, это UserControl var view = new ChangeSettingsView(settingsWindowRequest, this); //диалог сервис var dialogService = new DialogService(); //вызов диалога dialogService.ShowDialog( view, //пояснения к SetNewSettings см ниже newSettings => SetNewSettings(newSettings), throwIfNullResult: false, //imageUri - это моя отсебятина от лени, можно удалять из исходников imageUri: new Uri("pack://application:,,,/Red.ico", UriKind.RelativeOrAbsolute) ); В приведенном выше коде вбейте правильный Sborka_S_Ishodnikami_I_Sootvetstvuyushij_Namespace. Вот пример стиля OkCancelDialogStyle: На всякий случай и это: public partial class ChangeSettingsView : UserControl { public ChangeSettingsView(SettingsLocalEvent request, object viewModel) { InitializeComponent(); DataContext = viewModel; } } Так же во ViewModel можно реализрвать интерфейс IDialogCloseable (IDialogCloseable), тогда будет event: public event DialogCloseEvent DialogClose = delegate { }; Можно во ViewModel прописать такой метод: protected void RaiseDialogClose(object sender, bool? dialogResult, TResponse result) { DialogClose(sender, new DialogCloseEventArgs(dialogResult, result)); } Тогда, например, в какой-то команде (ICommand) по клику кнопки OK можно будет вызвать этот event: private void ExecuteOk(object input) { RaiseDialogClose(this, true, new NewSettingsLocalEvent() { } ); } Кстати, в самом начале кода в данном ответе, где написано "пользоваться можно так", там при вызове диалога есть такой параметр: newSettings => SetNewSettings(newSettings). Если не ошибаюсь, то этот Func как раз и вызывается при нажатии OK в диалоге. Здесь SetNewSettings это просто какой-то метод, суть в том, что он вызовется при нажатии ОК (опять же, если не ошибаюсь). исходники [StyleTypedProperty(Property="ContainerStyleProperty", StyleTargetType=typeof(DialogContainer))] public class DialogService : IDialogService { public static Style GetContainerStyle(DependencyObject obj) { return (Style)obj.GetValue(ContainerStyleProperty); } public static void SetContainerStyle(DependencyObject obj, Style value) { obj.SetValue(ContainerStyleProperty, value); } public static readonly DependencyProperty ContainerStyleProperty = DependencyProperty.RegisterAttached( "ContainerStyle", typeof(Style), typeof(DialogService), new PropertyMetadata(null) { CoerceValueCallback = ContainerStylePropertyCoerce } ); private static object ContainerStylePropertyCoerce(DependencyObject d, object baseValue) { var style = baseValue as Style; if (style != null) { if (style.TargetType != typeof(DialogContainer)) { return DependencyProperty.UnsetValue; } return style; } return null; } public void ShowDialog(FrameworkElement view) { this.ShowDialog(view, (DialogCallback)null); } public void ShowDialog(FrameworkElement view, Action onSuccess, bool throwIfNullResult = false, Uri imageUri = null) { this.ShowDialog( view, (dr, result) => { if (dr == true && onSuccess != null) onSuccess(result); }, throwIfNullResult, imageUri ); } public void ShowDialog(FrameworkElement view, DialogCallback callback, bool throwIfNullResult = false, Uri imageUri = null) { var container = new DialogContainer(view); if (imageUri != null) container.Icon = BitmapFrame.Create(imageUri); var dialogResult = container.ShowDialog(); if (callback != null) { T result = default(T); if (dialogResult == true) { if (throwIfNullResult && container.DialogContainerResult == null) { throw new NullReferenceException("DialogContainerResult"); } if (container.DialogContainerResult != null) { if (container.DialogContainerResult is T) { result = (T)container.DialogContainerResult; } } } callback(dialogResult, result); } } public MessageBoxResult ShowMessage( string message, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None, MessageBoxOptions options = MessageBoxOptions.None ) { return MessageBox.Show(message, caption, button, icon, defaultResult, options); } } public delegate void DialogCallback(bool? dialogResult, T parameter); public interface IDialogService { void ShowDialog(FrameworkElement view); void ShowDialog(FrameworkElement view, Action onSuccess, bool throwIfNullResult = false, Uri imageUri = null); void ShowDialog(FrameworkElement view, DialogCallback callback, bool throwIfNullResult = false, Uri imageUri = null); MessageBoxResult ShowMessage( string message, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None, MessageBoxOptions options = MessageBoxOptions.None ); } public class DialogContainer : Window { static DialogContainer() { DefaultStyleKeyProperty.OverrideMetadata( typeof(DialogContainer), new FrameworkPropertyMetadata(typeof(DialogContainer)) ); } //private IDialogCloseable _closeable = null; private static Style _GetBaseStyle; private static Style GetBaseStyle { get { return _GetBaseStyle ?? (_GetBaseStyle = Application.Current.FindResource(typeof(DialogContainer)) as Style); } } internal DialogContainer(FrameworkElement view, Window owner = null) { Content = view; this.Owner = owner ?? Application.Current.Windows.OfType().Where(x => x.IsActive).FirstOrDefault() ?? Application.Current.MainWindow; WindowStartupLocation = owner == null ? System.Windows.WindowStartupLocation.CenterOwner : System.Windows.WindowStartupLocation.CenterScreen; if (view != null) { var containerStyle = DialogService.GetContainerStyle(view); if (containerStyle != null) { if (containerStyle.BasedOn == null) { containerStyle.BasedOn = GetBaseStyle; } Style = containerStyle; } CloseableDescriptor = GetFirstIDialogCloseable(view, view.DataContext); var dataContextBinding = new Binding() { Source = view, Path = new PropertyPath(FrameworkElement.DataContextProperty) }; SetBinding(FrameworkElement.DataContextProperty, dataContextBinding); } } private DynamicCloseableDescriptor CloseableDescriptor = null; private DynamicCloseableDescriptor GetFirstIDialogCloseable(params object[] items) { var descriptors = from x in items where x != null let icloseable = x.GetType() .GetInterfaces() .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDialogCloseable<>)) .FirstOrDefault() where icloseable != null select new DynamicCloseableDescriptor(this, x, icloseable); var target = descriptors.FirstOrDefault(); return target; } private sealed class DynamicCloseableDescriptor : IDisposable { private static readonly MethodInfo OpenGenericDelegateMethod; static DynamicCloseableDescriptor() { OpenGenericDelegateMethod = typeof(DialogContainer) .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) .Where(x => x.Name == "DynamicCloseDialog") .FirstOrDefault(); } readonly object Target; readonly Type ICloseableType = null; readonly Delegate DynamicDelegate = null; readonly EventInfo DialogCloseEvent = null; readonly Type DialogResultType = null; public DynamicCloseableDescriptor(DialogContainer container, object target, Type iCloseableType) { if (container == null) throw new ArgumentNullException("container"); if (target == null) throw new ArgumentNullException("target"); if (iCloseableType == null) throw new ArgumentNullException("iCloseableType"); Target = target; ICloseableType = iCloseableType; DialogResultType = ICloseableType.GetGenericArguments()[0]; DialogCloseEvent = ICloseableType.GetEvent("DialogClose"); DynamicDelegate = Delegate.CreateDelegate( DialogCloseEvent.EventHandlerType, container, OpenGenericDelegateMethod.MakeGenericMethod(DialogResultType) ); DialogCloseEvent.AddEventHandler(Target, DynamicDelegate); } public void ExecuteCancelIfCan() { if (IsDisposed) throw new ObjectDisposedException("DynamicCloseableDescriptor"); var cmd = ((ICancelable)Target).CancelCommand; if (cmd != null) { if (cmd.CanExecute(null)) { cmd.Execute(null); } } else { DynamicDelegate.DynamicInvoke(new[] { Target, null }); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private bool IsDisposed = false; private void Dispose(bool isDisposing) { if (!IsDisposed && isDisposing) { DialogCloseEvent.RemoveEventHandler(Target, DynamicDelegate); IsDisposed = true; } } } internal object DialogContainerResult = null; /// /// Вызывается когда IDialogCloseable запускает DialogCloseEvent. /// Вызов этого метода строится динамически в DynamicCloseableDescriptor /// /// /// Event Sender /// Event args private void DynamicCloseDialog(object sender, DialogCloseEventArgs e) { if (CloseableDescriptor != null) { CloseableDescriptor.Dispose(); CloseableDescriptor = null; } if (e != null) { if (e.DialogResult == true) { DialogContainerResult = e.Result; } this.DialogResult = e.DialogResult; } else { this.DialogResult = false; } } protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { if (CloseableDescriptor == null) { base.OnClosing(e); } else { e.Cancel = true; this.Dispatcher.BeginInvoke((Action)CloseableDescriptor.ExecuteCancelIfCan); } } } public static class DialogHelper { public static void SendOk(this DialogCloseEvent handler, object sender, T result) { if (handler != null) { handler(sender, DialogCloseEventArgs.Success(result)); } } public static void SendCancel(this DialogCloseEvent handler, object sender) { if (handler != null) { handler(sender, DialogCloseEventArgs.Cancel()); } } } public class DialogCloseEventArgs : EventArgs { public bool? DialogResult { get; private set; } public T Result { get; private set; } public DialogCloseEventArgs(bool? dialogResult, T result) { DialogResult = dialogResult; Result = result; } private static readonly DialogCloseEventArgs CancelResult; static DialogCloseEventArgs() { CancelResult = new DialogCloseEventArgs(false, default(T)); } public static DialogCloseEventArgs Success(T result) { return new DialogCloseEventArgs(true, result); } public static DialogCloseEventArgs Cancel() { return CancelResult; } } //public delegate void DialogCloseEvent(bool dialogResult, T result); /// /// Запрос на закрытие диалога /// /// Тип возвращаемого результата /// Отправитель /// Результат диалога, по умолчанию - null = Cancel public delegate void DialogCloseEvent(object sender, DialogCloseEventArgs e = null); public interface ICancelable { ICommand CancelCommand { get; } } public interface IDialogCloseable : ICancelable { event DialogCloseEvent DialogClose; }

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

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