none
TabControl.ContentTemplate内のコントロールについて RRS feed

  • 質問

  • 現在VS2013 C# でWPFアプリケーションを作成しています。

    下記のようなコードの場合、TabControl.ContentTemplate内に存在する
    TextBlockや、TextBox、ListBoxは各TabItemごとに生成されるのではなく、
    同一のコントロールを使用している認識で相違ないでしょうか?

    一応自分で確認をしたのですが、いまいち自信がないので質問させていただきました。

    間違っているのであれば、指摘していただけると幸いです。
    宜しくお願いいたします。

    コードビハインド

    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows;
    
    namespace TabTemplateTest
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                this.InitializeComponent();
            }
        }
    
        public class MainWindowViewModel
        {
            public ObservableCollection<TabItem> TabItems { get; set; }
    
            public MainWindowViewModel()
            {
                this.TabItems = new ObservableCollection<TabItem>
                {
                    new TabItem
                    {
                        TabName = "001",
                        ContentItem = new ContentItem
                        {
                            ContentId = 1,
                            Message = "Message Content1",
                            ListItems = new List<string> { "001-1", "001-2", "001-3" }
                        },
                    },
                    new TabItem
                    {
                        TabName = "002",
                        ContentItem = new ContentItem
                        {
                            ContentId = 2,
                            Message = "Message Content2",
                            ListItems = new List<string> { "002-1" }
                        },
                    },
                    new TabItem
                    {
                        TabName = "003",
                        ContentItem = new ContentItem
                        {
                            ContentId = 3,
                            Message = "Message Content3",
                            ListItems = new List<string> { "003-1", "003-2", "003-3", "003-4", "003-5" }
                        },
                    }
                };
            }
        }
    
        // 各タブアイテム
        public class TabItem
        {
            public string TabName { get; set; }
    
            public ContentItem ContentItem { get; set; }
        }
    
        // タブのコンテツ内に必要な情報
        public class ContentItem
        {
            public int ContentId { get; set; }
    
            public string Message { get; set; }
    
            public List<string> ListItems { get; set; }
        }
    }
    

    xaml

    <Window x:Class="TabTemplateTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:TabTemplateTest"
            Title="MainWindow"
            Width="525"
            Height="350">
        <Window.DataContext>
            <local:MainWindowViewModel />
        </Window.DataContext>
        <Grid>
            <TabControl ItemsSource="{Binding Path=TabItems}">
                <TabControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=TabName}" />
                    </DataTemplate>
                </TabControl.ItemTemplate>
                <TabControl.ContentTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="10" />
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="10" />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <StackPanel Grid.Row="0">
                                <TextBlock Text="ID" />
                                <TextBox Text="{Binding Path=ContentItem.ContentId}" />
                            </StackPanel>
                            <StackPanel Grid.Row="2">
                                <TextBlock Text="Message" />
                                <TextBox Text="{Binding Path=ContentItem.Message}" />
                            </StackPanel>
                            <ListBox Grid.Row="4" ItemsSource="{Binding Path=ContentItem.ListItems}" />
                        </Grid>
                    </DataTemplate>
                </TabControl.ContentTemplate>
            </TabControl>
        </Grid>
    </Window>
    

    2015年12月2日 10:07

回答

  • こんにちは。

    以下で確認してみました。対象インスタンスへ設定した色は引き継がれイベントもサブスクライブされたままです。
    同一インスタンスのようですね。

    <Window x:Class="WpfApplication1.MainWindow"
            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"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <x:Array x:Key="hogeList" Type="sys:String">
                <sys:String>AAA</sys:String>
                <sys:String>BBB</sys:String>
                <sys:String>CCC</sys:String>
            </x:Array>
        </Window.Resources>
        <TabControl ItemsSource="{StaticResource hogeList}">
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <TextBox Margin="10" x:Name="txtHoge" />
                        <Button Content="ppp" Click="Button_Click_1" />
                    </StackPanel>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Window>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            var txt = ((StackPanel)((Button)sender).Parent).FindName("txtHoge") as TextBox;
            txt.Background = new SolidColorBrush(Colors.Red);
            txt.TextChanged += txt_TextChanged;
        }
    
        void txt_TextChanged(object sender, TextChangedEventArgs e)
        {
            return;
        }
    }
    #仮想化の関係で使いまわしされている感じなんですかね?
    2015年12月2日 11:07
    モデレータ
  • TabControlがやってることを別の方法で示すと

    <DockPanel>
        <ListBox ItemsSource="{Binding Path=TabItems}" x:Name="lst"
                    DockPanel.Dock="Top">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Black" BorderThickness="1,1,1,0"  >
                        <TextBlock Text="{Binding Path=TabName}" Margin="5" />
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ListBox>
        <ContentPresenter Content="{Binding Path=SelectedItem,ElementName=lst}" >
            <ContentPresenter.ContentTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="10" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="10" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <StackPanel Grid.Row="0">
                            <TextBlock Text="ID" />
                            <TextBox Text="{Binding Path=ContentItem.ContentId}" />
                        </StackPanel>
                        <StackPanel Grid.Row="2">
                            <TextBlock Text="Message" />
                            <TextBox Text="{Binding Path=ContentItem.Message}" />
                        </StackPanel>
                        <ListBox Grid.Row="4" ItemsSource="{Binding Path=ContentItem.ListItems}" />
                    </Grid>
                </DataTemplate>
            </ContentPresenter.ContentTemplate>
        </ContentPresenter>
    </DockPanel>

    と同じことになります。
    TabItemはItemsControlから継承していますが、Tab(つまみの部分)が複数生成されるためにあります。
    そのタブで選択されたコンテンツがContentPresenterで表示されます。

    ContentPresenterは同じテンプレートが適用される場合要素を作り直しません。(作り直すコストが無駄なので)
    流れとしては以下になります。

    1. Contentプロパティが変化した。
    2. ContentTemplateが指定されている場合はそのTemplateをえらび、指定されていない場合はContentに応じたTempleteを探す。
    3. Templateが前と違う場合は新しいTemplateから要素を生成する。
    4. 生成された要素にデータ(Content)を適用する。

    以上の動作により、TabControl.ContentTemplateにテンプレートが指定されている場合は要素が作り直されないので同一のコントロールが使用されることになります。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク れいじ 2015年12月3日 2:35
    2015年12月2日 11:14

すべての返信

  • こんにちは。

    以下で確認してみました。対象インスタンスへ設定した色は引き継がれイベントもサブスクライブされたままです。
    同一インスタンスのようですね。

    <Window x:Class="WpfApplication1.MainWindow"
            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"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <x:Array x:Key="hogeList" Type="sys:String">
                <sys:String>AAA</sys:String>
                <sys:String>BBB</sys:String>
                <sys:String>CCC</sys:String>
            </x:Array>
        </Window.Resources>
        <TabControl ItemsSource="{StaticResource hogeList}">
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <TextBox Margin="10" x:Name="txtHoge" />
                        <Button Content="ppp" Click="Button_Click_1" />
                    </StackPanel>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Window>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            var txt = ((StackPanel)((Button)sender).Parent).FindName("txtHoge") as TextBox;
            txt.Background = new SolidColorBrush(Colors.Red);
            txt.TextChanged += txt_TextChanged;
        }
    
        void txt_TextChanged(object sender, TextChangedEventArgs e)
        {
            return;
        }
    }
    #仮想化の関係で使いまわしされている感じなんですかね?
    2015年12月2日 11:07
    モデレータ
  • TabControlがやってることを別の方法で示すと

    <DockPanel>
        <ListBox ItemsSource="{Binding Path=TabItems}" x:Name="lst"
                    DockPanel.Dock="Top">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Black" BorderThickness="1,1,1,0"  >
                        <TextBlock Text="{Binding Path=TabName}" Margin="5" />
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ListBox>
        <ContentPresenter Content="{Binding Path=SelectedItem,ElementName=lst}" >
            <ContentPresenter.ContentTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="10" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="10" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <StackPanel Grid.Row="0">
                            <TextBlock Text="ID" />
                            <TextBox Text="{Binding Path=ContentItem.ContentId}" />
                        </StackPanel>
                        <StackPanel Grid.Row="2">
                            <TextBlock Text="Message" />
                            <TextBox Text="{Binding Path=ContentItem.Message}" />
                        </StackPanel>
                        <ListBox Grid.Row="4" ItemsSource="{Binding Path=ContentItem.ListItems}" />
                    </Grid>
                </DataTemplate>
            </ContentPresenter.ContentTemplate>
        </ContentPresenter>
    </DockPanel>

    と同じことになります。
    TabItemはItemsControlから継承していますが、Tab(つまみの部分)が複数生成されるためにあります。
    そのタブで選択されたコンテンツがContentPresenterで表示されます。

    ContentPresenterは同じテンプレートが適用される場合要素を作り直しません。(作り直すコストが無駄なので)
    流れとしては以下になります。

    1. Contentプロパティが変化した。
    2. ContentTemplateが指定されている場合はそのTemplateをえらび、指定されていない場合はContentに応じたTempleteを探す。
    3. Templateが前と違う場合は新しいTemplateから要素を生成する。
    4. 生成された要素にデータ(Content)を適用する。

    以上の動作により、TabControl.ContentTemplateにテンプレートが指定されている場合は要素が作り直されないので同一のコントロールが使用されることになります。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク れいじ 2015年12月3日 2:35
    2015年12月2日 11:14
  • >Tak1wa さん

    回答ありがとうございます。

    これで安心できました!

    2015年12月3日 2:40
  • > gekka さん

    回答ありがとうございます。

    また、細かな説明ありがとうございます。勉強になります。

    ContentPresenterですか。今まで意識したことがなかったのでもう少し調べてみたいと思います。

    2015年12月3日 2:45