【问题标题】:Change WPF DataGrid column cell background color based on values根据值更改 WPF DataGrid 列单元格背景颜色
【发布时间】:2020-06-02 19:37:50
【问题描述】:

我正在尝试用特定列中的颜色填充单元格。列名是“NRO”,我想填充以 2 个黄色开始的单元格和以 3 个蓝色开始的单元格。我通过了这里给出的答案:Change DataGrid cell colour based on values

还尝试了其他几种方法,但都无法奏效。我也不明白如何在我的设置中实现它们中的任何一个。他们似乎都有<DataGridTextColumn Binding="{Binding Name}">。我的情况应该是什么?

这是我的 XAML:

<Window x:Class="DB_inspector_FilterTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="DB database inspector v.0.0.01" Height="600" Width="1000" Icon="logo_icon-small.jpg" Background="White">
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid x:Name="DataGrid1" Margin="0,103,0,0" Background="White" BorderBrush="#FF38853F"/>
        <TextBox x:Name="NameSearch" HorizontalAlignment="Left" Height="20" Margin="22,41,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="437" TextChanged="NameSearch_TextChanged"/>
        <Button Content="Load" Margin="640,41,0,0" Click="Button_Click_1" BorderBrush="{x:Null}" Foreground="White" Background="#FF55B432" Width="66" Height="29" HorizontalAlignment="Left" VerticalAlignment="Top"/>
        <ProgressBar x:Name="ProgressBar" HorizontalAlignment="Left" Height="11" VerticalAlignment="Top" Width="992" BorderBrush="{x:Null}" Background="{x:Null}"/>
        <Label Content="Customer name" HorizontalAlignment="Left" Height="25" Margin="22,11,0,0" VerticalAlignment="Top" Width="154"/>
        <CheckBox x:Name="ActiveCustomer" Content="Active" HorizontalAlignment="Left" Height="24" Margin="486,63,0,0" VerticalAlignment="Top" Width="86" Click="ActiveCustomer_Click_1"/>
        <CheckBox x:Name="Only" Content="Leave only good" HorizontalAlignment="Left" Height="17" Margin="486,41,0,0" VerticalAlignment="Top" Width="115" Click="CheckBox_Click"/>
        <Image Margin="856,0,22,520" VerticalAlignment="Bottom" Source="logo_small.jpg" Height="27"/>

    </Grid>
</Window>

补充:

如果有人有时间,虽然我会自己弄清楚,但请给我一些提示如何继续我的申请,这是我的完整代码:

using System.Data.Odbc;
using System.Windows;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;

namespace DB_inspector_FilterTest
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            try
            {
                ProgressBar.IsIndeterminate = true;

                DataGrid1.ItemsSource = await GetDataAsync();

                ProgressBar.IsIndeterminate = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private Task<DataView> GetDataAsync()
        {
            return Task.Run(() =>
            {

                string connectionStringDE = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSSE;Uid=ADMIN;Pwd=123;";

                string queryStringDE = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                string connectionStringFR = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSFI;Uid=ADMIN;Pwd=123;";

                string queryStringFR = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                DataTable dataTable = new DataTable("COMPANY");
                // using-statement will cleanly close and dispose unmanaged resources i.e. IDisposable instances
                using (OdbcConnection dbConnectionDE = new OdbcConnection(connectionStringDE))
                {
                    dbConnectionDE.Open();
                    OdbcDataAdapter dadapterDE = new OdbcDataAdapter();
                    dadapterDE.SelectCommand = new OdbcCommand(queryStringDE, dbConnectionDE);

                    dadapterDE.Fill(dataTable);

                }
                using (OdbcConnection dbConnectionFR = new OdbcConnection(connectionStringFR))
                {
                    dbConnectionFR.Open();
                    OdbcDataAdapter dadapterFR = new OdbcDataAdapter();
                    dadapterFR.SelectCommand = new OdbcCommand(queryStringFR, dbConnectionFR);

                    var newTable = new DataTable("COMPANY");
                    dadapterFR.Fill(newTable);

                    dataTable.Merge(newTable);

                }

                return dataTable.DefaultView;

            });
        }

        private Dictionary<string, string> _conditions = new Dictionary<string, string>();

        private void UpdateFilter()
        {
            try
            {
                var activeConditions = _conditions.Where(c => c.Value != null).Select(c => "(" + c.Value + ")");
                DataView dv = DataGrid1.ItemsSource as DataView;
                dv.RowFilter = string.Join(" AND ", activeConditions);
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        private void NameSearch_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            string filter = NameSearch.Text;
            if (string.IsNullOrEmpty(filter))
                _conditions["name"] = null;
            else
                _conditions["name"] = string.Format("NAME Like '%{0}%'", filter);
            UpdateFilter();
        }

        private void ActiveCustomer_Click_1(object sender, RoutedEventArgs e)
        {
            if (ActiveCustomer.IsChecked == true)
            {
                _conditions["active"] = string.Format("ACTIVE Like '%{0}%'", "1");
                UpdateFilter();
            }
            else
            {
                _conditions["active"] = null;
                UpdateFilter();
            }
        }

        private void CheckBox_Click(object sender, RoutedEventArgs e)
        {
            if (OnlyFIandSE.IsChecked == true)
            {
                _conditions["onlyfrandde"] = string.Format("NRO Like '2%' OR NRO Like '3%'");
                UpdateFilter();
            }
            else
            {
                _conditions["onlyfrandde"] = null;
                UpdateFilter();
            }
        }
    }
}

至少现在我不明白的事情:在我的情况下,我应该如何设置 ItemSource 进行绑定?我应该先将数据库导入列表然后绑定到列表吗?


尝试 3:

这是我最近的 MVVM 尝试。

C#:

using System;
using System.ComponentModel;
using System.Data;
using System.Data.Odbc;
using System.Windows;
using System.Windows.Input;

namespace DB_inspector
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

    }

    public class ViewModel : INotifyPropertyChanged
    {
        public ICommand myCommand => new RelayCommand(obj =>
        {
            try
            {
                string connectionStringDE = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSSE;Uid=ADMIN;Pwd=123;";

                string queryStringDE = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                string connectionStringFR = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSFI;Uid=ADMIN;Pwd=123;";

                string queryStringFR = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                DataTable dataTable = new DataTable("COMPANY");
                // using-statement will cleanly close and dispose unmanaged resources i.e. IDisposable instances
                using (OdbcConnection dbConnectionDE = new OdbcConnection(connectionStringDE))
                {
                    dbConnectionDE.Open();
                    OdbcDataAdapter dadapterDE = new OdbcDataAdapter();
                    dadapterDE.SelectCommand = new OdbcCommand(queryStringDE, dbConnectionDE);

                    dadapterDE.Fill(dataTable);

                }
                using (OdbcConnection dbConnectionFR = new OdbcConnection(connectionStringFR))
                {
                    dbConnectionFR.Open();
                    OdbcDataAdapter dadapterFR = new OdbcDataAdapter();
                    dadapterFR.SelectCommand = new OdbcCommand(queryStringFR, dbConnectionFR);

                    var newTable = new DataTable("COMPANY");
                    dadapterFR.Fill(newTable);

                    dataTable.Merge(newTable);

                }

                _ = dataTable.DefaultView;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        });

        private bool _allowUIChanges = true;
        public bool AllowUIChanges
        {
            get => _allowUIChanges;
            set
            {
                _allowUIChanges = value;
                OnPropertyChanged(nameof(AllowUIChanges));
                OnPropertyChanged(nameof(IsReadOnlyDataGrid));
            }
        }

        private void OnPropertyChanged(string v)
        {
            throw new NotImplementedException();
        }

        public bool IsReadOnlyDataGrid
        {
            get => !_allowUIChanges;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
        public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);

        public void Execute(object parameter) => _execute(parameter);
    }
}

XAML:

<Window x:Class="DB_inspector.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="DB database inspector" Height="595.404" Width="1005.571">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <DataGrid IsReadOnly="{Binding IsReadOnlyDataGrid}" AutoGenerateColumns="False" ItemsSource="{Binding MyCollection}" Width="998" Margin="0,98,0,0" >
        </DataGrid>
        <Image Height="41" Margin="0,21,10,0" Width="141" Source="logo_small.jpg" HorizontalAlignment="Right" VerticalAlignment="Top"/>
        <Button Content="Go" Command="{Binding myCommand}" Width="80" Height="30" Margin="48,42,870,492"/>

    </Grid>
</Window>

有什么建议这里还有什么问题吗?没有错误,但按钮不处理任何内容。

【问题讨论】:

    标签: c# wpf datagrid


    【解决方案1】:

    我建议为 Cell 的 Background 属性绑定 IValueConverterIMultiValueConverter

    我不确定它如何与自动生成的列集一起使用,但手动它看起来像这样。我在这里提供的不是工作副本,而是一个标记示例。

    XAML

    <Window.Resources>
        <local:MyCellBackgroundConverter x:Key="myCellBackgroundConverter"/>
    </Window.Resources>
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
    <!-- some your markup here -->
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyCollection}">
        <DataGridTextColumn Header="Column1" Binding="{Binding Value1}">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="Background">
                        <Setter.Value>
                            <MultiBinding Converter="{StaticResource myCellBackgroundConverter}">
                                <Binding Path="Value1"/>
                                <Binding Path="Value2"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                </Style>
            </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>
        <DataGridTextColumn Header="Column2" Binding="{Binding Value2}"/>
    </DataGrid>
    </Grid>
    

    ViewModel 类

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    
    // ...
    
    public class ViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<MyItem> _myCollection = new ObservableCollection<MyItem>();
        public ObservableCollection<MyItem> MyCollection
        {
            get => _myCollection;
            set
            {
                _myCollection = value;
                OnPropertyChanged(nameof(MyCollection));
            }
        }
    
        public ViewModel()
             // you may load or add the data to MyCollection here
        {
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    物品

    using System.ComponentModel;
    
    // ...
    
    public class MyItem : INotifyPropertyChanged
    {
        private string _value1 = string.Empty;
        private string _value2 = string.Empty;
    
        public string Value1
        {
            get => _value1;
            set { _value1 = value; OnPropertyChanged(nameof(Value1)); }
        }
    
        public string Value2
        {
            get => _value2;
            set { _value2 = value; OnPropertyChanged(nameof(Value2)); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    最后是转换器

    using System;
    using System.Globalization;
    using System.Windows.Data;
    using System.Windows.Media;
    
    //...
    
    public class MyCellBackgroundConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values[0] is string value1 && values[1] is string value2)
            {
                if (value1.Length > 0)
                {
                    return Brushes.Green;
                }
                else
                if (value2.Length > 0)
                {
                    return Brushes.Yellow;
                }
                else
                    return Brushes.Red;
            }
            else return Brushes.White;
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => null;
    }
    

    您也可以在 XAML 中直接使用 Style.DataTriggers

    有关绑定和属性的更多信息,请查找 MVVM 编程模式。简而言之,您不再需要x:Name,因为在 MVVM 模式中,您只能与 ViewModel 类数据实例进行交互,而不能直接与那里的控制进行交互(这很好)。同时,Contols 会自动与绑定到它们的数据同步。在此处调用 OnPropertyChanged("PropertyName") 只会导致 GUI 刷新。

    关于 XAML 示例的标记,请尝试将控制组包装在 StackPanel 中并了解它。这将节省您与利润作斗争的时间。只需在 Window 的 Grid 中设置几个列和/或行,然后将 StackPanels 放置在那里,并为它们分配 Grid.ColumnGrid.Row


    补充:

    在我的情况下,我应该如何设置 ItemSource 进行绑定?我应该先将数据库导入列表然后绑定到列表吗?

    ObservableCollection&lt;&gt;List&lt;&gt; 相同,您可以以相同的方式使用它。第一个实现了CollectionChanged 事件的区别,如果从集合中添加或删除了任何项目,则通知 DataGrid。

    您的 Button.Click 事件处理程序包含多余的 async/await 声明。

    让我们继续前进,看看如何使用 MVVM 来完成。

    XAML

    <Button Content="Go" Command="{Binding myCommand}"/>
    

    Command 必须实现 ICommand 接口。您必须做两件事才能正确实施:

    1) 添加RelayCommand单独的实现ICommand接口的类

    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
        public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    
        public void Execute(object parameter) => _execute(parameter);
    }
    

    2) 将 Command 实例添加到您的 ViewModel 类中

    public ICommand myCommand => new RelayCommand(obj =>
    {
        // do the same here as in Button.Click above
    });
    

    接下来,您可能需要一些阻止用户在加载数据时执行任何操作的 UI。

    视图模型

    private bool _allowUIChanges = true;
    public bool AllowUIChanges
    {
        get => _allowUIChanges;
        set
        {
            _allowUIChanges = value;
            OnPropertyChanged(nameof(AllowUIChanges));
            OnPropertyChanged(nameof(IsReadOnlyDataGrid));
        }
    }
    public bool IsReadOnlyDataGrid
    {
        get => !_allowUIChanges;
    }
    

    最后将你的 Control 属性绑定到它

    XAML

    <Button Content="Go" Command="{Binding myCommand}" Enabled="{Binding AllowUIChanges}"/>
    <DataGrid IsReadOnly="{Binding IsReadOnlyDataGrid}" AutoGenerateColumns="False" ItemsSource="{Binding MyCollection}">
    

    然后在加载数据时将AllowUIChanges 设置为false

    【讨论】:

    • 谢谢你!现在我必须了解如何在我的设置中实现这一点。我看到的最大挑战又是ItemsSource="{Binding MyCollection}。我想我必须为此目的重组我的代码,因为我正在设置 ItemSource 并删除 x:Name
    • 不客气。我为您的答案添加了另一个提示。也使用它。
    • 我会将其标记为付出努力的答案。但是我无法让这个工作,也许我必须尝试纯 XAML 解决方案。我已经用最终得到的代码编辑了我的问题。
    • 您在我的上述回答中遗漏了一件重要的事情。 ViewModel 是一个单独的类public class ViewModel : InotifyPropertyChanged,所有命令和属性都必须在那里。不在Window 类中。在完美的 MVVM 中,Window 类必须为空,就像在刚刚创建的项目中一样。除了在Window.LoadedWindow.Closing 事件上做一些事情。要从 Window 类访问 ViewModel,请使用:(this.DataContext as ViewModel).SomeProperty.
    • 我回来让 MVVM 模型工作,因为这将是我在 C# 开发中的下一步。好像我几乎明白了,但现在它说CustomCommand - 方法必须有一个返回类型。应该有 RelayCommand 因为 Class 是 RelayCommand?
    猜你喜欢
    • 2011-04-03
    • 1970-01-01
    • 2018-09-26
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 2015-05-14
    • 2013-01-28
    • 2011-07-29
    相关资源
    最近更新 更多