I'm developing a WinUI3 application. I'd like to have a CommunityToolkit datagrid with a button column each row. When the button is clicked a flyout needs to show up letting user enters the data to populate that field.
For simplicity I put only two buttons in the example but the application will require more controls:
<controls:DataGridTemplateColumn Header="Value" IsReadOnly="True">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Button Content="{Binding Value}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock Text="All items will be removed. Do you want to continue?" Margin="0,0,0,12" />
<Button Content="Yes, empty my cart" Click="Button_Click" />
<Button Content="No, cart is full" Click="FullButton_Click" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
As shown in the gif I've got clicking focus issues. First click works like a charm, further clicking seems to put cell in edit mode and flyout is not triggered. If I change the focus on other rows / cells results are different each time.
I think there's an editing / click pass through mechanism that I don't know how to avoid. Putting cell in "IsReadOnly" mode doesn't change the behaviour.
If I try to use a MenuFlyout or ContextFlyout on the cell it works great, but I need to show more controls and not a simple menu to be triggered on right click.
The DataGridComboBoxColumn
used to have the same issue and you can see here how it was fixed.
You can create a custom DataGrid
and Column
and apply a similar fix like this:
UPDATE
I updated my answer in way that you can cancel the edit mode explictly when the Flyout
is closed.
public class DataGridButtonWithFlyoutColumn : DataGridTemplateColumn
{
public event EventHandler<object>? FlyoutClosed;
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
if (editingElement is Button button)
{
button.Flyout.ShowAt(button);
button.Flyout.Closed += (s, e) =>
{
this.FlyoutClosed?.Invoke(this, e);
};
}
return base.PrepareCellForEdit(editingElement, editingEventArgs);
}
}
public class DataGridEx : DataGrid
{
public DataGridEx()
{
this.BeginningEdit += DataGridEx_BeginningEdit;
}
private void DataGridEx_BeginningEdit(object? sender, DataGridBeginningEditEventArgs e)
{
if (e.Column is DataGridButtonWithFlyoutColumn column)
{
column.FlyoutClosed += DataGridButtonWithFlyoutColumn_FlyoutClosed;
}
}
private void DataGridButtonWithFlyoutColumn_FlyoutClosed(object? sender, object e)
{
_ = CancelEdit();
if (sender is DataGridButtonWithFlyoutColumn column)
{
column.FlyoutClosed -= DataGridButtonWithFlyoutColumn_FlyoutClosed;
}
}
}
and use it like this:
<local:DataGridEx
x:Name="DataGridControl"
ItemsSource="{x:Bind Items}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn
Binding="{Binding Name}"
Header="Name" />
<toolkit:DataGridComboBoxColumn
Binding="{Binding Type}"
Header="Type"
ItemsSource="{x:Bind Types, Mode=OneWay}" />
<local:DataGridButtonWithFlyoutColumn Header="Value">
<local:DataGridButtonWithFlyoutColumn.CellTemplate>
<DataTemplate>
<Grid Background="LightGreen">
<TextBlock
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
HorizontalTextAlignment="Center"
Text="{Binding Value}" />
</Grid>
</DataTemplate>
</local:DataGridButtonWithFlyoutColumn.CellTemplate>
<toolkit:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="HotPink"
Content="{Binding Value}"
FontWeight="ExtraBold">
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock
Margin="0,0,0,12"
Text="All items will be removed. Do you want to continue?" />
<Button
Click="Button_Click"
Content="Yes, empty my cart" />
<Button
Click="Button_Click_1"
Content="No, cart is full" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellEditingTemplate>
</local:DataGridButtonWithFlyoutColumn>
</toolkit:DataGrid.Columns>
</local:DataGridEx>