Взаимодействие с двумя окнами MVVM C#

179
19 апреля 2018, 09:48

Пробую освоить MVVM, дается очень тяжело. В теории вроде понятно, а вот как на практике реализовать, не очень понимаю. Тем более хочу использовать команды. Делаю простое приложение, два окна - одно основное, и второе. На первом расположена кнопка при нажатии на которую необходимо открыть второе окно-форму, в котором заполняются данные (имя и фамилия), при кнопки Ок - записать все в коллекцию (банально в List), как передать данные в список, то же вопрос. Примерно выглядеть будет так:

Дерево проекта:

Разметка первой вью

<Window x:Class="CommandMVVM.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:CommandMVVM"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid DataContext="{StaticResource MainWindowViewModel}">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <Button Name="v_Button_ToggleNextWindow" Grid.Row="1" Command="{Binding ToggleNextWindowCommand}"  VerticalAlignment="Center" Content="Second Window" HorizontalAlignment="Center" Width="90"/>
</Grid>

Вторая вью

<Window x:Class="CommandMVVM.SecondWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:CommandMVVM"
    mc:Ignorable="d"
    Title="SecondWindow" Height="450" Width="800">
<Grid DataContext="{StaticResource SecondWindowViewModel}">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <TextBlock Name="Name" Grid.Row="0" Margin="560,71,10,281" Command="{Binding .....}"/>
    <TextBlock Name="LastName" Grid.Row="0" Margin="560,71,10,281" Command="{Binding .....}"/>
    <Button Name="v_Button_ToggleNextWindow" Grid.Row="1" Command="{Binding .....}"  VerticalAlignment="Center" Content="Ok" HorizontalAlignment="Center" Width="90"/>
</Grid>

Класс реализующий интерфейс комманд

 public class DelegateCommand : ICommand
{
    private readonly Action _command;
    private readonly Func<bool> _canExecute;
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public DelegateCommand(Action command, Func<bool> canExecute = null)
    {
        if (command == null)
            throw new ArgumentNullException();
        _canExecute = canExecute;
        _command = command;
    }
    public void Execute(object parameter)
    {
        _command();
    }
    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute();
    }
}

Класс реализующий INotifyPropertyChanged

public abstract class NotificationObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected void SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            NotifyPropertyChanged(propertyName);
        }
    }
}

Базовый класс от которого потом наследую ViewModel

 public class BaseViewModel : NotificationObject //(реализует INotifyPropertyChanged)
{
}

Ну и сами вьюмодели. Вот в них и не знаю, что необходимо делать...

 public class MainWindowViewModel : BaseViewModel
{
}
public class SecondWindowViewModel : BaseViewModel
{
}

В биндинге пока пусто, так как не понимаю, что туда биндить, какой-то Action. Еще один вопрос, если использовать в проекте комманды, использовать события компонентов вью это моветон или нормальная практика?

Answer 1

Проблема в коде вида

DataContext="{StaticResource SecondWindowViewModel}"

Этот код, с одной стороны, противоречит принципам MVVM: окно не имеет права ни создавать, ни выбирать себе VM.

С другой стороны, отвлекаясь от иделогии в практическую сторону, если у вас VM создаются отдельно, у вас нет нормальной возможности связать их между собой. Соответственно передача данных между ними становится сложным заданием.

Я бы предложил воспользоваться идеей отсюда и создавать VM отдельно. Ваш код будет выглядеть как-то так:

<Application x:Class="Namespace.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!-- убрал StartupUri -->
</Application>
public partial class App : Application
{
    MainWindowViewModel mainVM;
    SecondWindowViewModel secondVM;
    public App()
    {
        // тут вы связываете между собой VM, например, так:
        mainVM = new MainWindowViewModel();
        secondVM = new SecondWindowViewModel(mainVM);
        // или наоборот mainVM = new MainWindowViewModel(secondVM)
    }
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        new MainWindow() { DataContext = mainVM }.Show();
    }
}

и т. д.

READ ALSO
Изменить под регулярные выражения

Изменить под регулярные выражения

Как заменить кучу Replace на регулярные выражения ?

169
Невозможно разрешить удалённое имя

Невозможно разрешить удалённое имя

Запускаю у себя - всё нормальноЗапускает друг - ошибка

180
Ошибка в Encoding.Unicode

Ошибка в Encoding.Unicode

Имею код:

198
Поиск изображения на экране

Поиск изображения на экране

Я хочу реализовать простенький скриптСмысл его заключается в том, чтобы когда вылезает окно с ошибкой на каком-либо сайте моя программа искала...

217