Using styles within a data template
Asked Answered
U

2

8

I have a class called item, and it contains just two properties. I will display them on the screen as buttons with a style. This question relates to how I can style the button based on the IsSelected value, when the element I want to affect is in the style not the data template. I have already tried with a Trigger but been unable to get it to work.

The class is below.

public class Item : ObservableObject
{
    private string _title;
    private bool _isSelected;

    public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            RaisePropertyChanged("Title");
        }
    }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            RaisePropertyChanged("IsSelected");
        }
    }
}

I use a data template to display these items in a ItemsControls.

<ItemsControl ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource ResourceKey=ItemTemplate}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

Using the following style and data template.

<Style x:Key="ItemButton" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border Name="ButtonBorder" BorderThickness="2,2,2,0" BorderBrush="#AAAAAA"  CornerRadius="6,6,0,0"  Margin="2,20,0,0" Background="Black">
                    <ContentPresenter
                            VerticalAlignment="Center"  
                            HorizontalAlignment="Center"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<DataTemplate x:Key="ItemTemplate">
    <Button Height="60" Style="{StaticResource ItemButton}" Name="Button">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Path=Title}" 
                       HorizontalAlignment="Left" Margin="5,5,5,3" FontSize="25" Foreground="#6B6B6B" FontFamily="Arial" />
                <Button Style="{StaticResource NoChromeButton}" Margin="0,0,5,0">
                <Button.Content>
                    <Image Height="20" Source="/WpfApplication1;component/Image/dialogCloseButton.png"></Image>
                </Button.Content>
                <Button.ToolTip>
                    Close    
                </Button.ToolTip>
            </Button>
        </StackPanel>
    </Button>
</DataTemplate>

I need to change the Background of "ButtonBorder" from Black to White when IsSelected is True, on the object Item.

I have added in a Trigger in the Data Template This does not work, I guess its because the style overrides the DataTemplate, thus the background stays white. Yet when i try to do a trigger in the style, I can't access the property IsSelected?

DataTemplate Trigger

    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsSelected}" Value="True">
            <Setter TargetName="Button" Property="Background" Value="White"/>
        </DataTrigger>
    </DataTemplate.Triggers>

Style trigger

    <Style.Triggers>
        <DataTrigger Binding="{Binding IsSelected}" Value="True">
            <Setter Property="Background" Value="White"/>
        </DataTrigger>
    </Style.Triggers>

Am i missing something?

Ultramicroscope answered 17/11, 2011 at 14:43 Comment(4)
If ItemButton is only used for the Item object, then why don't you just consolidate the Style into the Item's DataTemplate?Ludivinaludlew
Its a fair comment, I have gotten into the habit of separating everything out to individual resources, not sure if it is a good habit but one I have stuck to :)Ultramicroscope
@jberger It also makes the Resources and UI layout easier to read and modify if they're not cluttered with inline Templates and Styles.Webbing
I usually just Ctrl M,L to "declutter".Ludivinaludlew
W
12

Make your ButtonBorder.Background be {TemplateBinding Background}, which means it will use whatever background color is assigned to the templated Button, then you can change your Button's background based on a Trigger

<Style x:Key="ItemButton" TargetType="Button">
    <Setter Property="Background" Value="Black" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border Name="ButtonBorder" Background="{TemplateBinding Background}" ... >
                    <ContentPresenter ... "/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="SelectableItemButton" TargetType="Button" BasedOn="{StaticResource ItemButton}">
    <Setter Property="Background" Value="Black" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsSelected}" Value="True">
            <Setter Property="Background" Value="White"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

<DataTemplate x:Key="ItemTemplate">
    <Button Height="60" Style="{StaticResource SelectableItemButton}">
        ...
    </Button>
</DataTemplate>

I'm also making a SelectableItemButton Style which inherits from ItemButton, and just implements the trigger

Webbing answered 17/11, 2011 at 15:40 Comment(2)
That worked a treat, I forgot about TemplateBinding which comes from having such limited time to work with WPF. But how does {Binding IsSelected}, know it relates to the my object, not IsSelected on potentially another GUI control like a checkbox.Ultramicroscope
@Ultramicroscope Since you're using a DataTrigger, the binding is looking at Button.DataContext.IsSelected and changing the background color if the value exists and is True. You could technically make a Button and give it a DataContext of a CheckBox object and it would probably work, although I wouldn't recommend that :)Webbing
P
0

Shouldn't the target be "ButtonBorder" instead of "Button" in :

<Setter TargetName="Button"....

Also, to access the property IsSelected you need to set the TargetType in the style ....

Pentheas answered 17/11, 2011 at 15:9 Comment(2)
You are correct I got the name wrong, but. TargetName doesn't work. In DataTemplate ButtonBorder doesn't exist as that name is defined in the style. In the style, TargetName is not a valid property to use in a Trigger (so visual studio tells me).Ultramicroscope
TargetType is set on the style, it is set to "Button". The data template creates a button and the content within it, then the button it creates has the ItemButton style applied to it.Ultramicroscope

© 2022 - 2024 — McMap. All rights reserved.