如果找到则突出显示细胞

时间:2013-12-18 14:17:46

标签: c# wpf datagrid

我有一个数据网格,在绑定到数据表后输出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很新。我会尽我所能。

7 个答案:

答案 0 :(得分:3)

您可以使用IMultiValueConverter来实现,如果Text位于范围内,则返回true,否则返回false。 Based on the value returned by converter, change background color

将三个参数传递给转换器:

  1. 实际文字值。
  2. 最低价值。
  3. 最大值。
  4. <强>转换器

    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相同,可以执行您需要的操作。以下示例在启动时完成以下操作:

  1. 创建名为DataSet
  2. 的班级m_dataSet
  3. DataTable添加名为Numbers的两列DataSet,并将其加载样本数据。
    • 列名为ValueActive
    • 数据类型分别为intbool
  4. Numbers表格指定为DataSource控件的DataGridView。这会导致控件自动添加Numbers表中的两列。因此,之后,dataGridView1.Columns["Active"].Visible属性的值将设置为false。这样,Numbers表可以绑定到DataGridView控件,而不会向用户显示隐藏的列。显示的列数取决于您。
  5. 为以下内容创建事件处理程序:
    • dataGridView1_CellClick - 这只是提供一种简单的方法来选择随机单元格并证明单元格BackColor选择发生了变化。这是数据源中的“活动”列在true和false之间切换的位置。
    • dataGridView1_CellFormatting - 每次单元格需要格式化显示时,都会抛出(在Windows窗体中无论如何)。通过处理此事件来分配Cell.BackColor,我们不必遍历每一行来刷新颜色,因为Windows已经为任何无效的单元格执行了此操作。更新BackColor立即bofore细胞被绘制。当手动调用Invalidate时,或者当你没有发生偶然的重绘事件时,它同样有效。
    • button1_Click - 最后,它显示了如何清除DataGridView控件中的所有选定项目。
  6.     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窗体中的输出内容。我希望这个答案可以帮助您解决问题。小心。

    enter image description here

    同样从我读过的内容中,您可以在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>