none
WPF TabControlアクティブ状態のレイアウト設定方法について RRS feed

  • 質問

  • 現在WPFでアプリケーションを開発しております。

    TabControlについて、アクティブ状態のタブと非アクティブ状態のタブで上下に若干の隙間があるため、状態に関わらず共通したレイアウトに変更したいと思います。
    お手数ではありますが、アクティブ状態を検知したレイアウトの設定方法、もしくはTabControlのContainerStyleを指定する方法など、何か参考になる毒キュメンとがありましたら教えていただけると幸いです。(コードでも構いません。)

    現在想定しているデザインは、アクティブ状態のタブのみ背景色を変更する、隙間やボーダーは表示しないフラットデザインに寄せたコントロールにしたいと考えております。

    よろしくお願いいたします。

    2017年3月12日 14:52

回答

  • デザイナへTabControlを配置して、適当にItemSourceを設定します。たとえば以下のような

    <TabControl ItemsSource="{Binding}" DataContext="ABCD" />

    デザイナのデザインでタブコントロールを右クリックして「テンプレートの編集->コピーして編集」を選択するとTabConrolのスタイルがリソースにつくられます。
    その中のTemplateプロパティ対象のセッターで、TabPanelを探してMargin等を変更します。(Tabが上以外の場合はTriggersでHeaderPanelを対象にしたMarginも変更します。
    一端このTabControlに適用されたスタイルを消すか、リソースのスタイルをコメントアウトするか、どこかにコピーしておいて消しておきます。

    再度デザイナでタブコントロールを右クリックして「追加テンプレートの編集->生成されたアイテムコンテナーの編集->コピーして編集」を選択します。(表示されない場合はいったんデザイナを閉じます。)
    その中のTemplateプロパティ対象のセッターで、IsSelected=trueになっているMultiTriggerを探して、Marginを変更するセッターを変更します。必要ならBackgroundを変更するセッターを追加します。
    先の無効にしておいたTabControlのスタイルを再度適用できるように戻します。

    後は好きなようにボーダーなどをいじってみてください。


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

    2017年3月12日 15:50
  • できますがSelectedItemやSelectedIndexを使った方が楽だと思います

    <Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <DockPanel>
            <UniformGrid Columns="2" Rows="1">
                <DockPanel>
                    <StackPanel DockPanel.Dock="Bottom">
                        <CheckBox x:Name="chA" Content="A"/>
                        <CheckBox x:Name="chB" Content="B"/>
                    </StackPanel>
                    <TabControl>
                        <TabItem Header="A" x:Name="a">
                            <TabItem.Style>
                                <Style TargetType="TabItem">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsChecked,ElementName=chA}" Value="true">
                                            <Setter Property="IsSelected" Value="true" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TabItem.Style>
                        </TabItem>
                        <TabItem Header="B" x:Name="b">
                            <TabItem.Style>
                                <Style TargetType="TabItem">
                                    <Style.Triggers>
                                        <MultiDataTrigger>
                                            <MultiDataTrigger.Conditions>
                                                <Condition Binding="{Binding Path=IsChecked,ElementName=chA}" Value="false" />
                                                <Condition Binding="{Binding Path=IsChecked,ElementName=chB}" Value="true" />
                                            </MultiDataTrigger.Conditions>
                                            <Setter Property="IsSelected" Value="true" />
                                        </MultiDataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TabItem.Style>
                        </TabItem>
                    </TabControl>
                </DockPanel>
    
                <TabControl ItemsSource="{Binding Items}" 
                            SelectedItem="{Binding Path=CurrentItem}" >
                    <TabControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Text}"/>
                        </DataTemplate>
                    </TabControl.ItemTemplate>
                </TabControl>
            </UniformGrid>
        </DockPanel>
    </Window> 
    
    Class MainWindow
        Sub New()
            InitializeComponent()
            Me.DataContext = New Model()
        End Sub
    End Class
    
    Class Model
        Implements System.ComponentModel.INotifyPropertyChanged
    
        Public Sub New()
    
            _Items = New System.Collections.ObjectModel.ObservableCollection(Of Item)()
    
            _Items.Add(New Item() With {.Text = "AAA"})
            _Items.Add(New Item() With {.Text = "BBB"})
            _Items.Add(New Item() With {.Text = "CCC"})
    
            Me.CurrentItem = Me.Items(1)
        End Sub
        Public ReadOnly Property Items As System.Collections.ObjectModel.ObservableCollection(Of Item)
            Get
                Return _Items
            End Get
        End Property
        Private _Items As System.Collections.ObjectModel.ObservableCollection(Of Item)
    
        Public Property CurrentItem As Item
            Get
                Return _CurrentItem
            End Get
            Set(value As Item)
                _CurrentItem = value
                OnPropertyChanged("CurrentItem")
            End Set
        End Property
        Private _CurrentItem As Item
    
        Public Event PropertyChanged(sender As Object, e As ComponentModel.PropertyChangedEventArgs) Implements ComponentModel.INotifyPropertyChanged.PropertyChanged
        Protected Sub OnPropertyChanged(ByVal name As String)
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(name))
        End Sub
    
    End Class
    
    Class Item
        Public Property Text As String
    End Class

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

    2017年3月14日 23:00

すべての返信

  • デザイナへTabControlを配置して、適当にItemSourceを設定します。たとえば以下のような

    <TabControl ItemsSource="{Binding}" DataContext="ABCD" />

    デザイナのデザインでタブコントロールを右クリックして「テンプレートの編集->コピーして編集」を選択するとTabConrolのスタイルがリソースにつくられます。
    その中のTemplateプロパティ対象のセッターで、TabPanelを探してMargin等を変更します。(Tabが上以外の場合はTriggersでHeaderPanelを対象にしたMarginも変更します。
    一端このTabControlに適用されたスタイルを消すか、リソースのスタイルをコメントアウトするか、どこかにコピーしておいて消しておきます。

    再度デザイナでタブコントロールを右クリックして「追加テンプレートの編集->生成されたアイテムコンテナーの編集->コピーして編集」を選択します。(表示されない場合はいったんデザイナを閉じます。)
    その中のTemplateプロパティ対象のセッターで、IsSelected=trueになっているMultiTriggerを探して、Marginを変更するセッターを変更します。必要ならBackgroundを変更するセッターを追加します。
    先の無効にしておいたTabControlのスタイルを再度適用できるように戻します。

    後は好きなようにボーダーなどをいじってみてください。


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

    2017年3月12日 15:50
  • ありがとうございます!
    質問とは少しズレるのですが、MultiTriggerという便利なものがあるのを今知りました…かなり活用できそうです。

    ついでで申し訳ないのですが、今TabControlにコレクションをバインドして、動的にタブの内容を追加しているのですが、
    その際いずれのタブも選択されていない状態になっております。
    おそらくIsSelectedをTrueにすると思うのですが、DataTriggerで、動的に選択状態にすることは可能でしょうか?

    2017年3月14日 15:38
  • できますがSelectedItemやSelectedIndexを使った方が楽だと思います

    <Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <DockPanel>
            <UniformGrid Columns="2" Rows="1">
                <DockPanel>
                    <StackPanel DockPanel.Dock="Bottom">
                        <CheckBox x:Name="chA" Content="A"/>
                        <CheckBox x:Name="chB" Content="B"/>
                    </StackPanel>
                    <TabControl>
                        <TabItem Header="A" x:Name="a">
                            <TabItem.Style>
                                <Style TargetType="TabItem">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsChecked,ElementName=chA}" Value="true">
                                            <Setter Property="IsSelected" Value="true" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TabItem.Style>
                        </TabItem>
                        <TabItem Header="B" x:Name="b">
                            <TabItem.Style>
                                <Style TargetType="TabItem">
                                    <Style.Triggers>
                                        <MultiDataTrigger>
                                            <MultiDataTrigger.Conditions>
                                                <Condition Binding="{Binding Path=IsChecked,ElementName=chA}" Value="false" />
                                                <Condition Binding="{Binding Path=IsChecked,ElementName=chB}" Value="true" />
                                            </MultiDataTrigger.Conditions>
                                            <Setter Property="IsSelected" Value="true" />
                                        </MultiDataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TabItem.Style>
                        </TabItem>
                    </TabControl>
                </DockPanel>
    
                <TabControl ItemsSource="{Binding Items}" 
                            SelectedItem="{Binding Path=CurrentItem}" >
                    <TabControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Text}"/>
                        </DataTemplate>
                    </TabControl.ItemTemplate>
                </TabControl>
            </UniformGrid>
        </DockPanel>
    </Window> 
    
    Class MainWindow
        Sub New()
            InitializeComponent()
            Me.DataContext = New Model()
        End Sub
    End Class
    
    Class Model
        Implements System.ComponentModel.INotifyPropertyChanged
    
        Public Sub New()
    
            _Items = New System.Collections.ObjectModel.ObservableCollection(Of Item)()
    
            _Items.Add(New Item() With {.Text = "AAA"})
            _Items.Add(New Item() With {.Text = "BBB"})
            _Items.Add(New Item() With {.Text = "CCC"})
    
            Me.CurrentItem = Me.Items(1)
        End Sub
        Public ReadOnly Property Items As System.Collections.ObjectModel.ObservableCollection(Of Item)
            Get
                Return _Items
            End Get
        End Property
        Private _Items As System.Collections.ObjectModel.ObservableCollection(Of Item)
    
        Public Property CurrentItem As Item
            Get
                Return _CurrentItem
            End Get
            Set(value As Item)
                _CurrentItem = value
                OnPropertyChanged("CurrentItem")
            End Set
        End Property
        Private _CurrentItem As Item
    
        Public Event PropertyChanged(sender As Object, e As ComponentModel.PropertyChangedEventArgs) Implements ComponentModel.INotifyPropertyChanged.PropertyChanged
        Protected Sub OnPropertyChanged(ByVal name As String)
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(name))
        End Sub
    
    End Class
    
    Class Item
        Public Property Text As String
    End Class

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

    2017年3月14日 23:00
  • ありがとうございます!おかげさまで自分が希望していた動作を実現することができました!

    SelectedItem="{Binding Path=CurrentItem}"

    こういう書き方ができるんですね、すごく勉強になりました…

    2017年3月15日 12:54