views:

62

answers:

3

UPDATED : Clean subject, and summarize it.

Hi, I've a datable filled, where each cell is a class like this

class CValue{
     public object Value;
     public Brush Quality;
     private int m_quality;

     public override String toString(){
           return Value.toString();
     }
}

My datagrid is bind on the datable, and it's working well. But my aim is to switch the background color of the cell depending of the Quality value.

I intend tu use datatemplate but don't know how it's working at all...

<dg:DataGrid  Name="DataGridResult" IsReadOnly="True" AutoGenerateColumns="False"
     BorderThickness="1" BorderBrush="{DynamicResource clBLACK}"
     CanUserReorderColumns="False"
     ItemsSource="{Binding Path=Result}">
          <dg:DataGrid.Resources>
             <Style TargetType="{x:Type dg:DataGridCell}">
                <Style.Setters>
                   <Setter Property="Background" Value="{Binding [1].Quality}"/>
                </Style.Setters>
             </Style>
          </dg:DataGrid.Resources>           
          <dg:DataGrid.ItemTemplate>
             <DataTemplate>
                <dg:DataGridCell>
                   </dg:DataGridCell>
             </DataTemplate>
           </dg:DataGrid.ItemTemplate>
</dg:DataGrid>

Actually, if the Value of the background's setter is set to "Blue", all cells are blued, so it's fine, but I can't find a way to bind it to my property. the [1] seems to return the column 1 of the row...

How to set to the cell dynamically ? 'Cause i've got a dynamic number of columns but there all of the CValue Type.

+1  A: 

You should use DataTemplateSelector class to perform this logic. The scenario is described below:

  • Create the set of DataTemplates;
  • Derive from DataTemplateSelector Class and implement there logic of selecting appropriate DataTemplate as described in MSDN Article;
  • Define your custom DataTemplateSelector as the resource specifying the x:Key attribute;
  • Bind needed object to defined DataTemplateSelector resource.

UPDATE

The upper approach works best when you need to completely redesign cells as the guys mentioned in comment.

So for this task you should create your converter, define it as a resource and add it to your binding:

<!--somewhere in resources-->
<QualityToBackgroundConverter x:Key="qualityToBackgroundConverter "/>

then binding will look like:

Background="{Binding Quality, Converter={StaticResource qualityToBackgroundConverter }}" 

and finally the converter:

[ValueConversion(typeof(Quality), typeof(Brush))]
public class QualityToBackgroundConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null)
        {
            Quality quality = (Quality)value;           
            switch (quality)
            {
                case 0: return Brushes.Red;
                case 1: return Brushes.Yellow;
                case 2: return Brushes.Green;
                default: return Brushes.Transparent;
            }   
        }
        return Brushes.Transparent;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw NotImplementedException();
    }
}
Eugene Cheverda
It´s somewhat unnessesary to replace the entire template just for changing the background don´t you think? Using a style or converter should be more practical.
Almund
I'm a little affraid of DataTemplateSelector it's seems overkill, isn't it ?
ykatchou
See update with converter.
Eugene Cheverda
+1  A: 

One good way of doing this which keeps the coloring visible in the XAML is to use a style with binding to the quality. We put this style in the some resourcedictionary above the template, like in my case in the DataGrid.Resources.

<Controls:DataGrid>
    <Controls:DataGrid.Resources>
        <Style TargetType="{x:Type Controls:DataGridCell}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Quality}" Value="0">
                    <Setter Property="Background" Value="Red" />
                </DataTrigger>

                <DataTrigger Binding="{Binding Quality}" Value="0">
                    <Setter Property="Background" Value="Blue" />
                </DataTrigger>

            </Style.Triggers>
        </Style>
    </Controls:DataGrid.Resources>
    <Controls:DataGrid.ItemTemplate>
        <DataTemplate>
            <Controls:DataGridCell>
            </Controls:DataGridCell>
        </DataTemplate>
    </Controls:DataGrid.ItemTemplate>
</Controls:DataGrid>

Update:

To be able to databind the values or whatever use a converter like this:

[ValueConversion(typeof(int), typeof(SolidColorBrush))]
public class QualityToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Cast value
        int intValue = (int) value;

        if (intValue == 1)
            return new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));

        return new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException("TwoWay binding not supported!");
    }
}

Bind it in the XAML like the follwing:

<Window.Resources>
    <WpfApplication1:QualityToColorConverter x:Key="ColorConverter" />
</Window.Resources>

<Controls:DataGridCell Background="{Binding Quality, Converter={StaticResource ColorConverter}}">
</Controls:DataGridCell>
Almund
I think it's easier for me to make a "converter" inside my CValue class and bind directly to the color because i'm using bit mask for check Quality... Should I use a datatemplate ?
ykatchou
Updated with a Converter example, just give a comment if you need help with any adjustments.
Almund
As a side notice, I noticed your Quality is an 'object' instance, this means the ValueConversion attribute would need to be typeof(object), typeof(SolidColorBrush) though I think you should rather type the quality since that would improve your compile time error check.
Almund
Any Ideas : 'Quality' property not found on 'object' ''DataRowView' (HashCode=57338399)'. BindingExpression:Path=Quality; DataItem='DataRowView' (HashCode=57338399); target element is 'DataGridCell' (Name=''); target property is 'Background' (type 'Brush')System.Windows.Data Error: 39 : BindingExpression path error: 'Value' property not found on 'object' ''DataRowView' (HashCode=57338399)'. BindingExpression:Path=Value; DataItem='DataRowView' (HashCode=57338399); target element is 'DataGridCell' (Name=''); target property is 'Content' (type 'Object')
ykatchou
Seems you´re not binding your CValue class as the datacontext in the datatemplate. How are you binding the source to the DataGrid?
Almund
I'm binding my datagrid by ItemsSource={Binding Result}, Result is a datagrid where each cells is a CValue, except columns 0 and 1 (which are datetimes)
ykatchou
Okey, thats you´re problem. You shouldn't bind a DataGrid into the DataGrid. You should bind the main result list or so. Like if you´ve got the window/list datacontext DataContext = new List<CValue>(); then you´d want to bind it to the DataGrid like: ItemSource={Binding} (which´ll bind directly to the DataContext).
Almund
+1  A: 

Ok. So for an entire example databinding to the Brush of the model instead of using converters, styles etc. For the following cs -code:

class CValue
{
    public string Value { get; set; } // Notice we use properties for binding and not fields
    public Brush Quality { get; set; } // Notice we use properties for binding and not fields
    private int m_quality;

    public override String ToString()
    {
        return Value.ToString();
    }
} 

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        // Databind the list
        myGrid.ItemsSource = new List<CValue>
                          {
                              new CValue
                                  {
                                      Value = "First", 
                                      Quality = new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))},
                              new CValue
                                  {
                                      Value = "Second",
                                      Quality = new SolidColorBrush(Color.FromArgb(255, 255, 0, 255))
                                  },
                              new CValue
                                  {
                                      Value = "Third", 
                                      Quality = new SolidColorBrush(Color.FromArgb(0, 255, 255, 255))
                                  }
                          };
    }
}

You would use a xaml for the rowstyle (notice the TargetType on the style and the AutoGenerateColumns="false") to bind the row-color and the value:

<Controls:DataGrid x:Name="myGrid" AutoGenerateColumns="False">

    <Controls:DataGrid.RowStyle>
        <Style TargetType="{x:Type Controls:DataGridRow}">
            <Setter Property="Background" Value="{Binding Quality}" />
        </Style>
    </Controls:DataGrid.RowStyle>

    <Controls:DataGrid.Columns>
        <Controls:DataGridTemplateColumn Header="Value">
            <Controls:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Label Content="{Binding Value}" />
                </DataTemplate>
            </Controls:DataGridTemplateColumn.CellTemplate>
        </Controls:DataGridTemplateColumn>
    </Controls:DataGrid.Columns>

</Controls:DataGrid>

Hope it helps!

Almund
Hi, thanks a lot, i'm not really far i thought, i've got this error : System.Windows.Data Error: 39 : BindingExpression path error: 'Quality' property not found on 'object' ''DataRowView' (HashCode=9847817)'. BindingExpression:Path=Quality; DataItem='DataRowView' (HashCode=9847817); target element is 'DataGridRow' (Name=''); target property is 'Background' (type 'Brush'), maybe it's because i'm binding a datable where each Cell is a CValue
ykatchou
You´re still binding the wrong thing to the ItemSource, you don't want to bind the datagrid but a List. Where do you set the DataContext?
Almund
So I can try something like ItemsSource={Binding Result.Rows} ?
ykatchou
Not quite... You must have a list of the CValue instances right? This is the one you want to bind to. What is 'Result' in this case, seems to be the DataGrid? You don't want to bind to the DataGrid you want to bind to the actual result list, array (/collection).
Almund