Обработка событий в MVVM

Итак, это мое первое приложение MVVM. У меня есть модель представления «оболочка» с именем MainWindowViewModel для главного окна, которая в основном разбивает представление на две страницы: MainWindowRibbon и MainWindowFrame. MainWindowViewModel хранит обе страницы как свойства, которые я планирую использовать привязку данных для обновления в пользовательском интерфейсе. Вот часть кода для справки:

XAML MainWindowView ~

<Grid>
    <Frame Content = "{Binding MainWindowRibbon}" Grid.Column = "0" Grid.Row = "0"/>
    <ScrollViewer>
        <Frame Content = "{Binding MainWindowFrame}"/>
    </ScrollViewer>
</Grid>

Код MainWindowView позади~

    public partial class MainWindowView : Window
{
    public MainWindowView()
    {
        InitializeComponent();
        mainWindowViewModel = new MainWindowViewModel();
        DataContext = mainWindowViewModel;
    }
    public MainWindowViewModel mainWindowViewModel;
}

Код MainWindowViewModel~

        public MainWindowViewModel()
    {
        //MainWindowRibbon and MainWindowFrame are declared as public Page properties
        MainWindowRibbon = new MainWindowRibbonView();
        MainWindowFrame = new WelcomePageView();
    }

MainWindowRibbonView, как и MainWindowView, создает экземпляр MainWindowRibbonViewModel.

Моя проблема возникает, когда я хочу использовать событие в MainWindowRibbonViewModel, которое вызовет MainWindowViewModel для переназначения страницы MainWindowFrame. Я не знаю, как подключить кнопку кнопки панели навигации, которую я создал в MainWindowRibbonView, чтобы вызвать событие или изменение в MainWindowViewModel.

Я не знаю, является ли то, как я это организовал, идеальным. Пожалуйста, дайте мне знать, если мне нужно пересмотреть.

Если бы кто-нибудь мог помочь мне определить лучший подход или даже просто работающий, я был бы очень благодарен.

P.S. Извините, если соглашения об именах не самые лучшие.

Редактировать: Усвоенный урок: слушайте Джо.

Я думаю, вы неправильно понимаете, что такое Frame. Это ContentControl. Вы не устанавливаете его Content для какого-либо элемента пользовательского интерфейса. Вместо этого вы устанавливаете его для хранения экземпляра некоторой модели представления (например, PersonViewModel). Пользовательский интерфейс для него представлен в XAML; Вы устанавливаете значение по умолчанию DataTemplate для этого типа класса модели представления. DataTemplate имеет элементы управления пользовательского интерфейса, которые привязываются к свойствам этой модели представления. Таким образом, всякий раз, когда свойство Content установлено для объекта этого типа, определенного в DataTemplate, оно будет отображать пользовательский интерфейс, который вы разместили в связанном свойстве DataTemplate.

Joe 20.03.2022 22:17

Вы можете прочитать это: docs.microsoft.com/en-us/dotnet/desktop/wpf/data/…

Joe 20.03.2022 22:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
38
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я полагаю, это зависит от того, какую кнопку вы используете на панели навигации. это RadioButton? А RibbonToggleButton? Это обычная привязка кнопки к ICommand?

Поскольку вы назвали свою панель навигации «лентой», давайте предположим, что это RibbonToggleButton (который по-прежнему в основном является флажком). Если он отмечен, вы показываете какую-то модель представления вашей «страницы 1». Если он не отмечен, вам следует использовать другую модель представления, представляющую вашу «страницу 2».

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

Я мог бы переписать ваше MainWindow, чтобы оно выглядело так (обратите внимание на свойство IsChecked для некоторого логического свойства в вашей модели представления, например:)

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height = "Auto"/>  <!-- The "Ribbon" -->
        <RowDefinition Height = "*"/>     <!-- The page content -->
    </Grid.RowDefinitions>

    <ToggleButton Content = "Show Page 1" IsChecked = "{Binding ShowPage1}"/>

    <ScrollViewer Grid.Row=1>
        <Frame Content = "{Binding CurrentViewModel}"/>
    </ScrollViewer>
</Grid>

И я мог бы написать вашу модель представления следующим образом: (Обратите внимание, что я предполагаю, что она реализует INotifyPropertyChanged, и я вызываю функцию RaisePropertyChanged, которую я не показываю.

public class Page1ViewModel {}  // Fill this out with Page 1 properties
public class Page2ViewModel {}  // Fill this out with Page 2 properties

// MainWindowViewModel.  Implements INotifyPropertyChanged.  Implementation
// is not shown here.

public class MainWindowViewModel : INotifyPropertyChanged
{
    private Page1ViewModel = new Page1ViewModel();
    private Page2ViewModel = new Page2ViewModel();

    public MainWindowViewModel()
    {
        _currentViewModel = Page1ViewModel;
        ShowPage1 = true;
    }

    private object _currentViewModel;

    // The current contents of the main frame.

    public object CurrentViewModel 
    { 
        get => _currentViewModel;
        set
        {
            if (value == _currentViewModel)
                return;

            _currentViewModel = value;
            RaisePropertyChanged();
    }

    // Should CurrentViewModel be page 1 or page 2?

    public bool ShowPage1 
    { 
        get => return _currentViewModel == Page1ViewModel;
        set
        {
            if (value == ShowPage1)
                return;

            CurrentViewModel = value ? Page1ViewModel : Page2ViewModel;
            RaisePropertyChanged();
        }
    }
}

Обратите внимание, что я не показываю вам никаких свойств Page1VieModel или Page2ViewModel и не показываю вам неявные DataTemplate, которые, как я предполагаю, вы будете писать для них.

Также я предполагаю, что ваша панель навигации (и MainWindowView в целом) имеет DataContext, который уже установлен на MainWindowViewModel

Реализация с командной кнопкой или RadioButton будет совсем другой.

Спасибо, Джо, за исчерпывающий ответ. После просмотра я решил включить «MainWindowRibbon» (который не включает кнопки переключения ленты, которые я могу исследовать сейчас, но это было просто неудачное название. Я использовал обычные кнопки, используя ICommand в шаблоне ItemsControl.) в главное окно. Я полагаю, что мой самый большой вопрос сейчас заключается в том, как свойство Content получает доступ к xaml, с кодом позади, с классом модели представления? то есть каков тип объекта модели представления? Извините, если я сейчас нуб.

Madison Roby 21.03.2022 16:37

Вы предлагаете мне создать шаблон данных в основном xaml для каждой страницы? Извините, я соединил большинство точек, но не все

Madison Roby 21.03.2022 16:40

Отвечая на ваш второй вопрос, да, я предлагаю вам создать DataTemplate для каждой страницы. DataTemplate действительно является страницы, по крайней мере, с точки зрения пользовательского интерфейса. Между тем, Content каждой страницы — это своего рода объект модели представления, который сам по себе не имеет пользовательского интерфейса (например, некоторый класс модели представления в вашем проекте). Это позволяет вам позже добавлять новые типы страниц, если вы хотите, без изменения механизма. По сути, вы должны перестать думать о «контенте» с точки зрения пользовательского интерфейса/элементов управления и начать думать о нем с точки зрения моделей представления. DataTemplating переводит последнее в первое.

Joe 21.03.2022 17:08

WPF предназначен для работы таким образом, с шаблонами. Вы упомянули, что использовали ItemsControl, верно? Что ж, управление работает так же, за исключением того, что вместо управления одним ContentControl оно управляет многими. Вы привязываете ItemsControl к списку моделей представления, хранящихся где-то в вашем коде. Он показывает вам список ContentControls, по одному для каждого элемента в вашем списке. У него есть свойство ItemTemplate, которое используется для преобразования каждого элемента списка в пользовательский интерфейс. Итак, еще раз, элементы в вашем списке не являются элементами управления пользовательским интерфейсом, они являются моделями просмотра.

Joe 21.03.2022 17:15

Фантастическое спасибо большое, чувак. А теперь самое интересное — переработка всего проекта :) Хотя не так уж и плохо. Спасибо еще раз.

Madison Roby 21.03.2022 17:19

Другие вопросы по теме