如果绑定属性有一定的值,如何覆盖一个圆圈的单元格的内容?



我使用DataGrid来显示资产价格,因此我有许多行和列。例如,我像这样显示当前价格:

<DataGridTextColumn Width="50" SortMemberPath="Price" Binding="{Binding Path=Price}">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding Path=Price}"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>

有时如果值无效,我只显示-。我想做的是显示一个圆形,如果绑定属性的值是-

我可以通过添加一个圆圈来实现这一点,圆圈的可见性绑定到一个新属性,该属性检查价格是否无效,而上面的文本显示则相反。但问题是,这将要求我为每个属性创建新的绑定,这是我试图避免的。

这是否可能与触发器或有更好的方法来做到这一点?

文本值转换器

一种方法是创建一个值转换器,如果值不可用(-),则返回其参数。

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>

带有数据模板选择器的模板列

如果需要最大的灵活性,可以将模板列与数据模板选择器结合使用。请注意,在数据网格模板列中有一些限制,比如传递给列的itemnull,这需要一个变通方法。由于模板列的数据上下文是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创建一个专用类型。您可以参考这个相关的问题以获得更多信息,因为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>

相关内容

最新更新