terça-feira, fevereiro 01, 2011

Personalizar uma Row numa DataGrid WPF

Uma das coisas que mais me irrita fazer é a Graphical User Interface (GUI), perde-se imenso tempo com detalhes da treta e acabamos por não aprender nada de novo. Isto alcança o seu auge quando falamos de DataGrid, vendido como sendo a melhor coisa do mundo, a verdade é que é muito bom sim para demos rápidas, agora quando se quer começar a personalizar começa a dar mais trabalho que benefícios.

Estava aqui a trabalhar numa DataGrid em WPF quando me foi pedido que a cor de cada row fosse condicionada a uma propriedade do objecto que estava a ser bind. Parece simples...pois não é nada trivial e perdi à volta de 3h em pesquisas na internet e a implementar vários exemplos até que cheguei a uma solução que me agradou minimamente e era funcional.

Para o exemplo vamos considerar os seguintes pontos:
  • A nossa DataGrid tem o nome de dGrid;
  • O tipo do objecto que é bind a cada row é Register;
  • A propriedade do objecto que vai ser usada para condicionar a cor da row é a propriedade LK;
Os 2 principais contratempos que tive foram a altura em que podia afectar a cor da row e o facto de não poder afectar a cor da row, mas sim das células pois a cor das células sobrepõe-se à cor da row. Em relação à altura em que podia afectar a cor das células, em WPF só temos garantia que todos os filhos da janela estão prontos no evento Loaded da janela, logo tenho de ter um listener neste evento que no meu caso é o método WindowLoaded. Seguindo todos estes pressupostos o código seguinte exemplifica a solução a que cheguei.

private void WindowLoaded(object sender, RoutedEventArgs e){
    UpdateRowColor();
}

private void UpdateRowColor(){
    for (int i = 0; i < dGrid.Items.Count; ++i){
        DataGridRow row = GetRow(dGrid, i);

        if (row.Item is Register){
            Register registry = (Register)row.Item;

            for (int j = 0; j < dGrid.Columns.Count; ++j){
                if (registry.LK == "O")
                    GetCell(dGrid, row, j).Background = Brushes.Green;
                else
                    GetCell(dGrid, row, j).Background = Brushes.Red;
            }
        }
    }
}

private DataGridRow GetRow(DataGrid dataGrid, int index){
    DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
    if (row == null){
        dataGrid.ScrollIntoView(dataGrid.Items[index]);
        dataGrid.UpdateLayout();
        row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
    }
    return row;
}

private DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
{
    if (rowContainer != null){
        DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);

        // try to get the cell but it may possibly be virtualized
        DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
        if (cell == null){
            // now try to bring into view and retreive the cell
            dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
            cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
        }
        return cell;
    }
    return null;
}

private T GetVisualChild<T>(Visual parent) where T : Visual{
    T child = default(T);
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); ++i){
        Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
            child = GetVisualChild<T>(v);
        if (child != null)
            break;
    }
    return child;
}

Sem comentários: