如果绑定属性具有特定值,如何用圆圈覆盖单元格的内容?
Posted
技术标签:
【中文标题】如果绑定属性具有特定值,如何用圆圈覆盖单元格的内容?【英文标题】:How to override contents of a cell with a circle if the bound property has a certain value? 【发布时间】:2021-10-10 08:08:01 【问题描述】:我使用DataGrid
来显示资产价格,所以我有很多行和列。例如,我这样显示当前价格:
<DataGridTextColumn Width="50" SortMemberPath="Price" Binding="Binding Path=Price">
<DataGridTextColumn.Header>
<TextBlock Text="Binding Path=Price"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
有时如果值无效,我只显示-
。如果绑定属性的值为-
,我想要做的是显示一个圆形。
我可以通过添加一个圆圈来做到这一点,该圆圈的可见性绑定到一个检查价格是否无效的新属性,而上面的文本显示则相反。但问题是这将要求我为每个我试图避免的属性创建新的绑定。
这也许可以通过触发器实现,还是有更好的方法来做到这一点?
【问题讨论】:
【参考方案1】:文本值转换器
一种方法是创建一个返回其参数的值转换器,如果值不可用 (-
)。
public class ValueNotAvailableConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
return (value is string str) && str == "-" ? parameter : value;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
throw new InvalidOperationException("This is a one-way conversion.");
然后,您可以使用此转换器进行绑定,并指定符合您要求的字形作为转换器参数。要完成这项工作,请确保您使用的字体包含字形。在我的例子中,Segoe UI 包含一个实心圆圈,这可能正是您想要的。
<DataGrid ItemsSource="Binding YourItemsSource" AutoGenerateColumns="False">
<DataGrid.Resources>
<local:ValueNotAvailableConverter x:Key="ValueNotAvailableConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<!-- ...other columns. -->
<DataGridTextColumn Width="50" SortMemberPath="Price" Binding="Binding Price, Converter=StaticResource ValueNotAvailableConverter, ConverterParameter=●">
<DataGridTextColumn.Header>
<TextBlock Text="Binding Path=Price"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<!-- ...other columns. -->
</DataGrid.Columns>
<!-- ...other markup. -->
</DataGrid>
带有数据触发器的模板列
模板列、样式和数据触发器也是如此。
<DataGrid ItemsSource="Binding YourItemsSource" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="50" SortMemberPath="Price">
<DataGridTemplateColumn.Header>
<TextBlock Text="Binding Path=DataContext.Price, RelativeSource=RelativeSource AncestorType=x:Type DataGrid"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="x:Type TextBlock">
<Setter Property="Text" Value="Binding Price"/>
<Style.Triggers>
<DataTrigger Binding="Binding Price" Value="-">
<Setter Property="Text" Value="●"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
带有数据模板选择器的模板列
如果您需要最大的灵活性,可以将模板列与数据模板选择器结合使用。请注意,数据网格模板列存在限制,例如传递给列的item
是null
,这需要workaround。由于模板列的数据上下文是ItemsSource
的一个完整数据项,因此您必须在此处检查Price
属性。
public class PriceNotAvailableTemplateSelector : DataTemplateSelector
public string PriceAvailableTemplateKey get; set;
public string PriceNotAvailableTemplateKey get; set;
public override DataTemplate SelectTemplate(object item, DependencyObject container)
if (container is ContentPresenter contentPresenter &&
contentPresenter.Parent is DataGridCell dataGridCell)
if (dataGridCell.DataContext is YourDataType data && data.Price == "-")
return contentPresenter.FindResource(PriceNotAvailableTemplateKey) as DataTemplate;
return contentPresenter.FindResource(PriceAvailableTemplateKey) as DataTemplate;
return base.SelectTemplate(item, container);
现在您可以在价格可用和不可用时创建不同的数据模板。
<DataGrid ItemsSource="Binding YourItemsSource" AutoGenerateColumns="False">
<DataGrid.Resources>
<local:PriceNotAvailableTemplateSelector x:Key="PriceNotAvailableTemplateSelector"
PriceAvailableTemplateKey="PriceAvailableTemplate"
PriceNotAvailableTemplateKey="PriceNotAvailableTemplate"/>
<DataTemplate x:Key="PriceAvailableTemplate">
<TextBlock x:Name="ValueAvailable" Text="Binding"/>
</DataTemplate>
<DataTemplate x:Key="PriceNotAvailableTemplate">
<Ellipse x:Name="ValueNotAvailable" Width="5" Height="5" Fill="Red"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Width="50"
SortMemberPath="Price"
CellTemplateSelector="StaticResource PriceNotAvailableTemplateSelector">
<DataGridTemplateColumn.Header>
<TextBlock Text="Binding Path=DataContext.Price, RelativeSource=RelativeSource AncestorType=x:Type DataGrid"/>
</DataGridTemplateColumn.Header>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
自动数据模板选择
如您所见,数据模板选择器方法非常死板和不灵活。类似的方法是按类型自动选择数据模板。但是,要使其正常工作,您必须为 price 创建一个专用类型,并为 no price 创建一个类型。您可以参考this related question 了解更多信息,因为DataGrid
也有其怪癖,正如您所料。
带有数据模板和触发器的模板列
最后,一个更老套的解决方案是在数据模板中同时显示 TextBlock
和替代元素,并根据 Price
值更改它们的可见性。
<DataGrid ItemsSource="Binding YourItemsSource" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="50"
SortMemberPath="Price">
<DataGridTemplateColumn.Header>
<TextBlock Text="Binding Path=DataContext.Price, RelativeSource=RelativeSource AncestorType=x:Type DataGrid"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="ValueAvailable" Text="Binding Price"/>
<Ellipse x:Name="ValueNotAvailable" Visibility="Collapsed" Width="5" Height="5" Fill="Red"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="Binding Price" Value="-">
<Setter TargetName="ValueAvailable" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="ValueNotAvailable" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
【讨论】:
非常感谢,我正在使用您的最后一种方法并且它有效,但是当显示圆圈时,它似乎会缩放整行的高度。不知道为什么,因为我在另一列中使用具有相同宽度和高度 8 的相同形状,而他们不这样做。我将 Collapsed 更改为 Hidden 但结果仍然相同。是因为使用了 StackPanel 吗?以上是关于如果绑定属性具有特定值,如何用圆圈覆盖单元格的内容?的主要内容,如果未能解决你的问题,请参考以下文章