简体   繁体   English

WPF:如何在XAML中以您自己的C#代码中的变量为条件的样式

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

WPF and XAML newbie here.... WPF和XAML新手在这里...

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. 我需要将XAML代码中的WPF TriggerDataTrigger到XAML控件类之外的其他类中的某些C#代码中。 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 (eg MouseOver ), none of them give examples of how to tie it in with your own C# code. 这让我非常沮丧,因为我仅阅读了所有28,000个教程,为TriggerDataTrigger给出了一个简单的示例,其中涉及已经存在的属性 (例如MouseOver ),没有一个给出如何将其与您自己的C#代码绑定的示例。

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 (ie the default) should be TextBlock.TextAlignment="Center" . 除诊断报告外,所有报告类型的XAML均相同,我的要求是使用TextBlock.TextAlignment="Left"配置DataGrid单元,而所有其他报告(即默认报告)应为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. (还有一些其他差异;为简洁起见,我只说那是唯一的差异。)我真的不想复制整个XAML来对诊断报告进行特殊处理,因为其中99%是与其他报告相同。

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). 为了使用触发器,我想也许我需要我的类从DependencyObject继承,以便我可以在其中定义DependencyProperty(作为WPF新手,我意识到我可能在说一些很奇怪的事情)。 So in my C# code, I have a class with this... 所以在我的C#代码中,我有一个与此相关的类...

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. 因此,在我的XAML文件中,我玩过TriggerDataTrigger各种咒语,无法弄清楚如何使其工作。 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 ? ReportSettingsData.SelectedReport == SelectedReportType.DiagSummary时,如何Trigger

How to make styles in XAML conditional on a variable in your own C# code 如何在XAML中以您自己的C#代码中的变量为条件的样式

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. 我建议您查看一个CellTemplate选择器( GridViewColumn.CellTemplateSelector属性(System.Windows.Controls) ),在其中可以使用后面的代码进行选择逻辑。

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 网格单元的Xaml模板用法选择器

<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. 根据评论,各种各样的选择使模板建议增加到30多个模板。 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: 您的C#代码看起来不错,并且已经有了引用MyApplication名称空间的XML NameSpace声明,因此:

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): 您应该只能够使用x:Static标记并使用枚举标识符访问enum值,如本示例所示(我喜欢这个示例,因为它还显示了如何使用非自定义x:Static以及如何进行一些树遍历以及):

<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}"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM