在WPF DataGrid中,我需要根据相同的复杂基础classe(具有子属性)显示多个列,并能够自定义DataGridCell的显示(如背景颜色),具体取决于不同的子绑定属性从DataGridCell值来显示。这是一个明确的例子:
<Window x:Class="Wpf_DataGrid_In_out_range.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_DataGrid_In_out_range"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="250">
<Window.Resources>
<Style x:Key="InRangeStyle" TargetType="DataGridCell">
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="Background" Value="Orange"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsInRange}" Value="False" >
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsInRange}" Value="True" >
<Setter Property="Background" Value="Green"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="191*"/>
<ColumnDefinition Width="326*"/>
</Grid.ColumnDefinitions>
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" Grid.ColumnSpan="2">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding X}" CellStyle="{StaticResource InRangeStyle}" Header="X"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Y}" CellStyle="{StaticResource InRangeStyle}" Header="Y"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<MySample> samples = new List<MySample>();
samples.Add(new MySample(5, 25));
samples.Add(new MySample(25, 15));
samples.Add(new MySample(0, 0));
samples.Add(new MySample(15, 45));
DataContext = samples;
}
}
public class MySample
{
public RangeValue X { get; set; }
public RangeValue Y { get; set; }
public MySample(int x,int y)
{
X = new RangeValue(x, 1, 10);
Y = new RangeValue(y, 20, 40);
}
}
public class RangeValue
{
public int Value { get; set; }
public int Min { get; set; }
public int Max { get; set; }
public bool IsInRange
{
get
{
if (Value <= Max && Value >= Min) return true;
else return false;
}
}
public RangeValue(int value, int min, int max)
{
Value = value;
Min = min;
Max = max;
}
public override string ToString()
{
return Value.ToString("F2");
}
}
提前致谢。 RGDS, 帕斯卡。
答案 0 :(得分:0)
这里的复杂之处在于,行的datacontext是集合中的整个对象,您可以指定要绑定到哪个属性并将其视为文本。 细胞不知道&#34;您打算将该属性作为对象感兴趣,并将其用于任何事物。 如果您希望样式可以重复使用,那么必须以某种方式指示查看X或Y上的属性。
这样做的一种方法是,如果它有一个x或y的datacontext。 使样式适用于网格。
<Window.Resources>
<Style x:Key="InRangeStyle" TargetType="Grid">
<Setter Property="Background" Value="Orange"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsInRange}" Value="False" >
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsInRange}" Value="True" >
<Setter Property="Background" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
然后确保您的单元格中有一个网格:
<DataGridTemplateColumn Header="X">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid DataContext="{Binding X}" Style="{StaticResource InRangeStyle}">
<TextBlock Text="{Binding Value}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
由于IsInrange始终为红色或绿色,因此您可以只有一个数据触发器为true(或为false),并为其他状态而不是橙色提供默认值。
答案 1 :(得分:0)
谢谢安迪, 它起作用,这是一个很好的进步。但有没有办法让它更通用,如:
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" Grid.ColumnSpan="2">
<DataGrid.Resources>
<DataTemplate x:Key="InOutDataTemplate">
<Grid Style="{StaticResource InRangeStyle}">
<TextBlock Text="{Binding Value}"/>
</Grid>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="X" ClipboardContentBinding="{Binding X}" CellEditingTemplate="{StaticResource InOutDataTemplate}"/>
<DataGridTemplateColumn Header="Y" ClipboardContentBinding="{Binding Y}" CellEditingTemplate="{StaticResource InOutDataTemplate}"/>
</DataGrid.Columns>
</DataGrid>
我确实使用了ClipboardContentBinding,因为我没有找到任何其他&#39;绑定的东西&#39;或DataConridTemplateColumn的DataContext。
RGDS, 帕斯卡。
答案 2 :(得分:0)
用于在数据类具有相同类型的多个属性(例如下面的Item
)的情况下自动自动生成自定义列
public class Item
{
public Enum Prop0 { get; set; } = Enum.CustomEnum1;
public Enum Prop1 { get; set; } = Enum.CustomEnum2;
}
我建议为数据类制作一个立面,您打算将其实例绑定到数据网格。
facade类将基本上复制初始数据类,但相同类型的属性(需要自定义日期单元格)将具有单独的类型:
public class ItemFacade
{
private readonly Item item;
public ItemFacade(Item item) => this.item = item;
public EnumContainer0 Prop0
{
get => new EnumContainer0 { Enum = this.item.Prop0 };
set => this.item.Prop0 = value.Enum;
}
public EnumContainer1 Prop1
{
get => new EnumContainer1 { Enum = this.item.Prop1 };
set => this.item.Prop1 = value.Enum;
}
}
这些个别的特定类型可以是所讨论的属性类型的子类(如果可能),也可以是其外墙类型的子类:
public class EnumContainer
{
public Enum Enum { get; set; }
}
public class EnumContainer0 : EnumContainer
{ }
public class EnumContainer1 :EnumContainer
{ }
在这种情况下,您甚至可以考虑隐式强制转换:)。 XAML代码将如下所示:
Window x:Class="TestWpfDataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfDataGrid"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DataGrid ItemsSource="{Binding Items}">
<i:Interaction.Behaviors>
<local:ColumnHeaderBehaviour/>
</i:Interaction.Behaviors>
<DataGrid.Resources>
<DataTemplate DataType="{x:Type local:EnumContainer0}">
<TextBlock Text="{Binding Prop0.Enum}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:EnumContainer1}">
<TextBlock Text="{Binding Prop1.Enum}"/>
</DataTemplate>
</DataGrid.Resources>
</DataGrid>
</Grid>
</Window>
请注意,您必须绑定到数据模板中的各个属性。原因是模板控件的数据上下文是包含的实例。这就是为什么我们需要这种解决方法的原因。 在local:ColumnHeaderBehaviour中的附加行为可以如下:
public class ColumnHeaderBehaviour : Behavior<DataGrid>
{
protected override void OnAttached()
{
AssociatedObject.AutoGeneratingColumn += OnGeneratingColumn;
}
protected override void OnDetaching()
{
AssociatedObject.AutoGeneratingColumn -= OnGeneratingColumn;
}
private static void OnGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs eventArgs)
{
if (eventArgs.PropertyDescriptor is PropertyDescriptor descriptor)
{
var control = (DataGrid)sender;
var resourceDictionary = control.Resources;
var dataTemplate = resourceDictionary.Values
.OfType<DataTemplate>()
.Where(el => (Type)el.DataType == descriptor.PropertyType)
.FirstOrDefault();
if (dataTemplate != null)
{
var column = new DataGridTemplateColumn()
{
CellTemplate = dataTemplate,
};
eventArgs.Column = column;
}
eventArgs.Column.Header = descriptor.DisplayName ?? descriptor.Name;
}
else
{
eventArgs.Cancel = true;
}
}
}
请注意,如果在相关数据网格内指定了数据模板,则上述行为代码将起作用。
此外观类是完全可测试的。键入下来,对其进行测试肯定会比我在寻找解决方案时花费的时间少。
关于, 瓦西里