Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show popup during editing of WPF DataGrid cell

Along the same lines as How do I place a Popup in my DataGridTemplateColumn.CellEditingTemplate correctly?, I am trying to have a PopUp appear below a cell in a DataGrid when the cell is being edited, and disappear when the cell is no longer being edited. The final thing is that the PopUp contents are dynamic according to the column, and the columns are created dynamically via binding.

I started out with the following XAML, but I get a XamlParseException "Add value to collection of type 'System.Windows.Controls.ItemCollection' threw an exception".

<DataGrid ItemsSource="{Binding Path=Options}">
    <DataGridTemplateColumn>
        <DataGridTemplateColumn.CellEditingTemplate>
            <DataTemplate>
                <Grid>
                    <Popup Placement="Bottom" IsOpen="True" Width="200" Height="100">
                        <TextBlock>Somethingn here</TextBlock>
                    </Popup>
                </Grid>
            </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>
    </DataGridTemplateColumn>
</DataGrid>
like image 940
Walter Williams Avatar asked Sep 01 '25 05:09

Walter Williams


1 Answers

I have figured out a solution that works well so far. It involves some event handlers, but the code does not have to access the view model so hopefully it won't incur the wrath of MVVM purists.

XAML:

<Grid>
    <DataGrid ItemsSource="{Binding Path=Options}" BeginningEdit="DataGrid_BeginningEdit" CellEditEnding="DataGrid_CellEditEnding" />
    <Popup Name="pop1">
        <Border Width="300" Height="200" Background="LemonChiffon" BorderThickness="2" BorderBrush="Black" />
    </Popup>
</Grid>

MainWindow.xaml.cs event handlers:

private void DataGrid_BeginningEdit (Object sender, DataGridBeginningEditEventArgs e)
{
    DataGrid grid = (DataGrid) sender;
    Popup pop1 = (Popup) grid.FindName("pop1");
    pop1.PlacementTarget = grid.GetCell(e.Row.GetIndex(), e.Column.DisplayIndex);
    pop1.IsOpen = true;
}

private void DataGrid_CellEditEnding (Object sender, DataGridCellEditEndingEventArgs e)
{
    Popup pop1 = (Popup) ((DataGrid) sender).FindName("pop1");
    pop1.IsOpen = false;
}

DataGridExtensions.cs:

/// <summary>
/// Extension methods for DataGrid
/// These methods are thanks to http://blogs.msdn.com/b/vinsibal/archive/2008/11/05/wpf-datagrid-new-item-template-sample.aspx
/// </summary>
public static class DataGridExtensions
{
    /// <summary>
    /// Returns a DataGridCell for the given row and column
    /// </summary>
    /// <param name="grid">The DataGrid</param>
    /// <param name="row">The zero-based row index</param>
    /// <param name="column">The zero-based column index</param>
    /// <returns>The requested DataGridCell, or null if the indices are out of range</returns>
    public static DataGridCell GetCell (this DataGrid grid, Int32 row, Int32 column)
    {
        DataGridRow gridrow = grid.GetRow(row);
        if (gridrow != null)
        {
            DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(gridrow);

            // 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
                grid.ScrollIntoView(gridrow, grid.Columns[column]);

                cell = (DataGridCell) presenter.ItemContainerGenerator.ContainerFromIndex(column);
            }

            return (cell);
        }

        return (null);
    }

    /// <summary>
    /// Gets the DataGridRow based on the given index
    /// </summary>
    /// <param name="idx">The zero-based index of the container to get</param>
    public static DataGridRow GetRow (this DataGrid dataGrid, Int32 idx)
    {
        DataGridRow row = (DataGridRow) dataGrid.ItemContainerGenerator.ContainerFromIndex(idx);
        if (row == null)
        {
            // may be virtualized, bring into view and try again
            dataGrid.ScrollIntoView(dataGrid.Items[idx]);
            dataGrid.UpdateLayout();

            row = (DataGridRow) dataGrid.ItemContainerGenerator.ContainerFromIndex(idx);
        }

        return (row);
    }

    private static T GetVisualChild<T> (Visual parent) where T : Visual
    {
        T child = default(T);

        Int32 numvisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (Int32 i = 0; i < numvisuals; ++i)
        {
            Visual v = (Visual) VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
                child = GetVisualChild<T>(v);
            else
                break;
        }

        return child;
    }
}
like image 53
Walter Williams Avatar answered Sep 02 '25 19:09

Walter Williams