2016-02-24 5 views
0

Используя эти классы:WPF - Группировка коллекции по типу и шаблонам по типу

public class Person 
{ 
    public string LastName { get; set; } 
    public string FirstName { get; set; } 
} 

public class Employee : Person 
{ 
    public string JobTitle { get; set; } 
} 

public class Customer : Person 
{ 
    public string CustomerID { get; set; } 
} 

И это ViewModel класс:

public class MainViewModel 
{ 
    public ObservableCollection<Person> Persons { get; set; } 
} 

Как я могу создать мою точку зрения, так что я могу сгруппировать элементы MainViewModel.Persons по их производному классу Type и показать <ContentControl> для каждой группы И <ContentControl> должен использовать другой DataTemplate в зависимости от того, что Type принадлежит конкретному гриву ппа?

Предположим, что у меня есть большое количество классов, которые происходят от Person, и моя коллекция MainViewModel.Persons может содержать любой из этих производных классов одновременно.

Я также хотел бы иметь возможность объявить свои различные DataTemplate в XAML, если это возможно.

+0

GetType() возвращает наиболее производный тип, поэтому ' foreach (var person in person) {Console.WriteLine (GetType(). Name);} 'будет печатать,« Клиент, Сотрудник »и т. д., что должно быть хорошей отправной точкой. – Quantic

+0

@Quantic Как это может мне помочь? –

+1

Думаю, вам нужна коллекция наблюдаемых коллекций Observable в сочетании с селектором шаблонов для родителя. Эту коллекцию можно сохранить, слушая событие CollectionChanged оригинальной коллекции наблюдаемых лиц. –

ответ

0

Вот возможный ответ на вопрос: «Как я могу создать свое представление, чтобы я мог группировать элементы MainViewModel.Persons по их производному классу Type?»

static void Main(string[] args) 
    { 
     List<Person> Persons = new List<Person>() { new Employee("john","smith"), new Customer("jane","smoth"), new Person("jack","smath") }; 

     Dictionary<object, List<Person>> objectTypeToListOfThoseObjects = new Dictionary<object, List<Person>>(); 

     // Build dictionary of "types" that maps from type to a list of all people of that type 
     foreach (var person in Persons) 
     { 
      var personType = person.GetType(); 
      if (!objectTypeToListOfThoseObjects.Keys.Contains(personType)) 
      { objectTypeToListOfThoseObjects.Add(personType, new List<Person>()); } 
     } 

     // Group each person by their derived Type by adding persons to the correct dictionary list 
     foreach (var person in Persons) 
     { 
      objectTypeToListOfThoseObjects[person.GetType()].Add(person); 
     } 

     foreach (var personType in objectTypeToListOfThoseObjects.Keys) 
     { 
      Console.WriteLine($"All people with derived type {personType}:"); 

      foreach (var people in objectTypeToListOfThoseObjects[personType]) 
      { 
       Console.WriteLine($"{people.LastName}, {people.FirstName}"); 
      } 
     } 

     Console.ReadLine(); // pause the console 
    } 

Отпечатки это:

All people with derived type ConsoleApplication2.Employee: smith, john All people with derived type ConsoleApplication2.Customer: smoth, jane All people with derived type ConsoleApplication2.Person: smath, jack

+0

Это не имеет отношения к моему вопросу. Я пытаюсь создать класс WPF View, который может представлять мою модель данных определенным образом. В частности, проблема использования разных 'DataTemplate' для разных группировок, например, если я использовал' CollectionView' как 'ItemsSource' для' ListBox'. –

+0

@KyleV. О, мой плохой. Я подумал, что если у вас есть отдельные списки, вы можете отобразить их, как вы хотели. У меня только ограниченный опыт работы с WPF, но у вашего вопроса нет кода View и no xaml, поэтому я решил, что отправной точкой является «группировать людей по типу», что и делает мой простой код. – Quantic

+0

Правильно, у меня нет кода XAML в моем вопросе, потому что это часть, с которой я в тупике. –

1

Вы можете создать три различных DataTemplates с помощью DATATYPE. Он автоматически выберет шаблон для наиболее производного типа. Единственным недостатком является то, что ваши шаблоны будут немного повторяться, поскольку каждый шаблон получает переопределение базовой информации, предоставленной шаблоном Person.

Чтобы сгруппировать данные, у вас есть два подхода:

  1. ObservableCollection из ObservableCollections
  2. CollectionViewSource

Если вы хотите использовать вариант 1, вы можете использовать метод GroupBy LINQ в, например

Persons.GroupBy(_ => _.GetType()) 

, а затем массировать его в ObservableCollection. Однако, если ваша коллекция людей изменяется после загрузки экрана, вам необходимо либо перестроить всю коллекцию, либо подписаться на событие CollectionChanged, и самостоятельно синхронизировать изменения.

Я собираюсь предложить вариант 2, потому что он волшебный и удивительный. Если значение параметра IsLiveGroupingRequested установлено равным true в CollectionViewSource, WPF должен автоматически обрабатывать группировку для вас. Однако для этого я добавил новое свойство для объекта Person: PersonType. (Это будет работать лучше, чем использовать имя типа, если вы в конечном итоге локализовать приложение, хотя!)

<ItemsControl> 
    <ItemsControl.Resources> 
     <CollectionViewSource x:Key="viewSource" Source="{Binding Path=Persons}" IsLiveGroupingRequested="True"> 
      <CollectionViewSource.GroupDescriptions> 
       <PropertyGroupDescription PropertyName="PersonType" /> 
      </CollectionViewSource.GroupDescriptions> 
     </CollectionViewSource> 
     <DataTemplate DataType="{x:Type myNamespace:Person}"> ... </DataTemplate> 
     <DataTemplate DataType="{x:Type myNamespace:Employee}"> ... </DataTemplate> 
     <DataTemplate DataType="{x:Type myNamespace:Customer}"> ... </DataTemplate> 
    </ItemsControl.Resources> 
    <ItemsControl.ItemsSource> 
     <Binding Source="{StaticResource viewSource}" /> 
    </ItemsControl.ItemsSource> 
    <ItemsControl.ItemContainerStyle> 
     <Style TargetType="ContentPresenter"> 
      <Setter Property="Margin" Value="5" /> 
     </Style> 
    </ItemsControl.ItemContainerStyle> 
    <ItemsControl.GroupStyle> 
     <GroupStyle> 
      <GroupStyle.HeaderTemplate> 
       <DataTemplate> 
        <TextBlock FontSize="20" Text="{Binding Name}" /> 
       </DataTemplate> 
      </GroupStyle.HeaderTemplate> 
     </GroupStyle> 
    </ItemsControl.GroupStyle> 
</ItemsControl> 

Обратите внимание на дополнительные пространства имен:

xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
+0

Это почти то, что я хотел, но это просто отсутствует: я хотел, чтобы мой DataTemplate применялся на уровне группы. Например: если бы я хотел показать элемент управления DataGrid для каждой группы с его параметром «ItemSource» для членов группы и иметь заголовки столбцов в зависимости от 'Type', содержащихся в группе. Таким образом, в этом случае «Сотрудники» будут в одной сетке с определенными заголовками и «Клиентами» в другой сетке с разными заголовками. –

Смежные вопросы