2012-05-29 15 views
7

Ho una griglia di dati standard (WPF toolkit). Alcune delle colonne (che sono esplicitamente definite) devono essere mostrate come percentuali. Alcune colonne devono essere visualizzate in rosso se i valori sono inferiori a 0. (I due gruppi di colonne non sono uguali). Ho provato a implementare questi requisiti utilizzando uno StringFormat e Style, rispettivamente. La mia XAML:datagrid WPF: convertitore e 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> 

e il convertitore relativo:

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

penso che sia tutto abbastanza standard, ma il problema è che il convertitore ottiene il valore Text dopo che è passato attraverso la StringFormat, e in quel punto è difficile da analizzare correttamente (poiché in realtà, non tutte le colonne hanno lo stesso formato). Se tolgo lo StringFormats, il convertitore funziona correttamente e il testo si presenta in rosso. Mi manca qualcosa di ovvio? C'è un modo semplice per aggirare questo? L'unica cosa che posso pensare adesso è spostare la formattazione in un convertitore diverso, e non sono convinto che funzionerebbe.

risposta

5

Abbiamo avuto una situazione simile in cui avevamo bisogno di un diverso Path Immobili in il Binding ma per il resto uno CellStyle simile per ogni DataGridColumn. Abbiamo risolto questo problema con una personalizzazione MarkupExtension. Nel tuo caso sarebbe simile a questa

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

e poi ForegroundCellStyleExtension crea la Style per DataGridCell seconda 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; 
    } 
} 

Inoltre, se avete qualche altro Setters etc. che vorresti usare, possono essere inclusi da un altro parametro allo 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}}"/> 

E ForegroundCellStyleExtension sarebbe quindi utilizzare il secondo parametro come BasedOn per la DataGridCellStyle

ForegroundCellStyleExtension 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

sembra che funzioni, ma non ho avuto abbastanza tempo per provarlo. Grazie comunque! – vlad

+0

questa è una soluzione molto migliore per il riutilizzo, e funziona come un fascino! questa dovrebbe essere la vera risposta – DLeh

2

Specificare uno stile di cella per ogni colonna come segue:

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

... 

e modificare il convertitore

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

Ho provato questo, ma non ha funzionato. – vlad

+1

@vlad: funziona sicuramente, ho dimenticato di aggiungere il convertitore di valore aggiornato – Phil

+0

Gli darò uno scatto, grazie. – vlad

1

Il modo più semplice che ho capito è quello di legare il vostro articolo completo invece dell'oggetto/content.text solo al tuo convertitore. Quindi sarai in grado di fare ciò che volevi fare con le tue celle, dovendo preoccuparti dei valori degli articoli e dei parametri.

Nel tuo cellulare Stile:

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

e nel codice Converter:

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

Ho finito per fare qualcosa di simile, ma mantenendo il 'Binding Path' a' Content.Text'. Grazie! – vlad