Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - Create reusable style with variables/parameters

Tags:

styles

wpf

I have an WPF application which contains several TabItems. Each TabItem is different from each other, with a text and an icon.

This is how the TabItem´s style is defined:

<TabItem.Style>
                    <Style TargetType="TabItem">
                        <Setter Property="Header">
                            <Setter.Value>
                                <StackPanel Orientation="Horizontal">
                                    <Image Margin="10,0,0,0" Height="40" Width="40" Source="Images/IconLeafGrey.png"/>
                                    <TextBlock Text="This is the heading" VerticalAlignment="Center" Style="{StaticResource Heading2}" Margin="10,0,0,0"/>
                                </StackPanel>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type TabItem}">
                                    <Grid>
                                        <Border Name="Border" BorderBrush="Black" BorderThickness="1,1,1,1" CornerRadius="6,6,0,0" >
                                            <Border.Background>
                                                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                                    <GradientStop Color="#f5f7f8" Offset="0.0" />
                                                    <GradientStop Color="#c5d0dd" Offset="1.0" />
                                                </LinearGradientBrush>
                                            </Border.Background>
                                            <ContentPresenter Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Left" ContentSource="Header" Margin="5,2,0,2"/>
                                        </Border>
                                    </Grid>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsSelected" Value="True">
                                            <Setter Property="Background" TargetName="Border">
                                                <Setter.Value>
                                                    <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                                        <LinearGradientBrush.GradientStops>
                                                            <GradientStop Color="#ebedee" Offset="0.0" />
                                                            <GradientStop Color="#88a2bd" Offset="1.0" />
                                                        </LinearGradientBrush.GradientStops>
                                                    </LinearGradientBrush>
                                                </Setter.Value>
                                            </Setter>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Header">
                                    <Setter.Value>
                                        <StackPanel Orientation="Horizontal">
                                            <Image Margin="10,0,0,0" Height="40" Width="40" Source="Images/IconLeaf.png"/>
                                            <TextBlock Text="This is the heading" VerticalAlignment="Center" Style="{StaticResource Heading2}" Margin="10,0,0,0"/>
                                        </StackPanel>
                                    </Setter.Value>
                                </Setter>

                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </TabItem.Style>

So instead of writing this XAML markup for each TabItem, I would like to define this style once in my ResourceDictionary, but with the Icon and the Text property as optional parameters. So I afterwards could define a TabItem like this:

<TabItem Width="250" Height="60" Style="{StaticResource CustomTabItem}" >

I´ve read that it isnt directly possible for a style to accept parameters like this, but it should be posible with some sort of binding. I havent found out exactly how though, so I really hope you can help.

Kind regards.

like image 495
Farsen Avatar asked Oct 16 '13 08:10

Farsen


2 Answers

One way of doing it would be, if you have fixed number of TabItems and you dont want to generate the TabItems through ItemsSource Binding on TabControl. Then in your TabItem.Style you can use Resource keys to get the Text and Source like:

    <StackPanel Orientation="Horizontal">
         <Image Margin="10,0,0,0" Height="40" Width="40" Source="{DynamicResource Image1}"/>
         <TextBlock Text="{DynamicResource Header}" VerticalAlignment="Center" Style="{StaticResource Heading2}" Margin="10,0,0,0"/>
     </StackPanel>

and for your TabItems you can define these resources:

     <TabItem Width="250" Height="60" Style="{StaticResource CustomTabItem}">
         <TabItem.Resources>
               <System:String x:Key="Header">TabItem1</System:String>
               <System:String x:Key="Image1">image/1.png</System:String>
               <System:String x:Key="Image2">image/2.png</System:String>
          </TabItem.Resources>
      </TabItem>
       <TabItem Width="250" Height="60" Style="{StaticResource CustomTabItem}">
         <TabItem.Resources>
               <System:String x:Key="Header">TabItem2</System:String>
               <System:String x:Key="Image1">image/3.png</System:String>
               <System:String x:Key="Image2">image/4.png</System:String>
          </TabItem.Resources>
      </TabItem>

Also you will need to update your Style to set the HeaderTemplate instead of Header

     <Style x:Key="CustomTabItem" TargetType="TabItem">
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image x:Name="HeaderImage" Margin="10,0,0,0" Height="40" Width="40" Source="Images/IconLeafGrey.png"/>
                        <TextBlock Text="{DynamicResource Header}" VerticalAlignment="Center"  Margin="10,0,0,0"/>
                    </StackPanel>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" Value="true">
                            <Setter TargetName="HeaderImage" Property="Source" Value="Images/IconLeaf.png"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>

            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabItem}">
                    <Grid>
                        <Border Name="Border" BorderBrush="Black" BorderThickness="1,1,1,1" CornerRadius="6,6,0,0" >
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                    <GradientStop Color="#f5f7f8" Offset="0.0" />
                                    <GradientStop Color="#c5d0dd" Offset="1.0" />
                                </LinearGradientBrush>
                            </Border.Background>
                            <ContentPresenter Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Left" ContentSource="Header" Margin="5,2,0,2"/>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" TargetName="Border">
                                <Setter.Value>
                                    <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                        <LinearGradientBrush.GradientStops>
                                            <GradientStop Color="#ebedee" Offset="0.0" />
                                            <GradientStop Color="#88a2bd" Offset="1.0" />
                                        </LinearGradientBrush.GradientStops>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
like image 121
Nitin Avatar answered Oct 15 '22 13:10

Nitin


You just need to set the TabItem.DataContext to an object that contains values for your TextBlock and Image controls:

In code:

public class TabItemData
{
    public string ImageSource { get; set; }
    public string Heading { get; set; }
}

In your Style in XAML:

<StackPanel Orientation="Horizontal">
    <Image Margin="10,0,0,0" Height="40" Width="40" Source="{Binding ImageSource}"/>
    <TextBlock Text="{Binding Heading}" VerticalAlignment="Center" 
        Style="{StaticResource Heading2}" Margin="10,0,0,0"/>
</StackPanel>

In your view model:

public class TabControlViewModel
{
    public TabControlViewModel()
    {
        TabItemData = new TabItemData() { Header = "Some header", 
            ImageSource = "Images/IconLeafGrey.png" };
    }

    public TabItemData TabItemData { get; set; }
}

In XAML:

<TabItem DataContext="{Binding TabItemData}">
    ...
</TabItem>

Of course, I've left out some initialisation of the data objects and the INotifyPropertyChanged interface which you should implement, but I hope you get the idea.

like image 23
Sheridan Avatar answered Oct 15 '22 11:10

Sheridan