Search code examples
c#wpfmvvmtabcontroltabitem

How can I bind WPF TabItem to specific C# class using MVVM?


I want to bind each TabItem of TabControl to specific C# class to use state of class properties for reactions on View. I try to use MVVM and I know that to better to bind collection of objects to TabControl's ItemsSource but in my case classes which I want to bind so different and only few properties of it must be identical. I'm not used collection because customers want to see tab items by groups and I had to change standard style of TabControl and had to create each of Items in XAML and bound to specific element

<TabControl>
    <TabControl.Resources>
        <Style x:Key="TabItemDefaultStyle" TargetType="TabItem">
            <Setter Property="Height" Value="{StaticResource SquareButtonSize}"/>
            <Setter Property="Width" Value="{StaticResource SquareButtonSize}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TabItem">
                        <ControlTemplate.Resources>
                            <Storyboard x:Key="TabItemBorderBackgroundBlinking" >
                                <ColorAnimation Storyboard.TargetName="TabItemBorder" Storyboard.TargetProperty="Background.Color" From="LightBlue" To="Red" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="Forever" />
                            </Storyboard>
                        </ControlTemplate.Resources>
                        <Border Name="TabItemBorder" BorderThickness="1" BorderBrush="Black" CornerRadius="5" Margin="1">
                            <ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Header" Margin="5"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="TabItemBorder" Property="Background" Value="{StaticResource GeneralColor}" />
                            </Trigger>
                            <Trigger Property="IsSelected" Value="False">
                                <Setter TargetName="TabItemBorder" Property="Background" Value="Transparent" />
                            </Trigger>
                            <DataTrigger Binding="{Binding IsAlarm}" Value="True">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard Name="BeginTabItemBorderBackgroundBlinking" Storyboard="{StaticResource TabItemBorderBackgroundBlinking}" />
                                </DataTrigger.EnterActions>
                                <DataTrigger.ExitActions>
                                    <StopStoryboard BeginStoryboardName="BeginTabItemBorderBackgroundBlinking"/>
                                </DataTrigger.ExitActions>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TabControl.Resources>
    <TabControl.Template>
        <ControlTemplate TargetType="TabControl">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="{StaticResource SettingsPanelWidth}"/>
                </Grid.ColumnDefinitions>
                <Border Grid.Column="0" Margin="5" Padding="5" BorderBrush="Black" BorderThickness="1,1,1,1" CornerRadius="5">
                    <Grid IsItemsHost="True" >
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                    </Grid>
                </Border>
                <ContentPresenter Grid.Column="1" ContentSource="SelectedContent" />
            </Grid>
        </ControlTemplate>
    </TabControl.Template>
    <TabItem Grid.Row="0" Style="{StaticResource TabItemDefaultStyle}">
        <vc:MainIndicators />
    </TabItem>
    <TabItem Grid.Row="1" Style="{StaticResource TabItemDefaultStyle}">
        <vc:Dueses />
    </TabItem>
    <TabItem Grid.Row="3" Style="{StaticResource TabItemDefaultStyle}">
        <vc:ChartSettings />
    </TabItem>
    <TabItem Grid.Row="4" Style="{StaticResource TabItemDefaultStyle}">
        <vc:GeneralSettings />
    </TabItem>
</TabControl>

My UI

But I want to bound each of TabItem to object inherited of base class to use its IsAlarm property (you can see it in code above in DataTrigger tag) to make item blink if IsAlarm=true. Looks like I can make it via DataContext but I can't understand how.

UPDATE

As I see I can do something like this (add DataContext to item; all code samples below are parts of code above, except TabItemContext declaration)

<TabItem Grid.Row="0" Style="{StaticResource TabItemDefaultStyle}" DataContext="{Binding TabContext}">
    <vc:MainIndicators />
</TabItem>

And let

public class TabItemContext : BindableBase
{
    public bool IsAlarm { get; set; }
}

public TabItemContext TabContext { get; set; }

But in this case DataTrigger not worked

<DataTrigger Binding="{Binding IsAlarm}" Value="True">
    <DataTrigger.EnterActions>
        <BeginStoryboard Name="BeginTabItemBorderBackgroundBlinking" Storyboard="{StaticResource TabItemBorderBackgroundBlinking}" />
    </DataTrigger.EnterActions>
    <DataTrigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginTabItemBorderBackgroundBlinking"/>
    </DataTrigger.ExitActions>
</DataTrigger>

As I understand it is wrong context for trigger. How to change to right? Maybe need to define datacontext type for template?


Solution

  • I was near the right answer. In the UPDATE of original question if change TabItemContext to

    public class TabItemContext : BindableBase
    {
        private bool _isAlarm;
        public bool IsAlarm
        {
            get => _isAlarm;
            set
            {
                SetProperty(ref _isAlarm, value, nameof(IsAlarm));
            }
        }
    }
    

    all be good. Thanks @BionicCode for help