Дизайн пользовательского интерфейса с использованием шаблона MVVM - PullRequest
5 голосов
/ 27 февраля 2011

Я пытаюсь выбрать лучший способ реализовать этот интерфейс в манере MVVM.Я новичок в WPF (например, 2 месяца), но у меня огромный опыт работы с WinForms.enter image description here

ListBox здесь действует как TabControl (поэтому он переключает представление вправо) и содержит в основном тип элемента, отображаемый в таблицах.Весь пользовательский интерфейс является динамическим (элементы ListBox, TabItems и Columns определяются во время выполнения).Приложение предназначено для WPF и Silverlight.

Классы, которые нам нужны для ViewModel:

public abstract class ViewModel : INotifyPropertyChanged {}
public abstract class ContainerViewModel : ViewModel
{
    public IList<ViewModel> Workspaces {get;set;}
    public ViewModel ActiveWorkspace {get;set;}
}
public class ListViewModel<TItem> where TItem : class
{
    public IList<TItem> ItemList { get; set; }
    public TItem ActiveItem { get; set; }
    public IList<TItem> SelectedItems { get; set; }
}
public class TableViewModel<TItem> : ListViewModel<TItem> where TItem : class
{
    public Ilist<ColumnDescription> ColumnList { get; set; }
}

Теперь вопрос заключается в том, как подключить это к View.

Есть два базовых подхода, которые я вижу здесь:

  • С XAML: из-за отсутствия поддержки Generics в XAML я потеряю строгую типизацию.
  • Без XAML: я могуповторное использование того же ListView<T> : UserControl.

Далее, как связать данные, я вижу 3 метода здесь (с XAML или без здесь не имеет значения).Поскольку не существует простой DataBinding для столбцов DataGrid или TabItems TabControl, методы, которые я вижу, являются:

  • Использовать DataBinding с IValueConverter: я думаю, что это не будет работать с WPF | Silverlight из встроенных элементов управления, так какнекоторые свойства, которые мне нужны, доступны только для чтения или не могут быть привязаны в дуплексном режиме.(Я не уверен в этом, но я чувствую, что это не будет работать).
  • Используйте ручную логику, подписавшись на INotifyPropertyChanged в View: ViewModel.PropertyChanged + = .... ViewModel.ColumnList.CollectionChanged + = ....

  • Используйте пользовательские контроллеры, поддерживающие эту привязку: используйте код самостоятельно или найдите сторонние элементы управления, поддерживающие эту привязку (мне не нравится этот параметр, мой навык WPFслишком низок, чтобы кодировать это сам, и я сомневаюсь, что найду бесплатные элементы управления)


Обновление: 28.02.2011 Все становится все хуже и хуже, ярешил использовать TreeView вместо ListBox, и это был кошмар.Как вы, наверное, догадались, TreeView.SelectedItems является свойством только для чтения, поэтому для него нет привязки данных.Хм, хорошо, давайте сделаем это по-старому и подпишемся на события, чтобы синхронизировать представление с viewmodel.В этот момент неожиданно обнаружилось, что DisplayMemberPath ничего не делает для TreeView (ммм, хорошо, давайте сделаем его старым способом ToString ()).Затем в методе View я пытаюсь синхронизировать ViewModel.SelectedItem с TreeView:

private void UpdateTreeViewSelectedItem()
{
    //uiCategorySelector.SelectedItem = ReadOnly....

    //((TreeViewItem) uiCategorySelector.Items[uiCategorySelector.Items.IndexOf(Model.ActiveCategory)]).IsSelected = true;
    // Will not work Items's are not TreeViewItem but Category object......

    //((TreeViewItem) uiCategorySelector.ItemContainerGenerator.ContainerFromItem(Model.ActiveCategory)).IsSelected = true;
    //Doesn't work too.... NULL // Changind DataContext=Model and Model = new MainViewModel line order doesn't matter.
    //Allright.. figure this out later...
}

И ни один из методов, которые я мог придумать, не сработал ....

А вот ссылка намой пример проекта, демонстрирующий Ад библиотеки управления с MVVM: http://cid -b73623db14413608.office.live.com / self.aspx / .Public / MVVMDemo.zip

Ответы [ 4 ]

1 голос
/ 05 марта 2011

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

http://alexburtsev.wordpress.com/2011/03/05/mvvm-pattern-in-silverlight-and-wpf/

1 голос
/ 27 февраля 2011

Ответ Макика на самом деле даже сложнее, чем нужно.Вам не нужны селекторы шаблонов вообще.Чтобы создать гетерогенный элемент управления вкладками:

  1. Создайте класс модели представления для каждого типа представления, которое вы хотите отобразить в качестве элементов вкладки.Убедитесь, что в каждом классе реализовано свойство Text, содержащее текст, который вы хотите отобразить на вкладке для своего элемента.

  2. Создайте DataTemplate для каждого класса модели представления с помощьюDataType задайте тип класса и поместите шаблон в словарь ресурсов.

  3. Заполните коллекцию экземплярами ваших моделей представления.

  4. Создайте TabControl и привяжите его ItemsSource к этой коллекции и добавьте ItemTemplate, который отображает свойство Text для каждого элемента.

Вот пример, которыйне использует модели представления (и это также не реализует свойство Text, потому что объекты, к которым я привязываюсь, являются простыми типами CLR), но показывает, как выбор шаблона работает в этом контексте:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:sys="clr-namespace:System;assembly=mscorlib"
  xmlns:coll="clr-namespace:System.Collections;assembly=mscorlib">
  <DockPanel>  
   <DockPanel.Resources>
        <coll:ArrayList x:Key="Data">
          <sys:String>This is a string.</sys:String>
          <sys:Int32>12345</sys:Int32>
          <sys:Decimal>23456.78</sys:Decimal>
        </coll:ArrayList>
        <DataTemplate DataType="{x:Type sys:String}">
          <TextBlock Text="{Binding}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type sys:Int32}">
          <StackPanel Orientation="Horizontal">
           <TextBlock>This is an Int32:</TextBlock>
           <TextBlock Text="{Binding}"/>
          </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type sys:Decimal}">
          <StackPanel Orientation="Horizontal">
           <TextBlock>This is a Decimal: </TextBlock>
           <TextBlock Text="{Binding}"/>
          </StackPanel>
        </DataTemplate>
   </DockPanel.Resources>
    <TabControl ItemsSource="{StaticResource Data}">  
      <TabControl.ItemTemplate>
       <DataTemplate>
        <TextBlock Text="{Binding}"/>
       </DataTemplate>
      </TabControl.ItemTemplate>
    </TabControl>
  </DockPanel>
</Page>

Конечно, в реальном приложении MVVM эти DataTemplate s будут использовать UserControl s для сопоставления каждого типа его представлению:

<DataTemplate DataType="{x:Type my:ViewModel}">
   <my:View DataContext="{Binding}"/>
</DataTemplate>
1 голос
/ 28 февраля 2011

Maciek и Robert уже дали вам несколько идей о том, как это реализовать.

Для специфики привязки столбцов DataGrid я настоятельно рекомендую ответ Мелеака на этот вопрос.

Аналогично тому, что вы можете использовать вложенные свойства (или Поведения ) и по-прежнему поддерживать чистую ViewModel в MVVM.

Я знаю кривую обучения для WPFдовольно крутой, и вы уже боретесьЯ также знаю, что следующее предложение не помогает этому и даже делает эту кривую круче.Но ваш сценарий достаточно сложен, поэтому я бы порекомендовал использовать PRISM .

0 голосов
/ 27 февраля 2011

Чтобы подключить вашу ViewModel к вашему представлению, вам нужно назначить DataContext представления.Обычно это делается в конструкторе представления.

public View()
{
    DataContext = new ViewModel();
}

Если вы хотите увидеть эффект модели представления во время разработки, вам нужно объявить его в XAML, в ресурсах представления назначить ключ дляи затем установите DataContext цели через StaticResource.

<UserControl
xmlns:vm="clr-namespace:MyViewModels
>
    <UserControl.Resources>
         <vm:MyViewModel x:Key="MyVM"/>
    </UserControl.Resources>
    <MyControl DataContext={StaticResource MyVM}/>
</UserControl>

(Выше показано, как работает трюк времени разработки)

Поскольку вы имеете дело со сценарием, который включает в себяКонтейнер, такой как TabControl, я бы рекомендовал рассмотреть следующие вещи:

  • Держите ваш дочерний ViewModels в свойстве типа ObservableCollection
  • Привязка TabControls ItemsSource к этому свойству
  • Создание нового представления, производного от TabItem
  • Использование селектора шаблонов для автоматического выбора типа представления на основе типа модели представления.
  • Добавление ID, доступного для вашего дочернего ViewModelsи создайте функциональность для закрытия представлений.

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

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