Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable Specific DataGrid Cells with Binding

I'm trying to bind a list of custom data objects to a data grid and achieve the following behavior.

  1. Populate the grid
  2. Disable certain cells based on object data.

Consider the following DataGrid

<DataGrid ItemsSource="{Binding Path=CustomObjectList}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=FieldName}"
                            Header="Field Name"
                            IsReadOnly="True" />
        <DataGridCheckBoxColumn Binding="{Binding Path=Compare}"
                                Header="Compare" />
        <DataGridTextColumn Binding="{Binding Path=Tolerance}"
                            Header="Tolerance" />
    </DataGrid.Columns>
</DataGrid>

With a backing object like this...

public class CustomObject: BaseModel
{
    private bool _compare;

    private bool _disableTolerance;

    private string _fieldName;

    private bool _mustCompare;

    private double _tolerance;

    /// <summary>
    /// Gets or sets the compare.
    /// </summary>
    /// <value>The compare.</value>
    public bool Compare
    {
        get
        {
            return this._compare;
        }
        set
        {
            this._compare = value;
            this.NotifyPropertyChange("Compare");
        }
    }

    /// <summary>
    /// Gets or sets the disable tolerance.
    /// </summary>
    /// <value>The disable tolerance.</value>
    public bool DisableTolerance
    {
        get
        {
            return this._disableTolerance;
        }
        set
        {
            this._disableTolerance = value;
            this.NotifyPropertyChange("DisableTolerance");
        }
    }

    /// <summary>
    /// Gets or sets the name of the field.
    /// </summary>
    /// <value>The name of the field.</value>
    public string FieldName
    {
        get
        {
            return this._fieldName;
        }
        set
        {
            this._fieldName = value;
            this.NotifyPropertyChange("FieldName");
        }
    }

    /// <summary>
    /// Gets or sets the must compare.
    /// </summary>
    /// <value>The must compare.</value>
    public bool MustCompare
    {
        get
        {
            return this._mustCompare;
        }
        set
        {
            this._mustCompare = value;
            this.NotifyPropertyChange("MustCompare");
        }
    }

    /// <summary>
    /// Gets or sets the tolerance.
    /// </summary>
    /// <value>The tolerance.</value>
    public double Tolerance
    {
        get
        {
            return this._tolerance;
        }
        set
        {
            this._tolerance = value;
            this.NotifyPropertyChange("Tolerance");
        }
    }
}

And you can consider the CustomObjectList to be populated like this...

this.ComparisonsAndTolerances.Add(new ComparisonSettingsTolerances()
{
    FieldName = "Alpha",
    Compare = true,
    MustCompare = true,
    Tolerance = 0,
    DisableTolerance = false
});

this.ComparisonsAndTolerances.Add(new ComparisonSettingsTolerances()
{
    FieldName = "Bravo",
    Compare = true,
    MustCompare = false,
    Tolerance = 0,
    DisableTolerance = true
});

So, of course the FieldName, Compare, and Tolerance properties are filling into the grid appropriately.

However, what I would like to achieve is, when MustCompare is true, then that cell is marked as read only. And when DisableTolerance is true, that cell is marked as read only.

Obviously this will vary from cell to cell and row to row with the 4 different combinations, but I was hoping to achieve this through binding.

I tried

IsReadOnly="{Binding Path=MustCompare}"

and

IsReadOnly="{Binding Path=CustomObjectList/MustCompare}"

But neither of those worked.

Thanks.

like image 515
imdandman Avatar asked Apr 17 '13 15:04

imdandman


1 Answers

That is one interesting bug/situation you've got there.

What happens? Well, the binding is trying to bind to MustCompare property (and probably would have succeeded) but it cannot find FrameworkElement or FrameworkContentElement to be the target of the binding since none of data grid's columns are in the visual tree.

There is a solution, albeit not so elegant as what you (rightfully) tried to do:

<DataGridTemplateColumn Header="Compare">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock x:Name="TextHolder" />
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=Compare}"
                             Value="True">
                    <Setter TargetName="TextHolder"
                            Property="Text"
                            Value="True" />
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=Compare}"
                             Value="False">
                    <Setter TargetName="TextHolder"
                            Property="Text"
                            Value="False" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <CheckBox x:Name="Check"
                      IsChecked="{Binding Path=Compare}"
                      ToolTipService.ShowOnDisabled="True"/>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=MustCompare}"
                             Value="True">
                    <Setter TargetName="Check"
                            Property="IsEnabled"
                            Value="False" />
                    <Setter TargetName="Check"
                            Property="ToolTip"
                            Value="You cannot change this value since this item must compare" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

I've changed the cell so it shows text when in non-edit mode and check box when editing (just to see in what state is the cell, you can change it to check box in both cases).

This is not optimal, and I'd like to hear from you if you (or anybody) find a better solution.

EDIT:

Thought of another solution:

<DataGridCheckBoxColumn Binding="{Binding Path=Compare}"
                        Header="Compare">
    <DataGridCheckBoxColumn.CellStyle>
        <Style TargetType="{x:Type DataGridCell}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding MustCompare}"
                             Value="True">
                    <Setter Property="IsEnabled"
                            Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridCheckBoxColumn.CellStyle>
</DataGridCheckBoxColumn>

This makes the check box disabled in non-edit mode, so it looks different. Might be a good thing or a bad thing, depending upon your functional design.

like image 71
XAMeLi Avatar answered Oct 16 '22 01:10

XAMeLi