WPF ComboBoxにTreeViewを埋め込む実装(MVVM)

WPF ComboBoxにTreeViewを埋め込む実装(MVVM)

プロジェクトの要件により、ComboBoxコントロール内にツリー構造が必要

最終更新 2022/11/01 21:31
hlpinghcg
読了目安 4 分
カテゴリ
WPF
テーマ
WPF MVVMフレームワーク Prismシリーズ
タグ
.NET C# WPF MVVM

本記事は転載です。

著者:hlpinghcg

原文タイトル:WPF ComboBox に TreeView を埋め込む実装(MVVM)

原文リンク:https://blog.csdn.net/qq_28149763/article/details/126539635

はじめに

プロジェクトの都合上、ComboBox コントロール内にツリー構造を持たせる必要がありました。ネット上にも複雑なものや簡単な実装例が多くありますが、自身の実装中にいくつか問題に直面したため、ここで自分の実装方法を共有します。問題があれば、お気軽にDMでご連絡ください。

まず、参考にしたのはこちらの方の実装です:WPF 之 Treeview 实现 MVVM 双向绑定

余談はさておき、本題に入ります…

Xaml ファイル

<StackPanel Orientation="Vertical">
  <ComboBox Name="com" SelectedIndex="{Binding ComboSelected}">
    <i:Interaction.Triggers>
      <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction
          Command="{Binding SelectionChangedCommand}"
          CommandParameter="{Binding ElementName=com,Path=SelectedItem}"
        />
      </i:EventTrigger>
    </i:Interaction.Triggers>
    <ComboBoxItem Content="{Binding ShowName}" Visibility="Collapsed" />
    <ComboBoxItem FocusVisualStyle="{x:Null}">
      <ItemsControl>
        <TreeView
          x:Name="treeView"
          ItemsSource="{Binding TypeList}"
          Height="200"
          Width="{Binding ElementName=com, Path=ActualWidth}"
        >
          <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
              <i:InvokeCommandAction
                Command="{Binding SelectItemChangeCommand}"
                CommandParameter="{Binding ElementName=treeView,Path=SelectedItem}"
              />
            </i:EventTrigger>
          </i:Interaction.Triggers>
          <TreeView.Resources>
            <HierarchicalDataTemplate
              DataType="{x:Type m:TypeTreeModel}"
              ItemsSource="{Binding ChildList}"
            >
              <StackPanel Orientation="Horizontal">
                <!--<Image Source="/images/OIP-C.jpg" Width="15" Height="15"/>-->
                <TextBlock Text="{Binding Name}" Margin="3,2" />
                <TextBlock Text=" [" Foreground="Blue" />
                <TextBlock Text="{Binding ChildList.Count}" Foreground="Blue" />
                <TextBlock Text="]" Foreground="Blue" />
              </StackPanel>
              <!--<TextBlock Text="{Binding Name}" Margin="3,2"/>-->
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type m:TypeModel}">
              <StackPanel Orientation="Horizontal">
                <!--<Image Source="/images/OIP-D.jpg" Width="15" Height="15"/>-->
                <!--<TextBlock Text="{Binding Name}" ToolTip="{Binding Id}" Margin="3,2"/>-->
                <TextBlock Text="{Binding Name}" Margin="3,2" />
                <TextBlock Text=" (" Foreground="Green" />
                <TextBlock Text="{Binding Id}" Foreground="Green" />
                <TextBlock Text=" )" Foreground="Green" />
              </StackPanel>
              <!--<TextBlock Text="{Binding Name}" ToolTip="{Binding Id}" Margin="3,2"></TextBlock>-->
            </DataTemplate>
          </TreeView.Resources>
        </TreeView>
      </ItemsControl>
    </ComboBoxItem>
  </ComboBox>
  <!--<TreeView x:Name="treeView" ItemsSource="{Binding TypeList}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <i:InvokeCommandAction Command="{Binding SelectItemChangeCommand}"
                          CommandParameter="{Binding ElementName=treeView,Path=SelectedItem}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type m:TypeTreeModel}" ItemsSource="{Binding ChildList}">
                <TextBlock Text="{Binding Name}" Margin="3,2"/>
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type m:TypeModel}">
                <TextBlock Text="{Binding Name}" ToolTip="{Binding Id}" Margin="3,2"></TextBlock>
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>-->
</StackPanel>

注意すべき点は、イベントを直接バインドする方法から、Command を介したバインドに切り替えるところです:

  • GuNet パッケージを追加(Interactivity を検索)
  • XAML ファイルに名前空間を導入:xmlns:i=“clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”
  • 以下のコードを追加
<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
                  CommandParameter="{Binding ElementName=com,Path=SelectedItem}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>
  • MVVM ライブラリを参照。こちらを参考に:WPF MVVM 通用封装库
  • バックエンドロジック(ViewModel)におけるコマンドの宣言と使用
SelectItemChangeCommand = new RelayCommand<TypeModel>(onSelectItemChange);
SelectionChangedCommand = new RelayCommand(onSelectionChanged);
  public RelayCommand<TypeModel> SelectItemChangeCommand { get; set; }
  public RelayCommand SelectionChangedCommand { get; set; }

バックエンドロジック(ViewModel)

public class ViewModel:ViewModelBase
{

    public ObservableCollection<TypeTreeModel> TypeList { get; set; } = new ObservableCollection<TypeTreeModel>();

    public ViewModel()
    {
        TypeList = new ObservableCollection<TypeTreeModel>(GetData());

        SelectItemChangeCommand = new RelayCommand<TypeModel>(onSelectItemChange);
        SelectionChangedCommand = new RelayCommand(onSelectionChanged);
    }

    public RelayCommand<TypeModel> SelectItemChangeCommand { get; set; }
    public RelayCommand SelectionChangedCommand { get; set; }

    private TypeModel selectItem;
    public TypeModel SelectItem
    {
        get { return selectItem; }
        set
        {
            selectItem = value;
            RaisePropertyChanged(() => SelectItem);
        }
    }

    private string showName;
    public string ShowName
    {
        get { return showName; }
        set
        {
            showName = value;
            RaisePropertyChanged(() => ShowName);
        }
    }


    private int comboSelected;
    public int ComboSelected
    {
        get { return comboSelected; }
        set
        {
            comboSelected = value;
            RaisePropertyChanged(() => ComboSelected);
        }
    }


    private List<TypeTreeModel> GetData()
    {
        List<TypeTreeModel> typeTrees = new List<TypeTreeModel>()
        {
            new TypeTreeModel()
            {
                Id = 1,
                Name = "手机",
                ChildList = new ObservableCollection<TypeTreeModel>()
                {
                    new TypeTreeModel(){ Id=2,Name="苹果" },
                    new TypeTreeModel(){ Id=3,Name="华为",
                        ChildList = new ObservableCollection<TypeTreeModel>()
                        {
                            new TypeTreeModel(){Id=4,Name="荣耀" },
                        }},
                    new TypeTreeModel(){ Id=5,Name="小米",
                        ChildList = new ObservableCollection<TypeTreeModel>()
                        {
                            new TypeTreeModel(){Id=6,Name="红米" }
                        }}
                }
            },
            new TypeTreeModel()
            {
                Id=7,
                Name="笔记本",
                ChildList = new ObservableCollection<TypeTreeModel>()
                {
                    new TypeTreeModel(){Id=8,Name="联想"}
                }
            },
            new TypeTreeModel()
            {
                Id=9,
                Name="耳机"
            }
        };
        return typeTrees;
    }

    private void onSelectItemChange(TypeModel type)
    {
        SelectItem=type;
        ShowName = SelectItem.Name;

    }

    private void onSelectionChanged()
    {
        ComboSelected = 0;
    }
}

データクラス

public class TypeTreeModel: TypeModel
{
    public ObservableCollection<TypeTreeModel> ChildList { get; set; }
        = new ObservableCollection<TypeTreeModel>();
}

public class TypeModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsSelected { get; set; }
}

最終結果

サイト管理者による上記コードのテスト

サイト管理者は上記コードを試し、正常に動作することを確認しました。完全なコードはGitHubで管理しています。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2024/01/25

C# WPFにおけるFluentValidationの応用

この記事では、C# WPFプロジェクトでFluentValidationを使用してプロパティ検証を行う方法と、MVVMパターンを通じてこの機能を実装する方法について詳しく説明します。

続きを読む