WPF datagrid: конвертер и StringFormat
у меня есть стандартная сетка данных (WPF toolkit). Некоторые из столбцов (которые явно определены) должны отображаться в процентах. Некоторые столбцы должны быть показаны красным цветом, если значения ниже 0. (Два набора столбцов не совпадают). Я попытался реализовать эти требования, используя StringFormat
и Style
, соответственно. Мой XAML:
<Window xmlns:local="clr-namespace:myNamespace"
xmlns:tk="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit">
<Window.Resources>
<local:ValueConverter x:Key="valueToForeground" />
<Style TargetType="{x:Type tk:DataGridCell}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource valueToForeground}}" />
</Style>
</Window.Resources>
<Grid>
<tk:DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Path=myClass/myProperty}">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="A"
Binding="{Binding colA}" />
<tk:DataGridTextColumn Header="B"
Binding="{Binding colB, StringFormat={0:P}}" />
<tk:DataGridTextColumn Header="C"
Binding="{Binding colC, StringFormat={0:P}}" />
<tk:DataGridTextColumn Header="D"
Binding="{Binding colD, StringFormat={0:P}}" />
</tk:DataGrid.Columns>
</tk:DataGrid>
</Grid>
</Window>
и соответствующий конвертер:
namespace myNamespace
{
public class ValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
if (value != null)
{
if (Double.TryParse(value.ToString(), out doubleValue))
{
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
}
}
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Я думаю, что это все довольно стандартно, но проблема в том, что конвертер получает Text
значение после того, как он проходил через StringFormat
, и в этот момент его трудно правильно разобрать (так как на самом деле не все столбцы имеют одинаковый формат). Если я достану StringFormats
преобразователь прекрасно работает и текст отображается красным цветом. Я упускаю что-то очевидное? Есть простой способ обойти это? Единственное, о чем я могу думать сейчас, это перемещение форматирования в другой конвертер, и я не уверен, что это сработает.
3 ответов
у нас была похожая ситуация, когда нам нужна другая Path
свойство Binding
но в остальном похож CellStyle
для каждого DataGridColumn
. Мы решили это с помощью custom MarkupExtension
. В вашем случае это выглядело бы так
<tk:DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding MyItems}">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="A"
Binding="{Binding colA}" />
<tk:DataGridTextColumn Header="B"
Binding="{Binding colB, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle PropertyName=colB}"/>
<tk:DataGridTextColumn Header="C"
Binding="{Binding colC, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle PropertyName=colC}"/>
<tk:DataGridTextColumn Header="D"
Binding="{Binding colD, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle PropertyName=colD}"/>
</tk:DataGrid.Columns>
</tk:DataGrid>
а то ForegroundCellStyleExtension
создает Style
на DataGridCell
в зависимости от PropertyName
ForegroundCellStyleExtension
public class ForegroundCellStyleExtension : MarkupExtension
{
public ForegroundCellStyleExtension() { }
public ForegroundCellStyleExtension(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
DependencyObject targetObject = service.TargetObject as DependencyObject;
if (targetObject == null)
{
return null;
}
Binding foregroundBinding = new Binding
{
Path = new PropertyPath(PropertyName),
Converter = new ValueConverter()
};
Style foregroundCellStyle = new Style(typeof(DataGridCell));
foregroundCellStyle.Setters.Add(new Setter(DataGridCell.ForegroundProperty, foregroundBinding));
return foregroundCellStyle;
}
}
кроме того, если у вас есть другие Setters
etc. что вы хотели бы используйте, тогда они могут быть включены другим параметром в MarkupExtension
.
<Window.Resources>
<Style x:Key="dataGridCellStyle" TargetType="{x:Type tk:DataGridCell}">
<Setter Property="Background" Value="Blue"/>
</Style>
</Window.Resources>
<!-- ... -->
<tk:DataGridTextColumn Header="B"
Binding="{Binding colB, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle colB, {StaticResource dataGridCellStyle}}"/>
и ForegroundCellStyleExtension
затем будет использовать второй параметр как BasedOn
на DataGridCell
Style
передний план Cellstyleextension с BasedOn
public class ForegroundCellStyleExtension : MarkupExtension
{
public ForegroundCellStyleExtension() { }
public ForegroundCellStyleExtension(string propertyName, Style basedOnCellStyle)
{
PropertyName = propertyName;
BasedOnCellStyle = basedOnCellStyle;
}
public string PropertyName
{
get;
set;
}
public Style BasedOnCellStyle
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
DependencyObject targetObject = service.TargetObject as DependencyObject;
if (targetObject == null)
{
return null;
}
Binding foregroundBinding = new Binding
{
Path = new PropertyPath(PropertyName),
Converter = new ValueConverter()
};
Style foregroundCellStyle = new Style(typeof(DataGridCell), BasedOnCellStyle);
foregroundCellStyle.Setters.Add(new Setter(DataGridCell.ForegroundProperty, foregroundBinding));
return foregroundCellStyle;
}
}
задать стиль ячеек для каждого столбца следующим образом:
<DataGridTextColumn Header="ColA" Binding="{Binding colA, StringFormat=\{0:P\}}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Foreground"
Value="{Binding colA, Converter={StaticResource valueToForeground}}" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="ColB" Binding="{Binding colB, StringFormat=\{0:P\}}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Foreground"
Value="{Binding colB, Converter={StaticResource valueToForeground}}" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
...
и измените свой конвертер
public class ValueConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
return ((double) value < 0) ? Brushes.Red : Brushes.Black;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
самый простой способ, который я понял, - связать ваш полный элемент вместо элемента / содержимого.текст только для вашего конвертера. Тогда вы сможете делать то, что вы хотели сделать со своими ячейками, с необходимостью беспокоиться о значениях элементов и параметров.
в стиле вашей ячейки:
<Setter Property="Foreground"
Value="{Binding Converter={StaticResource valueToForeground}}" />
и в вашем коде конвертера:
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
if (value != null)
{
mydatatype data = value as mydatatype;
//your logic goes here and also can play here with your dataitem.
if (Double.TryParse(data.CollD.ToString(), out doubleValue))
{
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
}
}
return brush;
}