I have a DataGrid in a WPF app where I want to allow the user to perform actions on a selected item in the grid. I am hosting the controls for those actions in the DetailRow of the DataGrid.
When the user clicks a row in the grid, I expand it and reveal the controls. When they are done, I'd like them to be able to click the row again to hide that detail row. The problem I'm having is that I can't get WPF to distinguish between a click on the row (effectively the "header" of the row detail) and the row detail area where all my controls are. To WPF, a click on the detail means a click on the row, and my code considers that to be a toggle, and so it hides the row.
I'm doing the toggling in the code-behind, but I am using MVVM for the app, so if there is a more decoupled approach I could take, I'd be curious to know. Here is some sample code:
ViewModel and Model:
namespace StackOverflow
{
public class ViewModel
{
public ObservableCollection<MyClass> MyCollection { get; set; }
public ViewModel()
{
MyCollection = new ObservableCollection<MyClass>
{
new MyClass(0, "Foo"),
new MyClass(1, "Bar")
};
}
}
public class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public MyClass(int id, string name)
{
Id = id;
Name = name;
}
}
}
View:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackOverflow"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<local:ViewModel x:Key='MyVM'></local:ViewModel>
</Window.Resources>
<Grid DataContext='{StaticResource MyVM}'>
<DataGrid HorizontalAlignment='Center' Width='200' Margin='20'
RowDetailsVisibilityMode='Collapsed'
ItemsSource='{Binding MyCollection}'>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="UIElement.PreviewMouseLeftButtonDown"
Handler="OnRowLeftClicked" />
</Style>
</DataGrid.RowStyle>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Background='LightBlue' HorizontalAlignment='Center' Orientation='Horizontal' Height='100'>
<Button VerticalAlignment='Center'
HorizontalAlignment='Center'>Do stuff to this specific item</Button>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
Codebehind
namespace StackOverflow
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private DataGridRow lastRowClicked;
public MainWindow()
{
InitializeComponent();
}
private void OnRowLeftClicked(object sender, RoutedEventArgs e)
{
var clickedRow = (DataGridRow)sender;
//If a Todo has been clicked before for this selected user, we need to decide whether a new Todo has been selected.
if (lastRowClicked != null)
{
//If the Todo that was clicked last time is the same as the one that was just clicked, toggle its visibility --- this is where problem lies
if (lastRowClicked == clickedRow)
{
if (clickedRow.DetailsVisibility == Visibility.Collapsed)
clickedRow.DetailsVisibility = Visibility.Visible;
else
clickedRow.DetailsVisibility = Visibility.Collapsed;
}
//If this Todo was not clicked last time, hide the last Todo and show this current one.
else
{
lastRowClicked.DetailsVisibility = Visibility.Collapsed;
clickedRow.DetailsVisibility = Visibility.Visible;
}
}
//If this is the first Todo that was clicked for this user, we can show it without collapsing another row.
else
{
clickedRow.DetailsVisibility = Visibility.Visible;
}
//in any case, save the currently clicked row for next time
lastRowClicked = clickedRow;
}
}
}
Update:
This was marked as a possible duplicate for I need the Expand / Collapse for RowDetailsTemplate
Although it is a very similar problem, the solution in the other question involved using an expander button as a column in the datagrid. I would prefer to simply click anywhere the datagrid row and depending on whether the row was clicked before, expand or collapse the detail pane. As a backup, I would have the user left click to expand and right click to contract, which is what I'm doing now in my project.