Как установить фон ячейки таблицы данных во время события AutoGeneratingColumn в зависимости от его значения?

Я все еще борюсь с манипуляциями с клеточным фоном, поэтому я задаю новый вопрос.

Пользователь "HB" написал, что я могу на самом деле установить стиль ячейки во время события AutoGeneratingColumn - изменить цвет ячейки DataGrid на основе значений. Проблема в том, что я не уверен, как это сделать.

Что я хочу: установить разные цвета фона для каждой ячейки в зависимости от ее значения. Если значение null Я также хочу, чтобы он не был кликабельным (я думаю, фокусируемым).

Что я имею / я пытаюсь сделать:

private void mydatagrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    foreach (Cell cell in e.Column)
    {
        if (cell.Value < 1)
        { 
            cell.Background = Color.Black; 
            cell.isFocusable = false; 
        } 
        else
        {
            cell.Background = Color.Pink;
        }
    }
}

Это просто псевдокод. Возможно ли что-то подобное во время автогенерации столбцов, и если да, то как я могу отредактировать мой код, чтобы он был действительным?

Я читал о преобразователях значений, но я хочу знать, возможно ли это каким-то образом программно, без написания XAML.

Пожалуйста, поймите, что я все еще новичок в C#/WPF/DataGrid.

Решение часть1:

Я использовал ответ, который я принял. Просто поместите это в

<Window.Resources> 
<local:ValueColorConverter x:Key="colorConverter"/>
        <Style x:Key="DataGridCellStyle1" TargetType="{x:Type DataGridCell}"> 
            <Setter Property="Padding" Value="5"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridCell}">
                        <Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
                            <Border.Background>
                                <MultiBinding Converter="{StaticResource colorConverter}">
                                    <Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="Content.Text"/>
                                    <Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="IsSelected"/>
                                </MultiBinding>
                            </Border.Background>
                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
</Window.Resources>

И сделал для этого MultiBinding конвертер, поэтому я также могу установить цвет фона для выбранных ячеек.

Проблема:

Теперь мне остается только решить проблему установки фокуса пустых ячеек. Есть намеки?

  <Style.Triggers>
        <Trigger Property="HasContent" Value="False">
            <Setter Property="Focusable" Value="False"/>
        </Trigger>
    </Style.Triggers>

Это не работает У меня были пустые строки в пустых ячейках, но они заполнены нулями, так что это должно работать, верно? Или что я делаю не так:|?

Решение часть 2:

Поэтому приведенный выше код не будет работать до тех пор, пока значение ячейки является "TextBox", поэтому я решил найти другой способ справиться с ним, который можно найти в моем ответе здесь: /questions/29124404/datagrid-tsvet-fona-otklyuchenie-vyideleniya-v-zavisimosti-ot-znacheniya-yachejki-ispolzovanie-triggera/29124417#29124417

Спасибо за попытку помочь мне:)

3 ответа

Решение

Я могу предложить два разных решения для вашего вопроса: первое - это "код за стилем" (который вы просите, но лично я думаю, что это не правильный подход в WPF) и более WPF-стиль (который более хитрый, но сохраняет код - за чистотой и использованием стилей, триггеров и конвертеров)

Решение 1. Обработка событий и логика кода для раскраски

Прежде всего, выбранный вами подход не будет работать напрямую - событие AutoGeneratingColumn предназначено для изменения внешнего вида столбца, а не для каждой ячейки. Так что его можно использовать, скажем, для прикрепления правильного стиля ко всему столбцу на основе его отображаемого индекса или привязанного свойства.

Вообще говоря, при первом событии в вашей таблице данных не будет вообще никаких строк (и, следовательно, ячеек). Если вам действительно нужно перехватить событие - рассмотрите вместо этого событие DataGrid.LoadingRow. И вы не сможете получить клетки так просто:)

Итак, что вы делаете: обрабатываете событие LoadingRow, получаете строку (она имеет свойство Item, которое содержит (удивительно:)) ваш связанный элемент), получаете связанный элемент, производите все необходимые вычисления, получаете ячейку, которую нужно изменить, и наконец, измените стиль целевой ячейки.

Вот код (в качестве элемента я использую образец объекта со свойством int "Значение", которое я использую для раскраски).

XAML

   <DataGrid Name="mygrid" ItemsSource="{Binding Items}" AutoGenerateColumns="True" LoadingRow="DataGrid_OnLoadingRow"/>

.cs

    private void DataGrid_OnLoadingRow(object sender, DataGridRowEventArgs e)
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => AlterRow(e)));
    }

    private void AlterRow(DataGridRowEventArgs e)
    {
        var cell = GetCell(mygrid, e.Row, 1);
        if (cell == null) return;

        var item = e.Row.Item as SampleObject;
        if (item == null) return;

        var value = item.Value;

        if (value <= 1) cell.Background = Brushes.Red;
        else if (value <= 2) cell.Background = Brushes.Yellow;
        else cell.Background = Brushes.Green;
    }

    public static DataGridRow GetRow(DataGrid grid, int index)
    {
        var row = grid.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;

        if (row == null)
        {
            // may be virtualized, bring into view and try again
            grid.ScrollIntoView(grid.Items[index]);
            row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
        }
        return row;
    }

    public static T GetVisualChild<T>(Visual parent) where T : Visual
    {
        T child = default(T);
        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            var v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T ?? GetVisualChild<T>(v);
            if (child != null)
            {
                break;
            }
        }
        return child;
    }

    public static DataGridCell GetCell(DataGrid host, DataGridRow row, int columnIndex)
    {
        if (row == null) return null;

        var presenter = GetVisualChild<DataGridCellsPresenter>(row);
        if (presenter == null) return null;

        // try to get the cell but it may possibly be virtualized
        var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
        if (cell == null)
        {
            // now try to bring into view and retreive the cell
            host.ScrollIntoView(row, host.Columns[columnIndex]);
            cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
        }
        return cell;

    }

Решение 2. WPF-стиль

Это решение использует программный код только для преобразования значений в цвета (при условии, что логика раскраски более сложна, чем сравнение на равенство - в этом случае вы можете использовать триггеры и не связываться с конвертерами).

Что вы делаете: установите свойство DataGrid.CellStyle со стилем, который содержит триггер данных, который проверяет, находится ли ячейка в нужном столбце (на основе ее DisplayIndex), и, если это так, применяет фон через конвертер.

XAML

<DataGrid Name="mygrid" ItemsSource="{Binding Items}" AutoGenerateColumns="True">
        <DataGrid.Resources>
            <local:ValueColorConverter x:Key="colorconverter"/>
        </DataGrid.Resources>
        <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Column.DisplayIndex}" Value="1">
                        <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource colorconverter}}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.CellStyle>
    </DataGrid>

.cs

public class ValueColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var str = value as string;
        if (str == null) return null;

        int intValue;
        if (!int.TryParse(str, out intValue)) return null;

        if (intValue <= 1) return Brushes.Red;
        else if (intValue <= 2) return Brushes.Yellow;
        else return Brushes.Green;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

UPD: если вам нужно раскрасить всю сетку данных, XAML намного проще (не нужно использовать триггеры). Используйте следующий CellStyle:

    <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                 <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource colorconverter}}"/>
            </Style>
    </DataGrid.CellStyle>

Я имел в виду, что вы можете установить CellStyle Свойство столбца, вы не можете манипулировать ячейками напрямую, так как они недоступны в этом событии. Стиль может содержать вашу условную логику в виде DataTriggers (понадобится конвертер, так как у вас "меньше", а не равно) и Setters,

Также, если логика не специфична для столбцов, вы можете установить стиль глобально на самой сетке. Смысл использования события - манипулировать свойствами столбца, к которым вы не можете получить доступ в противном случае.

Я не уверен, доступно ли это свойство (Cell.Style) в вашей таблице данных WPF. Возможно, в вашем случае существует какая-то альтернатива. Это сработало для сетки данных WinForms.

 cell.Style.BackColor = System.Drawing.Color.Black;
Другие вопросы по тегам