Search code examples
c#wpfdatatrigger

Change the color of an ellipse based off a boolean


<Grid Grid.Row="1" Width="500" Height="500">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Ellipse Fill="Red" HorizontalAlignment="Center" Height="25" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Center" Width="25"/>
    <Ellipse Fill="Red" HorizontalAlignment="Center" Height="25" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Center" Width="25" Grid.Row="1"/>
    <Ellipse Fill="Red" HorizontalAlignment="Center" Height="25" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Center" Width="25" Grid.Row="3"/>
    <Ellipse Fill="Red" HorizontalAlignment="Center" Height="25" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Center" Width="25" Grid.Column="4"/>
    <Ellipse Fill="Red" HorizontalAlignment="Center" Height="25" Margin="0,0,0,0" Stroke="Black" VerticalAlignment="Center" Width="25" Grid.Column="4" Grid.Row="4"/>
</Grid>

Given the above XAML I want the dots to be green when a property is true. I'm assuming I'd do it with a DataTrigger, but the only way I can think of doing it involved duplicating it for each ellipse. It seems hackish to me and was wondering if their is a better solution. Each ellipse is based of a single property, but again that seems like a lot of duplicate code. Ideally what I want is this view to reflect the state of a list of "Stations" using a boolean to determine if they're available or not. The state of each is one-way and won't change while the view is up.

I'm far to new at WPF and XAML to come up with an elegant solution. I cringe every time I attempt something as it just seems like a complete hack.

EDIT: Thanks to @Alastair's answer I've gotten it working.


Solution

  • So I would make a custom UserControl that contains an Ellipse.

    You can then put a the datatrigger into the UserControl. You then bind the DataContext of the custom control to your boolean property, then bind the DataTrigger to the DataContext of your UserControl.

    Thus you are able to keep your XAML clean.

    EDIT:

    A basic user control. THis should be defined in a separate file, as opposed to a resource. Just right-click the project -> Add -> New Item... then choose WPF UserControl.

    <UserControl x:Class="Test_WPF.MyEllipseControl"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid>
            <Ellipse  HorizontalAlignment="Center"
                      Height="25"
                      Margin="0,0,0,0"
                      Stroke="Black"
                      VerticalAlignment="Center"
                      Width="25" 
                      Fill="Red">
                <Ellipse.Style>
                    <Style TargetType="Ellipse">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=IsAvailable}"
                                         Value="True">
                                <Setter Property="Fill"
                                        Value="Green" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Ellipse.Style>
            </Ellipse>
        </Grid>
    </UserControl>
    

    and then you'd use it:

    <local:MyEllipseControl DataContext="{Binding Path=Station1}" />
    

    where the local namespace is just your local projects namespace.