c# - 根据值更改 WPF DataGrid 列单元格背景颜色
问题描述
我正在尝试在某些列中用颜色填充单元格。列名是“NRO”,我想填充以 2 黄色开始的单元格和以 3 蓝色开始的单元格。我通过了这里给出的答案:Change DataGrid cell color 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>
有什么建议这里还有什么问题吗?没有错误,但按钮不处理任何内容。
解决方案
我建议IValueConverter
或IMultiValueConverter
绑定 Cell 的 Background 属性。
我不确定它如何与自动生成的列集一起使用,但手动它看起来像这样。我在这里提供的不是工作副本,而是一个标记示例。
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>
视图模型类
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;
}
以另一种方式,您可以Style.DataTriggers
直接在 XAML 中使用。
有关绑定和属性的更多信息,请查找MVVM编程模式。简而言之,您不再需要x:Name
了,因为在MVVM模式中,您只能与 ViewModel 类数据实例进行交互,而不能直接与那里的控制进行交互(这很好)。同时,Contols 会自动与绑定到它们的数据同步。在此处调用OnPropertyChanged("PropertyName")
只会导致 GUI 刷新。
关于您的 XAML 示例的标记,请尝试将控制组包装进去StackPanel
并了解它。这将节省您与利润作斗争的时间。只需在窗口中设置几个列和/或行,然后Grid
将 StackPanels 分配给它们。Grid.Column
Grid.Row
添加:
在我的情况下,我应该如何设置 ItemSource 进行绑定?我应该先将数据库导入列表然后绑定到列表吗?
ObservableCollection<>
相同List<>
,您可以以相同的方式使用它。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
同时加载数据。
推荐阅读
- java - 我需要帮助来使用 im4java API 将 png 图像的白色背景替换为透明,而不是使用命令行
- oracle - 在一个字符串中查找源 na 替换参数并在另一个字符串中执行替换
- r - 如何在 for 循环中索引 R 对象?
- virtualization - 虚拟化技术如何关闭操作系统?
- json - 使用 Codable 在一个模型类中解码两个不同的 JSON 响应
- java - 找到将出现在四种不同场景中的元素
- python - 重塑numpy数组
- android - 在过渡期间缩放第一个活动时更改黑色
- c++ - C ++,从堆中插入/删除时对象的属性消失
- r - 如何通过R中的矩阵从向量列表中提取向量