Como colocar o DataGrid no conversor
-
21-12-2019 - |
Pergunta
Eu tenho esse conversor, ele leva:o DataGridCell atual, um objeto DataGridCellInfo e estou tentando colocar o objeto DataGrid lá também.
<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>
Tentei apenas vincular o DataGrid conforme abaixo, porém quando ele é virtualizado e você rola para baixo e os itens são reciclados, a ligação é descartada e gera um erro.
Aviso de sistema.Windows.Data:4:Não é possível encontrar a fonte para ligação com a referência 'ElementName = GenericDataGrid'.Expressão de Ligação:Caminho=;DataItem=nulo;o elemento de destino é 'DataGridCell' (Nome='');A propriedade de destino é 'IsTextMatchFocused' (Type 'Boolean')
No conversor abaixo, o DataGridCell é convertido em um DataGridCellInfo, e estou basicamente comparando os índices de linha e coluna dos dois DataGridCellInfo para ver se eles correspondem, em caso afirmativo, retorne verdadeiro.
Para fazer isso eu preciso do objeto DataGrid.Posso ver três soluções possíveis:
1.Talvez eu possa apenas comparar os dois objetos DataGridCellInfo para ver se eles são iguais, sem precisar usar um objeto DataGrid.(Eu tentei isso, mas sempre retorna falso)
2.Obtenha o DataGrid real de um dos objetos DataGridCellInfo, pois é pai.(Não sei como fazer isso).
3.Faça a ligação funcionar de uma maneira diferente.
Obviamente, este conversor será executado em várias células sempre que uma das ligações for alterada, então gostaria que fosse o mais eficiente possível.
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;
}
}
Solução
Embora eu goste da solução dada de fornecê-lo ao conversor por meio de um RelativeSource, isso também pode ser feito de uma maneira diferente.É possível não passar um parâmetro DataGrid, mas sim localizá-lo no DataGridCell uma vez dentro do conversor através do Parent
propriedade no DataGridCell.
Para fazer isso, você precisará de um método auxiliar para encontrar os pais:
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);
}
Você pode optar por colocar esse código em um local reutilizável ou até mesmo torná-lo um método de extensão, mas aqui está como você o chama uma vez no conversor:
DataGrid parentDataGrid = FindParent<DataGrid>(dataGridCell);
Outras dicas
Eu imagino que você poderia usar um RelativeSource Binding
para alcançar sua exigência.Experimente isto:
<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>