2012-05-29 31 views
7

Tengo una cuadrícula de datos estándar (WPF toolkit). Algunas de las columnas (que están explícitamente definidas) deben mostrarse como porcentajes. Algunas columnas deben mostrarse en rojo si los valores están por debajo de 0. (Los dos conjuntos de columnas no son iguales). Traté de implementar estos requisitos usando un StringFormat y Style, respectivamente. Mi XAML:WPF datagrid: convertidor y 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> 

y el convertidor relevante:

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

Creo que es todo bastante estándar, pero el problema es que el convertidor obtiene el valor Text después de que se ha ido a través de la StringFormat, y en ese es difícil analizarlo correctamente (ya que, en realidad, no todas las columnas tienen el mismo formato). Si saco el StringFormats, el convertidor funciona bien y el texto aparece en rojo. ¿Me estoy perdiendo algo obvio? ¿Hay una manera fácil de evitar esto? Lo único que se me ocurre en este momento es mover el formato a un convertidor diferente, y no estoy convencido de que funcione.

Respuesta

5

Tuvimos una situación similar donde teníamos una diferente Path Propiedad en el Binding pero por lo demás un CellStyle similar para cada DataGridColumn. Hemos resuelto esto con un MarkupExtension personalizado. En su caso, se vería así

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

y luego ForegroundCellStyleExtension crea la Style para DataGridCell dependiendo de 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; 
    } 
} 

También, si tiene alguna otra Setters etc. que le gustaría usar, entonces pueden ser incluidos por otro parámetro al 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}}"/> 

Y ForegroundCellStyleExtension usaría entonces el segundo parámetro como BasedOn para la ForegroundCellStyleExtension DataGridCellStyle

con 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

parece que funcionaría, pero no tuve tiempo suficiente para probarlo. ¡Gracias de cualquier manera! – vlad

+0

esta es una solución mucho mejor para reutilizar, ¡y funciona como un encanto! esta debería ser la verdadera respuesta – DLeh

2

especificar un estilo de celda para cada columna de la siguiente manera:

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

... 

y modificar su convertidor

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

yo probamos este, pero no funcionó. – vlad

+1

@ vlad: definitivamente funciona, olvidé agregar el convertidor de valor actualizado – Phil

+0

Lo probaré, gracias. – vlad

1

La forma más sencilla que me di cuenta es enlazar su artículo completo en lugar del artículo/content.text solo a tu convertidor. Entonces podrá hacer lo que quería hacer con sus celdas, sin preocuparse por los valores de los elementos y los parámetros.

En tu teléfono Estilo:

<Setter Property="Foreground" 
    Value="{Binding Converter={StaticResource valueToForeground}}" /> 

y en el código de convertidor:

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

Terminé haciendo algo similar, pero manteniendo el 'Binding Path' en' Content.Text'. ¡Gracias! – vlad