Модальный диалог с WPF с использованием MVVM - PullRequest
0 голосов
/ 15 апреля 2020

Вокруг целого rnet есть тонны и тонны статей об этой топике c, но я просто не могу обернуть ее вокруг. Большинство статей используют код позади, но я хочу придерживаться «чистого» MVVM, так как я пытаюсь изучить его. Кроме того, я явно не хочу использовать какой-либо другой фреймворк (MVVMlight, Ninject ...). Я просто хочу придерживаться того, что может предложить WPF. Я знаю, что об этом часто спрашивают, но я не нашел mvvm или не указал c достаточно.

Моя задача проста: я хочу увидеть самое простое решение открытия модального диалога, отправьте ему строку и получите строку из диалогового окна после ее закрытия.

Поэтому я настроил свой MainWindow.xaml с полем ввода текста (TextBox), кнопкой (которая должна открыть модальное диалоговое окно) и текстовый блок, который покажет сообщение, которое я намерен получить из диалогового окна.

В диалоговом окне есть TextBlock, показывающий вводимые пользователем данные из MainWindow.xaml, а также TextBox для ввода некоторого текста и кнопку. Вы догадались: вы нажимаете кнопку, и сообщение, которое я набрал в текстовом поле get, возвращается в MainWindow.xaml. Пожалуйста, обратитесь также к изображениям, которые я включил - я думаю, что это довольно очевидно.

MainWindow.xaml

<Window x:Class="Dialogs.MainWindow"
     ...
Title="First View (Main Window)" Height="240" Width="630">
<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="Main View sayz: "/>
            <TextBox Width="360" Margin="10,0,0,30"/>
        </StackPanel>

        <Button Content="Send to Second View" Command="{Binding SendToSecondViewCommand}" Width="200"/>

        <StackPanel Orientation="Horizontal" Margin="10,30,10,10">
            <TextBlock Text="Second View replies: "/>
            <TextBlock Width="360"/>
        </StackPanel>           

    </StackPanel>

</Grid>
</Window>

SecondView.xaml

<UserControl x:Class="Dialogs.SecondView"
    ...
d:DesignHeight="240" d:DesignWidth="630" Background="BlanchedAlmond">
<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="This is what First View sayz: "/> 
            <TextBlock Width="360"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="Second View replies: "/>
            <TextBox Width="360" Margin="10,0,0,30"/>
        </StackPanel>

            <Button Content="Reply to First View" Command="{Binding ReplyToFirstViewCommand}" Width="200"/>

    </StackPanel>
</Grid>
</UserControl>

Здесь Вот как я реализовал INotifyPropertyChanged (Это на самом деле файл .cs с именем BaseClasses; я знаю, что он не назван правильно ...)

public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
{        
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged<T>(ref T variable, T value,
                    [CallerMemberName] string propertyName = null)
    {
        variable = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

А вот мой базовый класс для команд реле:

public class CommandDelegateBase : ICommand
{        
    public delegate void ExecuteDelegate(object parameter);

    public delegate bool CanExecuteDelegate(object paramerter);

    private ExecuteDelegate execute;

    private CanExecuteDelegate canExecute;

    public CommandDelegateBase(ExecuteDelegate _execute, CanExecuteDelegate _canExecute = null)
    {
        execute = _execute;
        canExecute = _canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return canExecute?.Invoke(parameter) ?? true;
    }

    public void Execute(object parameter)
    {
        execute.Invoke(parameter);
    }

}

Наконец, мои ViewModels: FirstViewModel:

public class FirstViewViewModel: NotifyPropertyChangedBase
{
    private string _sendText;
    public string SendText
    {
        get { return _sendText; }
        set
        {
            _sendText = value;

            OnPropertyChanged(ref _sendText, value);
        }
    }

    public ICommand SendToSecondViewCommand { get; set; }

    public FirstViewViewModel()
    {
        SendToSecondViewCommand = new CommandDelegateBase(SendExecuteCommand, SendCanExecuteCommand);
    }

    private bool SendCanExecuteCommand(object paramerter)
    {
        return true;
    }

    private void SendExecuteCommand(object parameter)
    {
        //Do stuff to :
        // a) show the second view as modal dialog
        // b) submit what I just wrote (SendText)
    }
}

SecondViewModel:

public class SecondViewViewModel : NotifyPropertyChangedBase
{
    private string _replyText;
    public string ReplyText
    {
        get { return _replyText; }
        set
        {
            _replyText = value;

            OnPropertyChanged(ref _replyText, value);
        }
    }

    public ICommand ReplyToFirstViewCommand { get; set; }

    public SecondViewViewModel()
    {
        ReplyToFirstViewCommand = new CommandDelegateBase(ReplyExecuteCommand, ReplyCanExecuteCommand);
    }

    private bool ReplyCanExecuteCommand(object paramerter)
    {
        return true;
    }

    private void ReplyExecuteCommand(object parameter)
    {
        //Do stuff to :
        // a) close the second view 
        // b) reply what I just wrote (ReplyText) back to First View.
    }
}       

В моем решении есть папка с именем "Models", но для простоты она пуста.

Я знаю, что есть решения с вспомогательными классами или сервисами - что бы ни делало mvvm. Я также знаю, что выполнение этого для такой простой задачи, как то, что я хочу, является тихим «излишним», и в нем содержится гораздо больше кода для написания, чем это было бы оправдано для этой цели. Но опять же: я хотел бы узнать это и понять, что я делаю.

Заранее большое спасибо!

MainWindow.xaml SecondView.xaml Structure of my solution

Ответы [ 2 ]

0 голосов
/ 15 апреля 2020

Найти MVVM-чистое решение проблемы программирования, которая может быть простой в других контекстах, часто не простая задача. Тем не менее, создание библиотеки вспомогательных классов - это сценарий «пишите один раз, много раз», поэтому независимо от того, сколько кода требуется, вам не нужно воспроизводить его для каждого использования.

Мой предпочтительный метод для обработки диалоговых сообщений в MVVM используется сервисный модуль, состоящий из двух частей.

  1. Представление регистрирует свой контекст данных (его ViewModel) с помощью DialogService как потенциально желающего отображать диалог - служба будет использовать контекст пользовательского интерфейса View для этого, когда он это делает.

  2. ViewModel вызывает сервис внедренного диалога каждый раз, когда должен отображаться диалог. Вызовы в службу MessageDialog выполняются с использованием шаблона asyn c / await, а не требуют какой-либо другой формы обратного вызова в ViewModel.

Итак, теперь отображение MessageDialog из ViewModel так же просто, как

await _dialogService.ShowMessageAsync(this, "Hello from the dialog service.", perDialogIcon.Information, "Mvvm Dialog Service").ConfigureAwait(false);

или

var response = await _dialogService.ShowDialogAsync(this, perDialogButton.YesNo, "Do you want to continue?", perDialogIcon.Question, "Mvvm Dialog Service").ConfigureAwait(false);

Более подробно об этом я писал в блоге .

Кроме того, ваши свойства ViewModel выглядят немного странно - вы устанавливаете значение вспомогательного поля, а затем передаете его в метод OnPropertyChanged(), где значение устанавливается снова.

0 голосов
/ 15 апреля 2020

Я написал статью на эту тему и предоставил библиотеку и пример приложения. Сама статья длинная ... потому что это не тривиальная топи c ... но вызвать появление диалогового окна может быть так просто:

this.Dialogs.Add(new CustomDialogBoxViewModel()); // dialog box appears here

ОБНОВЛЕНИЕ: Я только что заметил, что мой Библиотека MvvmDialogs в этом пакете фактически ссылается на MvvmLite. Хотя это было следом того, когда я его разрабатывал, самой библиотеке это не нужно, так что вы можете полностью удалить ссылку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...