Ответ 1
Я бы начал с создания класса для запуска приложения. Обычно я называю этот класс чем-то вроде ApplicationViewModel
или ShellViewModel
, хотя технически он может придерживаться разных правил, чем то, что я обычно использовал бы для ViewModel
Этот класс получает экземпляр при запуске и является DataContext
для ShellView
или ApplicationView
// App.xaml.cs
private void OnStartup(object sender, StartupEventArgs e)
{
var shellVM = new ShellViewModel();
var shellView = new ShellView();
shellView.DataContext = shellVM;
shellView.Show();
}
Обычно это единственное место, где я устанавливаю DataContext
для компонента UI напрямую. С этого момента ваши ViewModels являются приложением. Важно помнить об этом при работе с MVVM. Ваши представления - это просто удобный интерфейс, который позволяет пользователям взаимодействовать с ViewModels. На самом деле они не считаются частью кода приложения.
Например, ваш ShellViewModel
может содержать:
-
BoardViewModel CurrentBoard
-
UserViewModel CurrentUser
-
ICommand NewGameCommand
-
ICommand ExitCommand
и ваш ShellView
может содержать что-то вроде этого:
<DockPanel>
<Button Command="{Binding NewGameCommand}"
Content="New Game" DockPanel.Dock="Top" />
<ContentControl Content="{Binding CurrentBoard}" />
</DockPanel>
Это фактически превратит ваш объект BoardViewModel
в пользовательский интерфейс как ContentControl.Content
. Чтобы указать способ рисования BoardViewModel
, вы можете указать DataTemplate
в ContentControl.ContentTemplate
или использовать неявный DataTemplates
.
Неявный DataTemplate - это просто DataTemplate
для класса, не связанного с ним x:Key
. WPF будет использовать этот шаблон в любое время, когда он встречает объект указанного класса в пользовательском интерфейсе.
Таким образом, используя
<Window.Resources>
<DataTemplate DataType="{x:Type local:BoardViewModel}">
<local:BoardView />
</DataTemplate>
</Window.Resources>
будет означать, что вместо рисования
<ContentControl>
BoardViewModel
</ContentControl>
он рисует
<ContentControl>
<local:BoardView />
</ContentControl>
Теперь BoardView
может содержать что-то вроде
<ItemsControl ItemsSource="{Binding Squares}">
<ItemsControl.ItemTemplate>
<ItemsPanelTemplate>
<UniformGrid Rows="3" Columns="3" />
</ItemsPanelTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
и он нарисовал бы доску с использованием 3x3 UniformGrid
, причем каждая ячейка содержала содержимое вашего массива Squares
. Если ваше свойство BoardViewModel.Squares
оказалось массивом объектов TileModel
, то каждая ячейка ячейки будет содержать TileModel
, и вы можете снова использовать неявный DataTemplate
, чтобы сообщить WPF, как рисовать каждый TileModel
Теперь, как ваш ViewModel
получает свои фактические объекты данных, это зависит от вас. Я предпочитаю абстрагировать весь доступ к данным за классом, например, Repository
, а мой ViewModel
просто вызывает что-то вроде SodokuRepository.GetSavedGame(gameId);
. Это упрощает тестирование и поддержку приложения.
Однако вы получаете свои данные, имейте в виду, что ViewModel
и Models
являются вашим приложением, поэтому они должны нести ответственность за получение данных. Не делайте этого в View
. Лично мне нравится поддерживать мой слой Model
для простых объектов, содержащих только данные, поэтому всегда выполняйте операции доступа к данным из моих ViewModels.
Для связи между ViewModels
у меня есть статья в моем блоге об этом. Подводя итог, используйте систему обмена сообщениями, такую как Microsoft Prism EventAggregator
или MVVM Light Messenger
. Они работают как своего рода система подкачки: любой класс может подписаться на получение сообщений определенного типа, и любой класс может передавать сообщения.
Например, ваш ShellViewModel
может подписаться на получение сообщений ExitProgram
и закрыть приложение, когда он его услышит, и вы можете транслировать сообщение ExitProgram
из любого места приложения.
Я предполагаю, что другой метод заключается в том, чтобы просто привязывать обработчики из одного класса к другому, такие как вызов CurrentBoardViewModel.ExitCommand += Exit;
из ShellViewModel
, но я нахожу это беспорядочным и предпочитаю использовать систему обмена сообщениями.
В любом случае, я надеюсь, что ответы на некоторые из ваших вопросов и укажут вам в правильном направлении. Goodluck с вашим проектом:)