2012-05-29 3 views
7

У меня есть стандартная (WPF-инструментарий) сетка данных. Некоторые столбцы (которые явно определены) должны отображаться как проценты. Некоторые столбцы должны быть показаны красным цветом, если значения ниже 0. (Два набора столбцов не совпадают). Я попытался реализовать эти требования, используя StringFormat и Style, соответственно. Мой XAML:WPat datagrid: конвертер и StringFormat

<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, конвертер прекрасно работает, и текст появляется красным. Мне что-то не хватает? Есть ли простой способ обойти это? Единственное, о чем я могу думать сейчас, - это переместить форматирование в другой конвертер, и я не уверен, что это сработает.

ответ

5

У нас была похожая ситуация, когда мы нуждались в другой Path собственности для Binding, но в остальном аналогичный CellStyle для каждого DataGridColumn. Мы решили это с обычным 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 и т.д. которые вы хотели бы использовать, тогда они могут быть включены другим параметром в 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 для DataGridCellStyle

ForegroundCellStyleExtension с 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; 
    } 
} 
+0

похоже, что это сработает, но у меня не было достаточно времени, чтобы проверить это. Спасибо, в любом случае! – vlad

+0

это гораздо лучшее решение для повторного использования и работает как шарм! это должен быть реальный ответ – DLeh

2

Укажите стиль ячейки для каждого столбца следующим образом:

<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; 
    } 
} 

enter image description here

+0

Я попробовал это, но это не сработало. – vlad

+1

@ vlad: он определенно работает, я забыл добавить обновленный преобразователь значений – Phil

+0

Я дам ему шанс, спасибо. – vlad

1

Самым простым способом, я понял, как связать свой полный элемент вместо пункта item/content.text только для вашего конвертера. Затем вы сможете делать то, что хотите делать со своими ячейками, с необходимостью беспокоиться о значениях элементов и параметров.

В вашем сотовом Стиль:

<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; 
} 
+0

Я закончил делать что-то подобное, но сохраняю «Binding Path» в Content.Text. Благодаря! – vlad