#c# #mvvm #uwp #template10
#c# #mvvm #uwp #template10
Вопрос:
В данный момент я изучаю UWP в попытке перенести старый Win32 на новую платформу. Я использую Template10, и пока все работает нормально, за исключением того, что я немного запутался в том, как реализовать проблему, описанную ниже.
Проблема: на странице мне приходится постоянно удалять и вставлять пользовательские элементы управления в зависимости от свойства модели представления. Пользовательские элементы управления довольно сложны, и все они выглядят и ведут себя по-разному. Представьте себе мастер с кнопками «Назад» и «Далее». При каждом нажатии мне приходится удалять старое содержимое и вставлять новое, с совершенно другой моделью просмотра.
Вопрос: каков был бы рекомендуемый способ реализации этого способом MVVM?
На данный момент моя единственная идея — отправить сообщение из модели просмотра страницы и подписаться на сообщение в коде страницы, где я могу создать требуемый компонент и динамически вставить его на страницу (после удаления старого).
В MyPageViewModel:
public IComponentViewModel CurrentComponent {get; set;}
...
public DelegateCommand NextItemCommand = new DelegateCommand(() =>
{
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Publish(CurrentComponent);
});
В коде MyPage.xaml.cs за
public MyPage()
{
InitializeComponent();
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Subscribe(OnItemChanged);
}
private void OnItemChanged(IComponentViewModel viewModel)
{
switch (viewModel.Type)
{
case 1:
// create the new user control and insert it in the container
var component = new TypeOneComponent();
component.DataContext = (TypeOneCompoentViewModel)viewModel;
// ...
case 2:
...
}
}
Не уверен, что это лучший подход.
Ответ №1:
В последнее время я сам думал о подходе мастера. Мне кажется, что FlipView
с измененными шаблонами кнопок влево / вправо — самый простой подход. У моей WizardViewModel было бы несколько дочерних моделей просмотра; что-то вроде Page1ViewModel, Page2ViewModel и так далее. Я твердо уверен, что каждая модель просмотра страницы будет иметь выделенную, UserControl
поэтому пользовательский интерфейс может быть уникальным, но не динамичным — я думаю, имеет смысл разрабатывать динамический пользовательский интерфейс, используя адаптивный пользовательский интерфейс, который представляет собой совершенно другую концепцию.
Псевдокод может выглядеть следующим образом:
public interface IWizardPage { }
public class Page1ViewModel : ViewModelBase, IWizardPage { }
public class Page2ViewModel : ViewModelBase, IWizardPage { }
public class Page3ViewModel : ViewModelBase, IWizardPage { }
public class MainPageViewModel : ViewModelBase
{
public IWizardPage CurrentPage { get; set; }
public IWizardPage Page1ViewModel { get; set; }
public IWizardPage Page2ViewModel { get; set; }
public IWizardPage Page3ViewModel { get; set; }
}
И это:
<FlipView Template="{StaticResource WizardFlipView}"
SelectedItem="{Binding CurrentPage, Mode=TwoWay}">
<Page1UserControl DataContext="{Binding Page1ViewModel}" />
<Page2UserControl DataContext="{Binding Page2ViewModel}" />
<Page3UserControl DataContext="{Binding Page3ViewModel}" />
</FlipView>
Это всего лишь рекомендация. Но, отвечая на ваш вопрос, это было бы очень приемлемо для шаблона MVVM. Я также думаю, что это позволило бы вам быть очень гибким, не становясь настолько динамичным, чтобы обслуживание занимало приемлемое время. Существует множество способов создания мастеров. Я думаю, что это было бы прекрасным решением, подходящим для шаблона MVVM и подходящим для шаблона 10.
Желаю удачи.
Комментарии:
1. Мне действительно нравится этот подход — чистый и гибкий.
Ответ №2:
Обычно я использую ItemsControl. Это позволяет вам иметь общий шаблон элемента и шаблон для конкретного элемента, если вы хотите, и вы можете добавлять / удалять элементы по желанию, привязываясь к ItemsSource.
В вашем примере мастера вы могли бы сделать основной контейнер мастера ItemsControl, который одновременно отображает только один элемент, а страница будет «элементом». Отличие MVVM в том, что вы не добавляете дочерние элементы управления, вы добавляете данные, а затем указываете шаблон для их отображения. Итак, ваши элементы будут простыми объектами poco с привязкой к базе данных.
Для вашего реального примера, я думаю, вы можете добавить дочерние элементы управления в ItemsControl, и они будут отображаться автоматически, даже без использования шаблона, поскольку ContentPresenter знает, как отображать элементы управления. Я бы все равно использовал классы только для данных, хотя, поскольку одним из арендаторов MVVM является отделение данных от пользовательского интерфейса. Таким образом, вашим дочерним элементом будет модель, а вашим шаблоном для конкретного элемента будет макет пользовательского интерфейса, привязанный к данным элемента.
Комментарии:
1. Проблема в том, что разные компоненты должны реализовывать довольно сложную логику. Например, будет компонент, который должен позволять пользователю перетаскивать разные элементы, другой компонент с совершенно другим набором элементов, позволяющий пользователю добавлять и изменять порядок элементов в списке и т.д. Я чувствую, что эта логика должна быть каким-то образом инкапсулирована в UserControl. Верно?
2. @JulianKolev, на мой вкус, но если у меня сложная функциональность пользовательского интерфейса, я разбиваю эту часть на многоразовый UserControl, но делаю его полностью независимым от какой-либо бизнес-логики. Оно должно быть упаковано так, чтобы вы могли вставить его в любой другой проект без изменений. Итак, допустим, у вашего пользовательского элемента управления есть специальная сетка для d amp; d… Я бы создал пользовательский элемент управления MyDragNDropGrid и поместил В него ТОЛЬКО логику пользовательского интерфейса и сделал его похожим на реальный элемент управления, где все свойства можно привязать. Это упрощает привязку к нему простой модели в шаблоне MVVM.
3. действительно, я хочу, чтобы компоненты были полностью независимыми со своими собственными представлениями и логином в коде, лежащем в основе. Это не проблема. Чего я не понимаю, так это где / как поменять их местами в контейнере в зависимости от свойства view model содержащей страницы.
4. @JulianKolev, если вы хотите показывать только по одному за раз, то сделайте свой основной контейнер ContentPresenter и замените Содержимое. Много способов переключиться… триггер в представлении, свойство в виртуальной машине и т.д. Если вы хорошо структурируете свое именование, вам даже не понадобится переключатель или что-то еще. Вы можете создать свое соглашение об именовании ViewsUserControl1.xaml, UserControl2.xaml и т.д. А затем постройте путь на основе другого свойства. Или вы можете сделать то, что делает элемент управления tab, и добавить все представления в TabControl (визуально скрыть вкладки), а затем установить активную страницу на основе свойства int. Как я уже сказал, много способов :).