如何创建和使用自定义 DataGridRow 触发器

Posted

技术标签:

【中文标题】如何创建和使用自定义 DataGridRow 触发器【英文标题】:How to create and use a custom DataGridRow Trigger 【发布时间】:2017-07-28 22:52:48 【问题描述】:

我正在使用 Prism 6 创建我的应用程序,我需要根据日期为 DataGridRows 的背景着色。我开始创建一个 Blend SDK 触发器来接受两个参数:MinDate 和 MaxDate。然后该操作将设置背景颜色。但是,在使用触发器时,我遇到了障碍。它在集合中不被接受,当我使用 DataTemplate 时,我似乎无法让触发器执行。

这是触发器的代码。它实际上除了调用操作之外没有做任何事情,因为我想确保它在编码逻辑以检查日期之前正在执行。

public class AnniversaryTrigger: TriggerBase<DataGridRow>

    public DateTime MaxDate
    
        get  return (DateTime)GetValue(MaxDateProperty); 
        set  SetValue(MaxDateProperty, value); 
    

    public DateTime MinDate
    
        get  return (DateTime)GetValue(MinDateProperty); 
        set  SetValue(MinDateProperty, value); 
    

    protected override void OnAttached()
    
        AssociatedObject.Loaded += OnLoaded;
        AssociatedObject.Unloaded += OnUnloaded;
    

    protected override void OnDetaching()
    
        AssociatedObject.Loaded -= OnLoaded;
        AssociatedObject.Unloaded -= OnUnloaded;
    

    private void OnLoaded(object sender, System.Windows.RoutedEventArgs e)
    
        AssociatedObject.DataContextChanged += OnDataContextChanged;
        Refresh();
    

    private void OnUnloaded(object sender, System.Windows.RoutedEventArgs e)
    
        AssociatedObject.DataContextChanged -= OnDataContextChanged;
    

    private void Refresh()
    
        base.InvokeActions(null);
    

    private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    
        Refresh();
    

    #region Dependency Properties
    public static readonly DependencyProperty MaxDateProperty =
        DependencyProperty.Register("MaxDate", typeof(DateTime), typeof(AnniversaryTrigger), new PropertyMetadata(null));

    public static readonly DependencyProperty MinDateProperty =
        DependencyProperty.Register("MinDate", typeof(DateTime), typeof(AnniversaryTrigger), new PropertyMetadata(null));
    #endregion

DataTemplate 如下,附在 DataGrid ItemTemplate 上。

<UserControl.Resources>
    <DataTemplate x:Key="AnniversaryTemplate">
        <DataGridRow>
            <i:Interaction.Triggers>
                <b:AnniversaryTrigger MinDate="Binding MinDate" 
                                      MaxDate="Binding MaxDate" >
                    <ei:ChangePropertyAction PropertyName="Background">
                        <ei:ChangePropertyAction.Value>
                            <SolidColorBrush Color="Yellow"/>
                        </ei:ChangePropertyAction.Value>
                    </ei:ChangePropertyAction>
                </b:AnniversaryTrigger>
            </i:Interaction.Triggers>
        </DataGridRow>
    </DataTemplate>
</UserControl.Resources>

这是数据网格:

<DataGrid ItemsSource="Binding FalseAlarmHistory" AutoGenerateColumns="False" IsReadOnly="True" ItemTemplate="DynamicResource AnniversaryTemplate" >
        <DataGrid.Columns>
            <DataGridTextColumn Header="Incident ID" 
                                Binding="Binding IncidentID" />
            <DataGridTextColumn Header="Incident Date" 
                                Binding="Binding IncidentDate, StringFormat=d" />
            <DataGridTextColumn Header="Incident Time" 
                                Binding="Binding IncidentTime" />
            <DataGridTextColumn Header="Notes" 
                                Binding="Binding Notes" />
        </DataGrid.Columns>
    </DataGrid>

感谢任何指导。

事件历史视图模型:

public class FalseAlarmHistoryViewModel : ValidatingBindableBase, IConfirmNavigationRequest

    private IFalseAlarmService _service;
    private ICustomerService _custsvc;

    private string _title;
    private IEventAggregator _eventAggregator;

    public string Title  get => _title; set => SetProperty(ref _title, value); 

    public FalseAlarmHistoryViewModel(IFalseAlarmService service, ICustomerService custsvc, IEventAggregator eventAggregator)
    
        _service = service;
        _custsvc = custsvc;
        _eventAggregator = eventAggregator;
        Title = "False Alarms History";

        _eventAggregator.GetEvent<PermitSelectedChangedEvent>().Subscribe(PermitIdChanged);
    

    //todo Color DataGrid Rows based on the anniversary year the false alarm occurred.  
    // See https://***.com/questions/14997250/datagrid-row-background-color-mvvm
    // Add unmapped item to the FalseAlarmHistory entity that returns the year based on the anniversary year. 0 = current year, 1 = 1 year ago, etc.
    // Translate the year number into a color that will be used on the DataGrid row.  Make the color configurable (in app.config at least).

    //todo Initial sort should be most recent first.
    private void PermitIdChanged(int obj)
    
        FalseAlarmHistory = new ListCollectionView(_service.GetFalseAlarmHistoryByPermitId(_custsvc.CurrentPermitId).ToList());
        RaisePropertyChanged(nameof(FalseAlarmHistory));
    

    public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
    
        continuationCallback(true);
    

    public void OnNavigatedTo(NavigationContext navigationContext)
    
        FalseAlarmHistory = new ListCollectionView(_service.GetFalseAlarmHistoryByPermitId(_custsvc.CurrentPermitId).ToList());
        RaisePropertyChanged(nameof(FalseAlarmHistory));
    

    public bool IsNavigationTarget(NavigationContext navigationContext)
    
        return true;
    

    public void OnNavigatedFrom(NavigationContext navigationContext)
    
    

    #region Commands
    #endregion
    #region Event Handlers
    #endregion
    #region Bound Controls
    public ICollectionView FalseAlarmHistory  get; private set; 


    #endregion
    #region Bound Commands
    #endregion

【问题讨论】:

我觉得你正在尝试的对于简单的背景颜色变化来说太过分了。你看过Style Triggers吗?使用样式触发器,您可以根据日期在视图模型中设置属性,并根据日期在视图中设置颜色。 根据我对样式触发器的了解,您无法设置日期范围。我错了吗? 不,你没有错。但我建议您在视图模型中处理,而不是在视图 (XAML) 中。 你能给我更多的细节吗?您将如何在视图模型中而不是在 XAML 中处理它? 很高兴。我可以在今天晚些时候发布一些内容作为答案。就规则而言,您能否大致了解您打算如何处理最小和最大日期?我可以使用更多的上下文。 【参考方案1】:

所以,我的想法是向事件模型添加一个属性,该属性将根据事件和周年日期计算。听上去,您必须以某种方式将周年纪念日纳入事件模型。您可以从您的数据中执行此操作,或通过属性、构造函数、函数等将其传递给事件模型。

假设您将属性称为IncidentAgeBucket 并且类型为AgeBucket

// Inside Incident Model
public AgeBucket IncidentAgeBucket  get 
    // AgeBucket calculation logic goes here
    // Check to make sure you have an anniversary date first...
    // There are a few ways to approach setting this value, use one that you are comfortable with 
    


// New enumeration to use (name these something more to your liking)
public enum AgeBucket
    OneYear,
    TwoYear,
    ThreeYear

所以,如果日期在周年日期的年份内,则使IncidentAgeBucket的值返回AgeBucket.OneYear,两年后返回AgeBucket.TwoYear,等等。

在 XAML 中,根据属性中的值添加 RowStyle

<DataGrid x:Name="IncidentList" ItemsSource="Binding Incidents" ...>
    <DataGrid.Columns>
        <DataGridTextColumn Header="Incident Number" Binding="Binding IncidentNumber"/>
        ...
    </DataGrid.Columns>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="Binding IncidentAgeBucket" Value="x:Static local:AgeBucket.OneYear">
                    <Setter Property="Background" Value="LightGreen"></Setter>
                </DataTrigger>
                <DataTrigger Binding="Binding IncidentAgeBucket" Value="x:Static local:AgeBucket.TwoYear">
                    <Setter Property="Background" Value="LightBlue"></Setter>
                </DataTrigger>
                <DataTrigger Binding="Binding IncidentAgeBucket" Value="x:Static local:AgeBucket.ThreeYear">
                    <Setter Property="Background" Value="LightPink"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
</DataGrid>

您可能需要稍微尝试一下x:Static local:AgeBucket.OneYear 语法。本地命名空间可能不是您使用的。如果你最终嵌套了枚举,那么你还需要做其他事情才能让它工作。 A seemingly good reference 。

我希望这能让您对触发路线以外的方向有所了解。这对我来说似乎更直接。

如果这太离谱了,请告诉我,我会放弃这个答案。评论太多了!希望这会有所帮助。

【讨论】:

嗯...我试图避免修改模型以获取视图样式。这就是为什么我试图创建一个可以处理视图模型的触发器。我想在视图模型中有属性来设置触发器的参数并使用视图样式来相应地设置行的样式。这似乎是我打破了 MVVM 范式。我对此比较陌生。我的想法有问题吗? 诚然,这并不理想。它可能会破坏 MVVM。但是以我(个人)可以接受的方式。再说一次,也许不是。它确实有效。视图知道视图模型,但不知道相反,这是正确的 MVVM。这不像你是从视图模型中引导 UI,UI 只是查看视图模型的某些状态,然后做一些事情。我宁愿让一个属性返回一个可以在多个地方使用的枚举值,而不是它实际上返回类似于 Color 对象的东西。那条线我不会越过。 如果您觉得这没有任何价值,请告诉我,我将删除答案。 它很有价值,我很感激。我关心的不是视图模型,而是模型本身。如果我能让触发器工作,那么所有修改都将在视图模型中。我可能最终会改变模型,但这不是我的偏好。我还可以创建事件模型的副本,并为视图模型添加一个属性,但作为一种重用模式也不理想。 你真的有IncidentViewModel吗?或者,您只是通过视图的主要DataContext 中的Customer.Incidents 属性公开事件集合吗?

以上是关于如何创建和使用自定义 DataGridRow 触发器的主要内容,如果未能解决你的问题,请参考以下文章

在 WPF 样式中触发多个条件的正确方法是啥

如何基于现有的创建自定义 jquery 触发器

如何在自定义元素或按钮小部件上触发事件?

如何使用后面的代码创建自定义注释

javascript自定义事件

使用操作挂钩创建自定义Wordpress REST API端点