这是数据网格。每行代表一个锦标赛对象,每个单元格一个属性,可以在外部更新。目标是突出更新的价值。
来自 MainWindow.cs 的片段:
namespace TurnierChecker
{
/// <summary>
/// Interaction logic for MainWindow.xaml.
/// </summary>
public partial class MainWindow
{
private readonly ICollectionView sortedTournamentList;
/// <summary>
/// Initializes a new instance of the <see cref="MainWindow"/> class.
/// </summary>
public MainWindow()
{
// intialization step
this.InitializeComponent();
// Initial loading of data.
SortableBindingList<Tournament>? tournaments = JSONSerializer.DeserializeFromFile<SortableBindingList<Tournament>>("TournamentData.json");
this.TournamentList = tournaments ?? new SortableBindingList<Tournament>();
this.TournamentList.ListChanged += this.TournamentListChanged;
// Sorting
this.sortedTournamentList = CollectionViewSource.GetDefaultView(this.TournamentList);
this.sortedTournamentList.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Ascending));
this.sortedTournamentList.Refresh();
// tie View with ViewModel
this.TournamentGrid.DataContext = this.TournamentList;
}
/// <summary>
/// Gets the tournament list.
/// </summary>
/// <value>
/// The tournament list.
/// </value>
public SortableBindingList<Tournament> TournamentList { get; private set; }
public void TournamentListChanged(object? sender, ListChangedEventArgs e)
{
// Property of tournament change.
if (e.ListChangedType == ListChangedType.ItemChanged)
{
Tournament? tmnt = (sender as SortableBindingList<Tournament>)?.ElementAt(e.NewIndex);
DataGridRow? row = this.TournamentGrid.ItemContainerGenerator.ContainerFromItem(tmnt) as DataGridRow;
LoggerUI.Instance.Error(Logging.LogType.System, $"Row: {row} - Turnier: {tmnt}");
switch (e.PropertyDescriptor?.Name)
{
case nameof(Tournament.Date):
break;
case nameof(Tournament.Series):
row.Background = Brushes.Green;
break;
// Default property change.
default:
break;
}
}
}
}
}
MainWindow.xaml 中的片段:
<Window x:Class="TurnierChecker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TurnierChecker"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="TurnierSpion" Height="850" Width="1240" Icon="Resources/Logo.ico">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="100"/>
<RowDefinition Height="5" MinHeight="5"/>
<RowDefinition Height="22" MinHeight="22"/>
<RowDefinition Height="250" MinHeight="100"/>
<RowDefinition Height="30" MinHeight="30"/>
</Grid.RowDefinitions>
<!--Tournament DataGrid-->
<DataGrid x:Name="TournamentGrid" ItemsSource="{Binding}" AlternatingRowBackground="Moccasin" AlternationCount="2" IsReadOnly="True" HeadersVisibility="Column"
AutoGenerateColumns="False" VerticalScrollBarVisibility="Auto" RowHeight="28" SelectionUnit="FullRow">
<!-- This is required to handle CTRL + C when something is selected in the DataGrid -->
<DataGrid.CommandBindings>
<CommandBinding Command="Copy" Executed="CopyCommand" />
</DataGrid.CommandBindings>
<!-- This is required to handle CTRL + C when something is selected in the DataGrid -->
<DataGrid.InputBindings>
<KeyBinding Key="C" Modifiers="Control" Command="Copy" />
</DataGrid.InputBindings>
<DataGrid.Columns>
<!--Tournament Date-->
<DataGridTextColumn Binding="{Binding Date, StringFormat=\{0: ddd dd.MM.yy HH:mm U\\hr\}, ConverterCulture=de-DE}" Header="Datum" Width="160" MinWidth="170" SortDirection="Ascending"/>
<!--Tournament Catgory-->
<DataGridTextColumn Binding="{Binding Category}" Header="Kategorie" Width="180" MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</DockPanel>
</Window>
来自 Tournament.cs 的片段:
namespace TurnierChecker.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
/// <summary>
/// The tournament model.
/// </summary>
public class Tournament : INotifyPropertyChanged, IEquatable<Tournament>
{
/// <summary>
/// The date and time of the tournament.
/// </summary>
private DateTime? date;
/// <summary>
/// The series of the tournament.
/// </summary>
private string? series;
/// <summary>
/// Initializes a new instance of the <see cref="Tournament"/> class.
/// </summary>
public Tournament()
{
}
/// <summary>
/// Tritt ein, wenn sich ein Eigenschaftswert ändert.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Gets or sets the date and time of the tournament.
/// </summary>
/// <value>
/// The date and time.
/// </value>
public DateTime? Date
{
get => this.date;
set => this.SetField(ref this.date, value);
}
/// <summary>
/// Gets or sets the series of the tournament.
/// </summary>
/// <value>
/// The series.
/// </value>
public string? Series
{
get => this.series;
set => this.SetField(ref this.series, value);
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <param name="propertyName">Name of the property.</param>
private void OnPropertyChanged(string? propertyName) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary>
/// Sets the field.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="field">The field.</param>
/// <param name="value">The value.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns>Returns true if the property was changed.</returns>
private bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
this.OnPropertyChanged(propertyName);
return true;
}
}
}
好的,我可以给你一些东西。它有点离谱,并且不包含在 XAML 中,但我仍然会尝试。
这是我的数据网格行的片段
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Header="EmployeeId" Binding="{Binding EmployeeId}" IsReadOnly="True"/>
<DataGridTextColumn x:Name="Name" Header="Name" Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridCheckBoxColumn x:Name="HasLeased" Header="HasLeasedCar" Binding="{Binding HasLeasedCar, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn x:Name="RFID" Header="RFID" Binding="{Binding Rfid, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn x:Name="RFIDMapped" Header="RFIDMapped" Binding="{Binding RfidMapped, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn x:Name="Created" Header="CreatedDate" Binding="{Binding CreatedDate}" IsReadOnly="True"/>
<DataGridTextColumn x:Name="Modified" Header="ModifiedDate" Binding="{Binding ModifiedDate}" IsReadOnly="True" Width="*"/>
</DataGrid.Columns>
现在我的 Employee 对象对于每个可编辑属性都有一个 HasNew 属性。 例如,我有 RFID 和 HasNewRFID,在这种情况下,列标题与 HasNewAttributes 具有相同的名称也很重要。
private bool _nameInit;
private string _oldName = null!;
private string _name = null!;
public string Name
{
get => _name;
set
{
if (!_nameInit)
{
_oldName = value;
_name = value;
_nameInit = true;
}
else
{
if (value.Trim() == _name && value.Trim() != _oldName)
{
return;
}
_name = value.Trim();
OnPropertyChanged();
HasNewName = _name != _oldName;
}
}
}
private bool _hasNewName;
public bool HasNewName
{
get => _hasNewName;
set
{
_hasNewName = value;
OnPropertyChanged();
}
}
public bool HasNew => HasNewName || HasNewRFID || etc
现在我的主要想法是,我可以为每个实例编写一个样式,但我想自动化。
#region Style Support
public void CreateSources()
{
ILog log = _logger.Log().Start();
try
{
DataGridColumn[] editableColumns = DatGrdEmployee.Columns.Where(c => !c.IsReadOnly).ToArray();
string[] na = editableColumns.Select(c => "HasNew" + (c.Header as string)).ToArray();
Dictionary<string, Style> styles = CreateSource(na);
foreach (DataGridColumn c in editableColumns)
{
var style = styles.Where(s => s.Key == (c.Header as string)).ToArray()[0].Value;
c.CellStyle = style;
}
}
catch (Exception ex)
{
log.Error(ex);
}
log.End();
}
public Dictionary<string, Style> CreateSource(string[] pathNames)
{
// ReSharper disable once CoVariantArrayConversion
ILog log = _logger.Log().Start(pathNames);
Dictionary<string, Style> styles = new();
try
{
Setter orangeBackground = new(BackgroundProperty, new SolidColorBrush(Colors.Orange));
Setter transparentBackground = new(BackgroundProperty, new SolidColorBrush(Colors.Transparent));
foreach (string pathName in pathNames)
{
Style style = new();
style.Setters.Add(transparentBackground);
DataTrigger dataTrigger = new()
{
Binding = new Binding
{
Path = ((PropertyPath)(TypeDescriptor.GetConverter(typeof(PropertyPath)).ConvertFromInvariantString(pathName))!)
},
Value = true,
Setters = { orangeBackground }
};
style.Triggers.Add(dataTrigger);
string newPathName = pathName.Replace("HasNew", "");
styles.Add(newPathName, style);
}
}
catch (Exception ex)
{
log.Error(ex);
}
return log.End(styles);
}
#endregion
因此,如果我们一步一步进行:一开始我只是收集所有我感兴趣/应该对值变化做出反应的列。
然后在 na 中,我将所有标头与 HasNew 组合起来,这样我就有一个充满 HasNewName、HasNewRFID 等的数组。
然后在 CreateSource() 中我创建一个包含字符串和样式的字典。最终目标是用标题名称和样式填充它。
然后我们创建两个 setter,一个用于更改值,一个用于正常状态。
我们进入 foreach 循环,首先创建样式并添加或普通设置器。
然后我们创建一个DataTrigger,它将创建一个Binding,看起来很复杂,但最后是这样的
Binding = new Binding
{
Path = ((PropertyPath)(TypeDescriptor.GetConverter(typeof(PropertyPath)).ConvertFromInvariantString(pathName))!)
}
Only 真正意味着 Binding ="{Binding Path= HasNew[Property]}" 完整的 DataTrigger 代码翻译为
<DataTrigger Binding="{Binding Path=HasNewName}" Value="True">
<Setter Property="Background" Value="Orange"/>
</DataTrigger>
然后我们将该触发器添加到样式中。然后,我们从字符串中删除 HasNew,以便取回标头,然后将该标头名称和样式添加到字典中。现在冲洗并重复,您就明白了。
现在样式看起来像这样
<Style>
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=HasNewName}" Value="True">
<Setter Property="Background" Value="Orange"/>
</DataTrigger>
</Style.Triggers>
</Style>
简单解释一下Style中的DataTrigger。它基本上只是意味着当 X 条件满足时,将 Y 属性设置为值 Z,如果未满足,则值 Y 将为其默认值。
一旦我们拥有了所有样式,我们就会返回字典并迭代我们的列。我们从标题具有适当名称的字典中获取样式,并将列单元格样式设置为该样式,导致该列中的每个单元格现在都具有该样式。现在不用担心每个单元格仍然绑定到其行的员工。就是这样。现在,如果您真的不想使用 C# 代码,则必须为每一列单独执行每种样式。
但是现在假设您想要更简单的东西,只需检查:是否有更改并重新着色整行。 现在这相当简单
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding HasNew}" Value="True">
<Setter Property="Background" Value="Orange"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
只需将其直接添加到 DataGridColoumns 下方即可完成。 这对我来说就是细胞变化的样子。我审查了很多行,因为其他条目都是真实的人,我不会破坏公司数据