我有一个数据网格,在绑定到数据表后输出90行。行值为1-90递增。
我想要实现的目标:
如果在数组/范围/数字列表中找到该数字,则将其突出显示为绿色。
我设法让它根据1值突出显示单元格,但是如果在范围内找到它们,我想要突出显示几个单元格。
<DataGrid Name="grid" ItemsSource="{Binding}" Height="300" Width="900"
AutoGenerateColumns="False"
VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Center" VerticalAlignment="Top" RowHeight="40">
<DataGrid.Resources>
<Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="1">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Number}"
ElementStyle="{StaticResource BackgroundColourStyle}" MinWidth="40">
</DataGridTextColumn>
</DataGrid.Columns>
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
</DataGrid>
你可以看到我有一个值为1的触发器属性。这样可以正常工作,但是如上所述,如果一个单元格在后端的c#中设置的范围内,然后将其高亮显示为绿色,我该如何更改呢?
绑定到数据表:
calledGrid.DataContext = calledNumbers.DefaultView;
数据表的实际制作:
DataSet dataSet = new DataSet("myDS");
this.bingoCalls(dataSet);
DataTable numbersTable = new DataTable("Numbers");
numbersTable.Columns.Add("Number", typeof(Int32));
for (int i = 1; i < 91; i++)
{
numbersTable.Rows.Add(i);
}
dataSet.Tables.Add(numbersTable);
感谢您的帮助。如果您需要更多信息,或者我一直在模糊或不清楚任何事情,请询问,我会尽我所能尽快回复。也请原谅我可能有的任何无知,我对wpf很新。我会尽我所能。
答案 0 :(得分:3)
您可以使用IMultiValueConverter
来实现,如果Text位于范围内,则返回true,否则返回false。 Based on the value returned by converter, change background color
。
将三个参数传递给转换器:
<强>转换器强>
public class ItemExistInRangeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
bool itemsExistInRange = false;
if (values.Length == 3)
{
int outputValue = int.MinValue;
if (Int32.TryParse(values[0].ToString(), out outputValue))
{
int minValue = (int)values[1];
int maxValue = (int)values[2];
itemsExistInRange = minValue <= outputValue
&& outputValue <= maxValue;
}
}
return itemsExistInRange;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<强> XAML 强>
<DataGrid.Resources>
<local:ItemExistInRangeConverter x:Key="ItemExistInRangeConverter"/>
<sys:Int32 x:Key="MinimumValue">1</sys:Int32>
<sys:Int32 x:Key="MaximumValue">50</sys:Int32>
<Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ItemExistInRangeConverter}">
<Binding Path="Text" RelativeSource="{RelativeSource Self}"/>
<Binding Source="{StaticResource MinimumValue}"/>
<Binding Source="{StaticResource MaximumValue}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
确保在根级别添加相应的命名空间:
xmlns:local="clr-namespace:NamespaceOfConverter"
// Replace NamespaceOfConverter with namespace where converter resides.
xmlns:sys="clr-namespace:System;assembly=mscorlib"
更新(如果想要查找数组中的项目)
假设您的数字数组属性存在于代码隐藏或视图模型类中。
首先,您需要set ItemsSource of DataGrid to DataTable
而不是DataContext。
其次,set DataContext of DataGrid to this from code behind or an instance of viewModel class
。
此外,要刷新GUI,您的类应该实现INotifyPropertyChanged接口。
现在,假设您有一个属性说:
public int[] RangeNumbers { get; set; }
默认情况下,它将包含您要突出显示的数字列表。
XAML将如下所示:
<DataGrid.Resources>
<local:ItemExistInRangeConverter x:Key="ItemExistInRangeConverter"/>
<Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ItemExistInRangeConverter}">
<Binding Path="Text" RelativeSource="{RelativeSource Self}"/>
<Binding Path="DataContext.RangeNumbers"
RelativeSource="{RelativeSource FindAncestor,
AncestorType=DataGrid}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
转换器代码:
public class ItemExistInRangeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
bool itemsExistInRange = false;
if (values.Length == 2)
{
int outputValue = int.MinValue;
int[] rangeNumbers = (int[])values[1];
if (rangeNumbers != null &&
Int32.TryParse(values[0].ToString(), out outputValue))
{
itemsExistInRange = rangeNumbers.Contains(outputValue);
}
}
return itemsExistInRange;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
现在,项目将根据RangeNumbers
中的初始数字突出显示。但是假设您之后更新了数组,则需要引发属性更改事件,以便GUI刷新如下:
RangeNumbers = new int[] { 23, 45, 47, 69 };
OnPropertyChanged("RangeNumbers");
答案 1 :(得分:3)
这是一个如何通过使用MVVM模式(如果编辑/更改任何数字或突出显示更改的条件也可以工作)和使用MultiBinding的样式设置器来实现此目的的示例:
<强> MainWindowModel.cs 强>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
namespace WpfApplication
{
class MainWindowModel : INotifyPropertyChanged
{
private DataTable numbersTable;
private Predicate<int> highlightCriteria;
public event PropertyChangedEventHandler PropertyChanged;
public DataTable NumbersTable
{
get { return this.numbersTable; }
set { this.SetValue(ref this.numbersTable, value, "NumbersTable"); }
}
public Predicate<int> HighlightCriteria
{
get { return this.highlightCriteria; }
set { this.SetValue(ref this.highlightCriteria, value, "HighlightCriteria"); }
}
public MainWindowModel()
{
this.NumbersTable = new DataTable("Numbers")
{
Columns =
{
{ "Number", typeof(int) }
},
};
for (int i = 1; i < 91; i++)
this.NumbersTable.Rows.Add(i);
// By default only numbers larger than 10 and lower than 50 will be highlighted.
this.HighlightCriteria = num => num > 10 && num < 50;
}
protected void SetValue<T>(ref T field, T value, string propertyName)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, e);
}
}
}
<强> NumberTextToBackgroundConverter.cs 强>
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace WpfApplication
{
class NumberTextToBackgroundConverter : IMultiValueConverter
{
public static readonly IMultiValueConverter Instance = new NumberTextToBackgroundConverter();
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int number;
var numberText = values[0] as string;
if (!string.IsNullOrEmpty(numberText) && int.TryParse(numberText, NumberStyles.Integer, CultureInfo.InvariantCulture, out number))
{
var highlightCriteria = values[1] as Predicate<int>;
if (highlightCriteria != null && highlightCriteria(number))
return Brushes.LightGreen;
}
return Brushes.Transparent;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
<强> MainWindow.xaml 强>
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication">
<Window.DataContext>
<local:MainWindowModel/>
</Window.DataContext>
<DataGrid ItemsSource="{Binding NumbersTable}">
<DataGrid.Resources>
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{x:Static local:NumberTextToBackgroundConverter.Instance}">
<Binding RelativeSource="{RelativeSource Self}" Path="Content.Text"/>
<Binding RelativeSource="{RelativeSource AncestorType=DataGrid}" Path="DataContext.HighlightCriteria"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
</Window>
答案 2 :(得分:0)
您必须使用条件转换器。请参阅this article。
答案 3 :(得分:0)
一种做法是这样的:
假设您将行绑定到名为Items的属性,这是一个List的东西。您应该像建议的那样创建像“IsHighlighted”这样的属性,并且您的代码应根据用户输入将其设置为true或false。
然后,要实际突出显示该行,您应该在DataGrid中附加一个LoadingRow事件,如:
MyDataGrid.LoadingRow
+= new EventHandler<System.Windows.Controls.DataGridRowEventArgs>(myMethod);
然后在myMethod中做这样的事情:
void myMethod(object sender, System.Windows.Controls.DataGridRowEventArgs e)
{
RowClass item = (RowClass)e.Row.Item;
if (item.IsHighlighted)
{
e.Row.Background = new SolidColorBrush(Colors.Green);
}
else
{
e.Row.Background = new SolidColorBrush(Colors.White);
}
}
答案 4 :(得分:0)
我会抛出这个选项,可能更简单......你已经拥有一个包含1-90行的列的数据表。为什么不将另一列添加到数据表中作为“ShowColor”列。对于所有行上的任何条件,将列表一次爆破,并将背景颜色绑定到“ShowColor”的背景颜色。然后,不需要特殊的“转换器”。
另外,你指的是要为许多细胞着色,而这一点有点模糊。你的意思是在一行可能暴露10列,你想要第1行,第1,2,5,8,10列的单元格显示为绿色(不太可能)?或者......对于符合条件的ENTIRE Row 1,您希望该行的所有列都是绿色背景。将出现两个完全不同的答案。
答案 5 :(得分:0)
我不是真正的WPF程序员,但下面的示例应该可以轻松转换,因为这些概念与Windows Forms相同,可以执行您需要的操作。以下示例在启动时完成以下操作:
DataSet
。m_dataSet
DataTable
添加名为Numbers
的两列DataSet
,并将其加载样本数据。
Value
和Active
。int
和bool
。Numbers
表格指定为DataSource
控件的DataGridView
。这会导致控件自动添加Numbers
表中的两列。因此,之后,dataGridView1.Columns["Active"].Visible
属性的值将设置为false。这样,Numbers
表可以绑定到DataGridView
控件,而不会向用户显示隐藏的列。显示的列数取决于您。dataGridView1_CellClick
- 这只是提供一种简单的方法来选择随机单元格并证明单元格BackColor
选择发生了变化。这是数据源中的“活动”列在true和false之间切换的位置。dataGridView1_CellFormatting
- 每次单元格需要格式化显示时,都会抛出(在Windows窗体中无论如何)。通过处理此事件来分配Cell.BackColor
,我们不必遍历每一行来刷新颜色,因为Windows已经为任何无效的单元格执行了此操作。更新BackColor
立即bofore细胞被绘制。当手动调用Invalidate
时,或者当你没有发生偶然的重绘事件时,它同样有效。button1_Click
- 最后,它显示了如何清除DataGridView
控件中的所有选定项目。
public Form1() {
InitializeComponent();
LoadGridData();
}
private DataSet m_dataSet = new DataSet("myDS");
private void LoadGridData() {
//--------------------------------------------------
// Build Numbers Table
//--------------------------------------------------
DataTable numbersTable = new DataTable("Numbers");
numbersTable.Columns.Add("Value", typeof(Int32));
numbersTable.Columns.Add(new DataColumn(
"Active",
typeof(bool)) {
DefaultValue = false
});
for (int i = 1; i < 91; i++) {
numbersTable.Rows.Add(i);
}
m_dataSet.Tables.Add(numbersTable);
dataGridView1.DataSource = m_dataSet.Tables["Numbers"];
dataGridView1.Columns["Active"].Visible = false;
}
private void button1_Click(object sender, EventArgs e) {
// Reset all Active settings to false.
foreach (DataRow row in m_dataSet.Tables["Numbers"].Rows) {
row["Active"] = false;
}
dataGridView1.Invalidate();
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex > -1 && e.ColumnIndex == 0) {
int cellValue = (int)dataGridView1.Rows[e.RowIndex].Cells[0].Value;
//---------------------------------------------------
// Create a query that selects any rows that have the
// cellValue in the "Value" field. This will only return
// one row as long as long as you Numbers table stays
// unique. You have to query for the number because
// e.RowIndex returns the wrong row when sorted descending.
//---------------------------------------------------
var query =
m_dataSet.Tables["Numbers"].AsEnumerable()
.Where(row => row.Field<int>("Value") == cellValue);
// Update Active field
foreach(var row in query)
row["Active"] = (bool)row["Active"] == false;
}
// Take focus off the control to avoid dark blue
// selection color. -- could also set default backcolor
// to forecolor to create the obvious selection immediately
// when a cell gets clicked.
dataGridView1.ClearSelection();
}
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
DataRowView drv = (DataRowView)dataGridView1.Rows[0].DataBoundItem;
//---------------------------------------------------
// Set the BackColor of the cell based on the underlying
// data's Active setting.
//---------------------------------------------------
if (e.RowIndex < m_dataSet.Tables["Numbers"].Rows.Count) {
if ((bool)drv.DataView[e.RowIndex]["Active"] == true)
e.CellStyle.BackColor = Color.PowderBlue;
else
e.CellStyle.BackColor = SystemColors.Window;
}
}
以下是Windows窗体中的输出内容。我希望这个答案可以帮助您解决问题。小心。
同样从我读过的内容中,您可以在WPF应用程序中托管Windows窗体控件。 DataGridView具有DataGrid控件的显着性能优势。特别是对于大型数据集。
答案 6 :(得分:0)
如果您只使用DataTrigger
,这应该很简单。使用IValueConverter
根据行返回true / false值。您可以对转换器中包含的数字的列表/范围进行硬编码,也可以将它们作为参数从XAML传入:
public class HighlightConverter : IValueConverter
{
private int[] _highlightedNumbers = { 1, 5, 12, 15 };
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value as int? == null)
return false;
// get the highlighted numbers, either hard-coded locally, or passed in as the ConverterParameter
int[] highlightedNumbers;
var numbersCsv = parameter as string;
if (!string.IsNullOrEmpty(numbersCsv))
highlightedNumbers = numbersCsv.Split(',').Select(item => int.Parse(item.Trim())).ToArray();
else
highlightedNumbers = _highlightedNumbers;
return highlightedNumbers.Contains((int)value) ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
现在只需根据行值应用DataTrigger
,使用此转换器检查突出显示条件:
<DataGrid.Resources>
<my:HighlightConverter x:Key="my:HighlightConverter" />
<Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource my:HighlightConverter},ConverterParameter='1,5,12,15'}" Value="True">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
修改(替代方案)
如果您在编译时知道突出显示的数字列表,则上述操作将起作用。或者,如果列表基于另一个运行时值,那么您将需要MultiBinding
。这个想法是类似的,但这次转换器将应用两个值:允许列表和行值本身:
public class HighlightConverter : IValueConverter, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var includedNumbers = values[0] as int[];
if (includedNumbers == null || values[1] as int? == null)
return false;
return includedNumbers.Contains((int)values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
现在您只需绑定到包含突出显示数字列表的属性即可。假设您当前Window的DataContext是一个具有int[] HighlightedNumbers
属性的视图模型。然后,您可以使用RelativeSource
绑定。结果看起来像这样:
<DataGrid.Resources>
<converter:HighlightConverter x:Key="HighlightConverter" />
<Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource HighlightConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="DataContext.HighlightedNumbers" />
<Binding />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>