Question

I have this converter, it takes: the current DataGridCell, a DataGridCellInfo object and I'm trying to get the DataGrid object in there as well.

    <Style TargetType="{x:Type DataGridCell}" x:Key="cellStyle" >
        <Setter Property="helpers:SearchBehaviours.IsTextMatchFocused">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource SelectedSearchValueConverter}" FallbackValue="False">
                    <Binding RelativeSource="{x:Static RelativeSource.Self}"/>
                    <Binding Source="{x:Static helpers:MyClass.Instance}" Path="CurrentCellMatch" />
                    <Binding ElementName="GenericDataGrid"/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>

Tried just binding the DataGrid as below, however when this is virtualized and you scroll down and the items are recycled it drops the binding and throws an error.

System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'ElementName=GenericDataGrid'. BindingExpression:Path=; DataItem=null; target element is 'DataGridCell' (Name=''); target property is 'IsTextMatchFocused' (type 'Boolean')

In the converter below the DataGridCell is cast to a DataGridCellInfo, and I'm basically comparing the two DataGridCellInfo's row and column index to see if they match, if so return true.

In order to do this I need the DataGrid object. I can see 3 possible solutions:
1. Maybe I can just compare the two DataGridCellInfo objects to see if they are the same, without having to use a DataGrid object. (I have tried this but it always returns false)
2. Get the actual DataGrid from one of the DataGridCellInfo objects as it is a parent. (Don't know how to do this).
3. Get the binding working in a different way.

Obviously this converter will be run for multiple cells whenever one of the bindings change so I would like it to be as efficient as possible.

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    try
    {
        if (values[0] == null || values[1] == null || values[2] == null)
        {
            return false;
        }

        DataGridCellInfo currentCellInfoMatch = (DataGridCellInfo)values[1];
        if (currentCellInfoMatch.Column == null)
            return false;

        DataGridCellInfo cellInfo = new DataGridCellInfo((DataGridCell)values[2]);
        if (cellInfo.Column == null)
            return false;

        DataGrid dg = (DataGrid)values[3];

        int cellInfoItemIndex = ((DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(cellInfo.Item)).GetIndex();
        int cellInfoColumnIndex = cellInfo.Column.DisplayIndex;
        int currentCellInfoMatchItemIndex = ((DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(currentCellInfoMatch.Item)).GetIndex();
        int currentCellInfoMatchColumnIndex = currentCellInfoMatch.Column.DisplayIndex;

        if (cellInfoItemIndex == currentCellInfoMatchItemIndex && cellInfoColumnIndex == currentCellInfoMatchColumnIndex)
            return true;

        return false;
    }
    catch (Exception ex)
    {
        Console.WriteLine("SelectedSearchValueConverter error : " + ex.Message);
        return false;
    }
}
Was it helpful?

Solution

Although I do like the given solution of giving it to the converter via a RelativeSource, it can also be done a different way. It is possible to not pass a DataGrid parameter, but instead find it from the DataGridCell once inside the converter via the Parent property on the DataGridCell.

To do this, you'll need a parent-finding helper method:

private T FindParent<T>(DependencyObject child)
    where T : DependencyObject
{
    T parent = VisualTreeHelper.GetParent(child) as T;  
    if (parent != null)
        return parent;
    else
        return FindParent<T>(parent);
}

You may chose to put this code in a reusable spot, or even make it an extension method, but here is how you call it once within the converter:

DataGrid parentDataGrid = FindParent<DataGrid>(dataGridCell);

OTHER TIPS

I would imagine that you could use a RelativeSource Binding to achieve your requirement. Try this:

<Style TargetType="{x:Type DataGridCell}" x:Key="cellStyle" >
    <Setter Property="helpers:SearchBehaviours.IsTextMatchFocused">
        <Setter.Value>
            <MultiBinding Converter="{StaticResource SelectedSearchValueConverter}" FallbackValue="False">
                <Binding RelativeSource="{x:Static RelativeSource.Self}"/>
                <Binding Source="{x:Static helpers:MyClass.Instance}" Path="CurrentCellMatch" />
<!-- ----> -->  <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}"/>
            </MultiBinding>
        </Setter.Value>
    </Setter>
</Style>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top