Select cells if found

I have a datagrid that outputs 90 rows after binding to a datatable. The row values ​​are 1-90 ascending.

What I want to achieve:

If the number is found in the array / range / list of numbers, highlight it in green.

I managed to get it to select a cell based on 1 value, but I want to select multiple cells if they are in a range.

<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> 

you can see that I have a trigger property of value 1. This works fine, but as above, how to change it, so if the cell is in the range set in C # in the backend, then highlight it in green.

datatable binding:

 calledGrid.DataContext = calledNumbers.DefaultView; 

Actual data creation:

 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); 

Thanks for the help. If you need more information, or I was vague or unclear about anything, please ask and I will do my best to reply as soon as possible. Also please excuse the ignorance that I may have, I am very new to wpf. I will do everything possible.

+6
source share
7 answers

You can achieve this by using IMultiValueConverter , which will return true if Text is within the range, otherwise return false. Based on the value returned by converter, change background color .

Pass three parameters to the converter:

  • The value of the actual text.
  • Minimum value.
  • The maximum value.

Converter

 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> 

Make sure the appropriate namespace is added at the root level:

 xmlns:local="clr-namespace:NamespaceOfConverter" // Replace NamespaceOfConverter with namespace where converter resides. xmlns:sys="clr-namespace:System;assembly=mscorlib" 

UPDATE (If you want to find an element in an array of numbers)

Assuming a property of your array of numbers exists in the code behind or viewing the model class.

First of all you need to set ItemsSource of DataGrid to DataTable instead of DataContext.

Secondly, set DataContext of DataGrid to this from code behind or an instance of viewModel class .

In addition, to update the GUI, your class must implement the INotifyPropertyChanged interface .

Now suppose you have a property:

 public int[] RangeNumbers { get; set; } 

By default, it will contain a list of numbers that you want to highlight.

XAML will look like this:

 <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> 

Converter Code:

 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(); } } 

Elements will now be highlighted based on the starting numbers in RangeNumbers . But suppose that after that you update the array, you need to raise an event with a property change so that the GUI updates like this:

 RangeNumbers = new int[] { 23, 45, 47, 69 }; OnPropertyChanged("RangeNumbers"); 
+3
source

Here is an example of how to do this using the MVVM template (which will also work if you edit / change any number or criteria for highlighting changes) and Style Setter with 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> 
+3
source

You must use a conditional converter. See this article .

0
source

One way to do this:

Suppose you bind strings to the "Items" property, which is a list of something. You should create a property of type "IsHighlighted" as suggested, and your code should set it to true or false depending on user input.

Then, in order to actually highlight the row, you must attach the LoadRow event to the DataGrid, for example:

 MyDataGrid.LoadingRow += new EventHandler<System.Windows.Controls.DataGridRowEventArgs>(myMethod); 

then in myMethod do something like this:

 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); } } 
0
source

I would choose this option and could be even simpler ... You already have a column data table with rows 1-90. Why not add another column to the datatable as the "ShowColor" column. Break through the list once for any condition for all lines and the background color associated with the color "ShowColor". Then no special “converters” are required.

In addition, you turn to the desire to color many cells, and this point is a bit vague. You mean one row that can expose 10 columns, do you want the cells in row 1, columns 1, 2, 5, 8, 10 to be displayed in green (this is not likely)? Or ... for ENTIRE Row 1, which qualified the condition, you want ALL columns for this row to be green. Two completely different answers came up.

0
source

I'm not really a WPF programmer, but the example below should be easily convertible, because the concepts are the same as Windows Forms to do what you need. In the following example, the following is executed at startup:

  • Creates a class-level DataSet named m_dataSet .
  • Adds two columns of a DataTable called Numbers to a DataSet and loads it with sample data.
    • The columns are called Value and Active .
    • The data types are int and bool respectively.
  • Designates the Numbers table as the DataSource of the DataGridView control. This forces the control to automatically add both columns from the Numbers table. Therefore, after this, the value of the dataGridView1.Columns["Active"].Visible property is set to false. This allows you to bind the Numbers table to the DataGridView control without displaying hidden columns to the user. The number of columns that are displayed is up to you.
  • Creates event handlers for:
    • dataGridView1_CellClick - This simply provides an easy way to select random cells and confirm the BackColor cell selection. Here, the “Active” column in the data source switches between true and false.
    • dataGridView1_CellFormatting - this is thrown (in Windows Forms anyway) every time a cell needs formatting to display. Handling this event to assign Cell.BackColor , we do not need to Cell.BackColor over each row to update colors, because Windows already does this for any invalid cells. BackColor updated immediately when the cell was drawn. It works equally well when manually invoking Invalidate or when random redraw events occur that you did not call.
    • button1_Click - Finally, it shows how to clear all selected items in a DataGridView control.

  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 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; } } 

Here's what the output looks like in Windows Forms. Hope this answer helps you solve your problem. Be careful.

enter image description here

Also from what I read, you can host Windows Forms controls in WPF applications. DataGridView has significant performance advantages for the DataGrid control. Especially with large data sets.

0
source

This should be simple enough if you just use a DataTrigger . Use IValueConverter to return true / false based on the string. You can either adjust the list / range of included numbers in the converter, or pass them from XAML as a parameter:

 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(); } } 

Now just apply a DataTrigger based on the string value, using this converter to check the highlight condition:

 <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> 

Edit (alternative)

The above will work if you know the list of allocated numbers at compile time. Alternatively, if the list is based on a different runtime value, you will need MultiBinding . The idea is similar, but this time the converter will apply two values: an allowed list and a string value:

 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(); } } 

Now you just need to bind to a property containing a list of allocated numbers. Say your current WindowContext Window is a viewmodel with the int[] HighlightedNumbers property. Then you can use the RelativeSource binding. The result will look something like this:

 <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> 
0
source

All Articles