簡體   English   中英

根據值更改 WPF DataGrid 列單元格背景顏色

[英]Change WPF DataGrid column cell background color based on values

我正在嘗試在特定列中用顏色填充單元格。 列名是“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>

任何建議這里還有什么問題? 沒有錯誤,但按鈕不處理任何事情。

我建議為 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<>List<>相同,您可以以相同的方式使用它。 區別在於第一個實現了CollectionChanged事件,該事件通知 DataGrid 是否從集合中添加或刪除了任何項目。

您的Button.Click事件處理程序包含冗余的 async/await 聲明。

讓我們繼續前進,看看如何使用MVVM來完成。

XAML

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

命令必須實現 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
});

接下來,您可能需要一些阻止用戶界面的東西,以防止用戶在加載數據時進行任何操作。

視圖模型

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM