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>
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With