WPF: How to make styles in XAML conditional on a variable in your own C# code

WPF and XAML newbie here....

I need to tie a WPF Trigger or DataTrigger in XAML code into some C# code in a class other than the class of the XAML control. This is very frustrating as all 28,000 tutorials I've read only give a trivial example for Trigger or DataTrigger that involves properties that already exist (e.g. MouseOver), none of them give examples of how to tie it in with your own C# code.

I have a screen for displaying various report types. The XAML for all of the report types is the same, except that for diagnostic reports, my requirements are that the DataGrid cells be configured with TextBlock.TextAlignment="Left", while all other reports (i.e. the default) should be TextBlock.TextAlignment="Center". (There are a few other differences; for brevity I'll just say that's the only difference.) I really don't want to have to duplicate the entire XAML to special-case the diagnostics report, since 99% of it would be the same as the other reports.

To use a Trigger, I thought perhaps I need my class to inherit from DependencyObject so I can define DependencyProperty's in it (being a WPF newbie I realize I may be saying some really outlandish things). So in my C# code, I have a class with this...

namespace MyApplication
{
   public enum SelectedReportType
   {
      EquipSummary,
      EventSummary,
      UserSummary,
      DiagSummary
   }

   public sealed class ReportSettingsData : DependencyObject
   {
      private static ReportSettingsData _instance; // singleton

      static ReportSettingsData() { new ReportSettingsData(); }

      private ReportSettingsData() // private because it's a singleton
      {
         if (_instance == null) // only true when called via the static constructor
            _instance = this; // set here instead of the static constructor so it's available immediately
         SelectedReport = SelectedReportType.EquipSummary; // set the initial/default report type
      }

      public static ReportSettingsData Instance
      {
         get { return _instance; }
      }

      public static SelectedReportType SelectedReport
      {
         get { return (SelectedReportType)Instance.GetValue(SelectedReportProperty); }
         set { Instance.SetValue(SelectedReportProperty, value); }
      }

      public static readonly DependencyProperty SelectedReportProperty =
         DependencyProperty.Register("SelectedReport", typeof(SelectedReportType), typeof(ReportSettingsData));
   }
}

So in my XAML file, I've played with various incantations of Trigger and DataTrigger and can't figure out how to make it work. In every case, the diagnostic report has the same default characteristics of the other reports.

<my:HeaderVisual x:Class="MyApplication.ReportsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:my="clr-namespace:MyApplication">

   <DataGrid Name="_dgReport"
                ColumnWidth="Auto"
                CanUserAddRows="False"
                VerticalScrollBarVisibility="Auto"
                HorizontalScrollBarVisibility="Auto"
                ItemsSource="{Binding}"
                IsReadOnly="True">
      <DataGrid.Resources>
         <Style TargetType="DataGridCell">
            <Setter Property="TextBlock.TextAlignment" Value="Center"></Setter>
            <Style.Triggers>
               <!-- Override some property settings for Diagnostics reports... -->
               <!--
                  <DataTrigger Binding="{Binding my:ReportSettingsData.SelectedReport}"  Value="DiagSummary">
                  <DataTrigger Binding="{Binding Path=my:ReportSettingsData.SelectedReport}"  Value="DiagSummary">
               -->
               <Trigger Property="my:ReportSettingsData.SelectedReport"  Value="DiagSummary">
                  <Setter Property="TextBlock.TextAlignment" Value="Left"></Setter>
               </Trigger>
            </Style.Triggers>
         </Style>
      </DataGrid.Resources>
   </DataGrid>

</my:HeaderVisual>

How can I get my Trigger to fire when ReportSettingsData.SelectedReport == SelectedReportType.DiagSummary?

Answers


How to make styles in XAML conditional on a variable in your own C# code

I recommend that you look into a CellTemplate Selector (GridViewColumn.CellTemplateSelector Property (System.Windows.Controls)) where you can do the selection logic in code behind.

Rough Example

Simply define the templates (4 but two shown) needed in the resource

<Window.Resources>
    <DataTemplate x:Key="EquipTemplate">

        <TextBlock Margin="2" Text="Equip" Foreground="Green"/>

    </DataTemplate>

    <DataTemplate x:Key="EventTemplate">

        <TextBlock Margin="2" Text="Event" Foreground="Red"/>

   </DataTemplate>

    <DataTemplate x:Key="UserTemplate" ...

</Window.Resources>

Xaml template usage selector for the grid cell

<DataGridTemplateColumn Header="My Event">
    <DataGridTemplateColumn.CellTemplateSelector>
        <local:SelectedReportTypeTemplateSelector
            EquipTemplate="{StaticResource EquipTemplate}"
            EventTemplate="{StaticResource EventTemplate}"
            User...
        />
    </DataGridTemplateColumn.CellTemplateSelector>
</DataGridTemplateColumn>

Code Behind

public class MeetingTemplateSelector : DataTemplateSelector
    {
        public DataTemplate EquipTemplate { get; set; }

        public DataTemplate EventTemplate { get; set; }

        public DataTemplate UserTemplate { get; set; }

        protected override DataTemplate SelectTemplateCore(object item, 
                  DependencyObject container)
        {
           DataTemplate result;

           switch( ((ReportSettingsData) item).SelectedReport)
           {
                case EquipSummary : result = EquipTemplate; break;
                case EventSummary : result = EventTemplate; break;
                case UserSummary ..
           }

          return result;
        }
    }

Update

As per the comment that the variety of choices makes the template suggestion grow to over 30 templates. One other way might be to extend the target class with operational properties in lieu of the triggered actions. For example say we need a red color shown, provide it on the instance and bind.

public Partial MyClassInstance
{
    public Brush ColorMeAs 
    {
         get { return this.IsValid ? BrushGreen  : BrushRed; }
    }
    ... other properties as such:
}

then bind as such

Foreground="{Binding ColorMeAs}"
Triggers

Here is an example of a data trigger pulled from my archive:

<Style x:Key="LabelStyle" TargetType="{x:Type Label}">
    <Setter Property="VerticalAlignment" Value="Top" />
    <Setter Property="Width" Value="80" />
    <Setter Property="Height" Value="28"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=LoginInProcess}" Value="True">
            <Setter Property="IsEnabled" Value="False" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=LoginInProcess}" Value="False">
            <Setter Property="IsEnabled" Value="True" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Your C# code looks fine, and you already have the XML NameSpace declaration referencing your MyApplication namespace, so:

You should just be able to access the enum value using the x:Static markup using the enum identifier as shown in this example (I like this example because it also shows how to use non-custom x:Static and how to do some tree traversal as well):

<DataTemplate.Triggers>
  <MultiDataTrigger>
    <MultiDataTrigger.Conditions>
      <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True" />
      <Condition Binding="{Binding Type}" Value="{x:Static loc:AppProfileItemType.Custom}" />
    </MultiDataTrigger.Conditions>
    <MultiDataTrigger.Setters>
      <Setter TargetName="PART_Delete" Property="Visibility" Value="{x:Static Visibility.Visible}" />
    </MultiDataTrigger.Setters>
  </MultiDataTrigger>
</DataTemplate.Triggers>

In your case your Markup for your enum should be:

Value="{x:Static my:SelectedReportType.DiagSummary}"


Need Your Help

JPA : Many to Many query help needed

java hibernate jpa jpql

I have four entities that are involved in a query that I'm having a little trouble with. The relationship is as follows : Exchange----*Contract*----*Combo----*Trade and the (simplified) entities a...

What is a good easy to use framework for rapidly developing web apps with a C# backend?

asp.net web responsive-design single-page-application web-frameworks

I am after recommendations for a framework (or project template) for rapid application development using C# on the back-end. It must support the following: