如果绑定 属性 具有特定值,如何用圆圈覆盖单元格的内容?
How to override contents of a cell with a circle if the bound property has a certain value?
我正在使用 DataGrid
来显示资产价格,所以我有很多行和列。例如,我这样显示当前价格:
<DataGridTextColumn Width="50" SortMemberPath="Price" Binding="{Binding Path=Price}">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding Path=Price}"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
有时如果值无效,我只显示 -
。如果绑定 属性 的值为 -
.
,我想要做的是显示一个圆形
我可以通过添加一个圆圈来做到这一点,该圆圈的可见性绑定到一个新的 属性 以检查价格是否无效,而上述文本显示则相反。但问题是,这将要求我为每个 属性 创建新的绑定,而我试图避免这种情况。
这是否可以通过触发器实现,或者有更好的方法吗?
文本值转换器
一种方法是创建一个 returns 其参数的值转换器,如果值不可用 (-
)。
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>
带有数据模板选择器的模板列
如果您需要最大的灵活性,您可以将模板列与数据模板结合使用 select 或者。请注意,数据网格模板列中存在限制,例如传递给列的 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>
自动数据模板选择
如您所见,数据模板select或方法非常死板和不灵活。类似的方法是 select 按类型自动生成数据模板。但是,要使其工作,您必须为 price 创建一个专用类型,并为 no price 创建一个类型。您可以参考 this related question 了解更多信息,因为 DataGrid
也有它的怪癖,正如您所期望的那样。
带有数据模板和触发器的模板列
最后,一个更 hackish 的解决方案是在数据模板中同时显示 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>
我正在使用 DataGrid
来显示资产价格,所以我有很多行和列。例如,我这样显示当前价格:
<DataGridTextColumn Width="50" SortMemberPath="Price" Binding="{Binding Path=Price}">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding Path=Price}"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
有时如果值无效,我只显示 -
。如果绑定 属性 的值为 -
.
我可以通过添加一个圆圈来做到这一点,该圆圈的可见性绑定到一个新的 属性 以检查价格是否无效,而上述文本显示则相反。但问题是,这将要求我为每个 属性 创建新的绑定,而我试图避免这种情况。
这是否可以通过触发器实现,或者有更好的方法吗?
文本值转换器
一种方法是创建一个 returns 其参数的值转换器,如果值不可用 (-
)。
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>
带有数据模板选择器的模板列
如果您需要最大的灵活性,您可以将模板列与数据模板结合使用 select 或者。请注意,数据网格模板列中存在限制,例如传递给列的 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>
自动数据模板选择
如您所见,数据模板select或方法非常死板和不灵活。类似的方法是 select 按类型自动生成数据模板。但是,要使其工作,您必须为 price 创建一个专用类型,并为 no price 创建一个类型。您可以参考 this related question 了解更多信息,因为 DataGrid
也有它的怪癖,正如您所期望的那样。
带有数据模板和触发器的模板列
最后,一个更 hackish 的解决方案是在数据模板中同时显示 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>