Как добавить условный шаблон TreeView.Resources в XAML? - PullRequest
0 голосов
/ 28 октября 2019

Я хочу иметь условный XAML, который выбирает этот TreeView.Resources или другой.

Я использую MaterialDesignInXAML инструментарий * Card и внутри него находится TreeView.

Допустим, у меня есть коллекция Фрукты , внутри которой есть еще одна коллекция под названием Деревья .

<TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type domain:Tree}" ItemsSource="{Binding Fruits}">
        // tree information here ...
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type domain:Fruit}">
        // fruit information here ...
    </DataTemplate>
</TreeView.Resources>

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

<TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type domain:Fruit}">
        // fruit information here
    </HierarchicalDataTemplate>
</TreeView.Resources>

Я хочу, чтобы мой вывод выглядел так:

> Apple Tree
    - Apples
> Mango Tree
    - Mangoes
> Watermelon

Редактировать :

Я использовал второе предложение @ BionicCode , которое должно использовать DataTemplateSelector. Я добавил это в свой XAML:

<UserControl DataContext="{Binding ViewModel}">
    <UserControl.Resources>
        <domain:DtmSelector x:Key="DtmSelector">
            <domain:DtmSelector.WithTree>
                <HierarchicalDataTemplate DataType="{x:Type domain:Tree}" ItemsSource="{Binding Fruits}">
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate DataType="{x:Type domain:Fruit}">
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </domain:DtmSelector.WithTree>
            <domain:DtmSelector.WithoutTree>
                <DataTemplate DataType="{x:Type domain:Tree}">
                    <TextBlock Text="{Binding Fruits[0].Name}"/>
                </DataTemplate>
            </domain:DtmSelector.WithoutTree>
        </domain:DtmSelector>
    </UserControl.Resources>

    <TreeView
        ItemsSource="{Binding Trees}"
        ItemTemplateSelector="{StaticResource DtmSelector}"                
    />
</UserControl>

И в моем DtmSelector.cs :

public class DtmSelector: DataTemplateSelector
{
    public DataTemplate WithTrees { get; set; }
    public DataTemplate WithoutTrees { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        Tree key = item as Tree;
        if (key.Fruits.Count() == 0)
        {
            return WithTrees;
        }
        else
        {
            if (key.Fruits.Select(c=>c.Name).Contains(key.Name))
            {
                return WithTrees;
            }
            else
            {
                return WithoutTrees;
            }
        }
    }
}

Моя концепция ViewModel выглядит следующим образом:

foreach (var item in fruits)
{
    if ( item.Tree == null)
    {
        if (!Trees.Select(c => c.Name).Contains(item.Name))
        {
            Trees.Add(new Tree(item.Name));
        }

        foreach (var tree in Trees.ToList())
        {
            if (tree.Name == item.Name)
            {
                tree.Fruits.Add(item);
            }
        }
    }
    else
    {
        if (!Trees.Select(c => c.Name).Contains(item.Tree.Name))
        {
            Trees.Add(item.Tree);
        }

        foreach (var tree in Trees.ToList())
        {
            if (tree.Name == item.Tree.Name)
            {
                tree.Fruits.Add(item);
            }
        }
    }
}

ПРОБЛЕМА

Этот набор кодов сначала проверит Trees без добавления Fruits. Таким образом, эта строка if (key.Fruits.Count() == 0) всегда будет возвращать true. Есть ли что-то, что я могу сделать? Я что-то упустил?

Я нашел ответ из другой ветки , который мог бы помочь, однако я не знаю, как это будет работать в ViewModel, как я могу выбрать определенныйсеттер по переплету Property.

1 Ответ

3 голосов
/ 29 октября 2019

У вас есть несколько вариантов здесь. Я покажу вам два предложения на основе структуры структуры данных (модели).

Первое предложение

Используйте один тип данных для полной древовидной структуры:

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
  ViewModel()
  {
    this.Fruits = new ObservableCollection<Fruit>()
    {
      new Fruit("Apple Tree", new ObservableCollection<Fruit>() {new Fruit("Apples")}),
      new Fruit("Mango Tree", new ObservableCollection<Fruit>() {new Fruit("Mangos")}),
      new Fruit("Watermelon")
    }

  ObservableCollection<Fruit> fruits;
  ObservableCollection<Fruit> Fruits
  {
    get => this.fruits;
    set
    {
      this.fruits = value;
      OnPropertyChanged();
    }
  }

  // INotifyPropertyChanged implementation here...
}

Fruit.cs (модель данных)

class Fruit : INotifyPropertyChanged
{
  Fruit(string name) : this(name, new List<Fruit>())
  {
  }

  Fruit(string name, IEnumerable<Fruit> children)
  {
    this.Name = name;
    this.Children = new ObservableCollection<Fruit>(children ?? new List<Fruit>());
  }

  string name;
  string Name 
  {
    get => this.name;
    set
    {
      this.name = value;
      OnPropertyChanged();
    }
  }

  ObservableCollection<Fruit> children;
  ObservableCollection<Fruit> Children 
  {
    get => this.children;
    set
    {
      this.children = value;
      OnPropertyChanged();
    }
  }

  // INotifyPropertyChanged implementation here...
}

TreeView XAML

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <TreeView ItemsSource={Binding Fruits}>
    <TreeView.ItemTemplate>
      <HierarchicalDataTemplate DataType="{x:Type domain:Fruit}" ItemsSource="{Binding Children}">
        <HierarchicalDataTemplate.ItemTemplate>
          <DataTemplate DataType="{x:Type domain:Fruit}">
            <TextBlock Text="{Binding Name}" />
          </DataTemplate>
        </HierarchicalDataTemplate.ItemTemplate>

        <TextBlock Text="{Binding Name}" />
      </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
  </TreeView>
</Window>

Второе предложение

Если вам нужны разные типы для разных ролей (узлов), вы должны использовать DataTemplateSelector:

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
  ViewModel()
  {
    this.Fruits = new ObservableCollection<INode>()
    {
      new Category("Apple Tree", new ObservableCollection<INode>() {new Fruit("Apples")}),
      new Category("Mango Tree", new ObservableCollection<INode>() {new Fruit("Mangos")}),
      new Fruit("Watermelon")
    }

  ObservableCollection<INode> fruits;
  ObservableCollection<INode> Fruits
  {
    get => this.fruits;
    set
    {
      this.fruits = value;
      OnPropertyChanged();
    }
  }

  // INotifyPropertyChanged implementation here...
}

INode.cs (модель данных)

// Common base type for the node collection and all tree nodes
interface INode: INotifyPropertyChanged
{
  string Name {get; set; }

  ObservableCollection<INode> Children { get; set; }
}

Category.cs (модель данных)

class Category : INode
{
  Category(string name) : this(name, new List<INode>())
  {
  }

  Category(string name, IEnumerable<INode> children) 
  {
    this.Name = name;
    this.Children = new ObservableCollection<INode>(children ?? new List<INode>());
  }

  string name;
  string Name 
  {
    get => this.name;
    set
    {
      this.name = value;
      OnPropertyChanged();
    }
  }

  ObservableCollection<INode> children;
  ObservableCollection<INode> Children 
  {
    get => this.children;
    set
    {
      this.children = value;
      OnPropertyChanged();
    }
  }

  // INotifyPropertyChanged implementation here...
}

Fruit.cs (модель данных)

class Fruit : INode
{
  Fruit(string name) : this(name, new List<INode>())
  {
  }

  Fruit(string name, IEnumerable<INode> children)
  {
    this.Name = name;
    this.Children = new ObservableCollection<INode>(children ?? new List<INode>());
  }

  string name;
  string Name 
  {
    get => this.name;
    set
    {
      this.name = value;
      OnPropertyChanged();
    }
  }

  ObservableCollection<INode> children;
  ObservableCollection<INode> Children 
  {
    get => this.children;
    set
    {
      this.children = value;
      OnPropertyChanged();
    }
  }

  // INotifyPropertyChanged implementation here...
}

NodeTemplateSelector.cs

class NodeTemplateSelector : DataTemplateSelector
{
  public DataTemlplate CategoryNodeTemplate { get; set; }
  public DataTemlplate FruitNodeTemplate { get; set; }

  public override DataTemplate SelectTemplate(object item, DependencyObject container)
  {
    switch (item)
    {
        case Category _:
           return this.CategoryNodeTemplate;
        case Fruit _:
           return this.FruitNodeTemplate;
        default:
           return this.FruitNodeTemplate;
    }
  }
}

TreeView XAML

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>
  <Window.Resources>
    <NodeTemplateSelector x:Key="NodeTemplateSelector">
      <NodeTemplateSelector.CategoryNodeTemplate>
        <HierarchicalDataTemplate DataType="{x:Type Category}" ItemsSource="{Binding Children}">
          <TextBlock Text="{Binding Name}" />
        </HierarchicalDataTemplate>
      </NodeTemplateSelector.CategoryNodeTemplate>
      <NodeTemplateSelector.FruitNodeTemplate>
        <DataTemplate DataType="{x:Type Fruit}">
          <TextBlock Text="{Binding Name}" />
        </DataTemplate>
      </NodeTemplateSelector.FruitNodeTemplate>
    </NodeTemplateSelector>
  </Window.Resources>

  <TreeView ItemsSource="{Binding Fruits}" 
            ItemTemplateSelector="{StaticResource NodeTemplateSelector}"  />
</Window>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...