89713


開発環境は、Windows10、VS2022 .Net6(WPF) Prism(Unity) MVVM です。  
（Prismはテンプレートパックにて導入しています）

TabControlの動的に生成される各タブに、それぞれに応じたViewを配置したいのですが、その方法が分かりません。

### 前提として

  * MainView に TabControl を配置
  * 他、View(UserControl)として、AView,BView,CView,DView を作成
  * AView には、「BView 追加」「CView 追加」「DView 追加」ボタンを配置
  * BView,CView,DView はそれぞれ独立しており、配置しているコントロールはバラバラ  
（独立と書くと語弊あるかもしれませんが。他のViewとの関連は無いという意味です）

### やりたいこと

  * MainView の TabControl の最初のタブにAViewを配置  
（実行した最初はこの状態で。タブ1つのみ、AViewだけの状態です）

  * MainView の TabControl のAView の各「追加」ボタンを押したら、押した順にタブを追加し、  
追加したタブには、該当するView（例えば、CView追加ボタンを押したら、追加されたタブにはCView）を配置

  * 例えば。最初のAViewのみの状態から。DView追加→DView追加→BView追加としたら、  
2つ目、3つ目のタブはDView、4つ目のタブはBView としたい。

  * 条件として、タブの数は毎回不定とする。

という事をしたいのですが、Viewが複数ある段階でどの様に実装すれば良いか分からず、途方に暮れてしまいました。

* * *

例えば、全てのタブが同じBViewのみだったらViewModelにて、

[code]

    ObservableCollection<TabItemData> TabItems
    
[/code]

の様にすれば出来るのは理解できたのですが、これが複数かつ、不定の場合にどうすれば良いのでしょうか。

一つの案として考えたのは、乱暴な方法なのですがAView～DViewまでのViewModelを1つにまとめたViewModelを実装し、それをObservableCollectionし、必要なプロパティだけ使用する、というものです。

もっとスマートな方法があると思い、ご存じの方がいらっしゃいましたらご教授をお願い致したく、よろしくお願いいたします。


CC BY-SA 4.0
2022-06-08T16:33:18.633
89296
2022-07-01T15:00:14.570
2022-06-08T23:58:10.923
3060
52988
question
c# wpf prism
C# WPF TabControl 動的に生成されるタブ毎に違うView（UserControl）を設定したい


Messenger パターンを使うという方法もあります。  
ViewModel から View を操作したいときに使用するパターンで、メッセージダイアログを使いたいときによく利用されます。  
Messenger パターンの詳細は MVVM Messenger あたりで検索すれば得られると思います。

タブを追加する機能は MainView(のコードビハインド)に実装し、 各 ViewModel から Messenger を介してタブ追加を
MainView に要求するといった実装になるかと思います。


CC BY-SA 4.0
2022-06-23T10:27:12.157
89553
2022-06-23T10:27:12.157
14817
89296
answer


TabItemDataを継承した各クラスごとに[DataTemplate](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.datatemplate?view=windowsdesktop-6.0)を定義してはいかがでしょうか。

例えば以下のようなViewModelを用意します。  
各ViewModelに[ObservableCollection](https://docs.microsoft.com/ja-
jp/dotnet/api/system.collections.objectmodel.observablecollection-1?view=net-6.0)を追加する[ICommand](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.input.icommand?view=net-6.0)を持たせています。

[code]

    public abstract class TabItemData : BindableBase
    {
        public abstract string Header { get; set; } 
    }
    
    public class AViewModel : TabItemData
    {
        public override string Header { get; set; } = "AView";
        public ICommand? CreateBView { get; set; }
        public ICommand? CreateCView { get; set; }
    }
    
    public class BViewModel : TabItemData
    {
        public override string Header { get; set; } = "BView";
        public ICommand? CreateAView { get; set; }
        public ICommand? CreateCView { get; set; }
    }
    
    public class CViewModel : TabItemData
    {
        public override string Header { get; set; } = "CView";
        public ICommand? CreateAView { get; set; }
        public ICommand? CreateBView { get; set; }
    }
    
    public class MainViewModel : BindableBase
    {
        public MainViewModel() { CreateAViewModel(); }
    
        public ObservableCollection<TabItemData> TabItems { get; set; } = new ObservableCollection<TabItemData>();
    
        private void CreateAViewModel()
        {
            TabItems.Add(new AViewModel()
            {
                CreateBView = new DelegateCommand(CreateBViewModel),
                CreateCView = new DelegateCommand(CreateCViewModel),
            });
        }
    
        private void CreateBViewModel()
        {
            TabItems.Add(new BViewModel()
            {
                CreateAView = new DelegateCommand(CreateAViewModel),
                CreateCView = new DelegateCommand(CreateCViewModel),
            });
        }
    
        private void CreateCViewModel()
        {
            TabItems.Add(new CViewModel()
            {
                CreateAView = new DelegateCommand(CreateAViewModel),
                CreateBView = new DelegateCommand(CreateBViewModel),
            });
        }
    }
    
[/code]

これを[TabControl.ItemsSource](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.controls.itemscontrol.itemssource?view=windowsdesktop-6.0)に設定し、各クラスの[DataTemplate](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.datatemplate?view=windowsdesktop-6.0)を用意してやれば、クラスに応じたViewを使用してくれるようになります。  
※[UserControl](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.controls.usercontrol?view=windowsdesktop-6.0)であるAView、BView、CViewの内容については省略します。[ICommand](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.input.icommand?view=net-6.0)を実行するボタンを配置しておけば良いです。

[code]

    <Window 
        x:Class="Sample.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Sample"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance Type=local:MainViewModel}"    
        Title="MainView"
        Height="400" 
        Width="400">
        <Window.Resources>
            <DataTemplate DataType="{x:Type local:AViewModel}">
                <local:AView/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:BViewModel}">
                <local:BView/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:CViewModel}">
                <local:CView/>
            </DataTemplate>
            <Style TargetType="TabItem">
                <d:Style.DataContext>
                    <x:Type Type="local:TabItemData"/>
                </d:Style.DataContext>
                <Setter Property="Header" Value="{Binding Header}"/>
            </Style>
        </Window.Resources>
        <Grid>
            <TabControl ItemsSource="{Binding TabItems}"/>
        </Grid>
    </Window>
    
[/code]

型以外の条件で[DataTemplate](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.datatemplate?view=windowsdesktop-6.0)を切り替えたいのであれば、[TabControl.ItemTemplateSelector](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.controls.itemscontrol.itemtemplateselector?view=windowsdesktop-6.0)に自作の[DataTemplateSelector](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.controls.datatemplateselector?view=windowsdesktop-6.0)を設定してみてください。  
例えばViewModelのプロパティの値によって[DataTemplate](https://docs.microsoft.com/ja-
jp/dotnet/api/system.windows.datatemplate?view=windowsdesktop-6.0)を切り替えることも可能となります。


CC BY-SA 4.0
2022-07-01T15:00:14.570
89713
2022-07-01T15:00:14.570
70
89296
answer