Как установить фон ячейки таблицы данных во время события 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;