<DataTemplate DataType="{x:Type EntityType:FixedDevice}">
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource NameConverter}}"/>
</DataTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<EventSetter Event="PreviewMouseRightButtonUp" Handler="TreeViewItem_RightClick"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem Header="Data Warehouse">
<TreeViewItem.Items>
<TreeViewItem Header="Platforms">
<TreeViewItem.Items>
<TreeViewItem>
<TreeViewItem.Header>
<TextBlock Text="{Binding RelativeSource={RelativeSource Self},
Path=Parent.Items.Count,
StringFormat=Public ({0})}">
</TextBlock>
</TreeViewItem.Header>
</TreeViewItem.Header>
</TreeViewItem.Items>
</TreeViewItem>
</TreeViewItem.Items>
</TreeViewItem>
private void TreeViewItem_RightClick(object sender, MouseButtonEventArgs e) {
TreeViewItem item = sender as TreeViewItem;
if(item != null) { item.Focus(); item.IsSelected = true; }
e.Handled = true;
}
How come when I right-click, it only selects the top-most node? and not the one I click? Similar for an ItemsSource, which the Item itself is not a TreeViewItem, but a DataTemplate with a TextBlock, how do I select the object in the TreeView visually?
Edit, see below for answer based on #Viv answer.
I ended up having to use e.OriginalSource to retrieve the TextBlock then the TreeViewItem that the Item was in.
private void TreeViewItem_RightClick(object sender, MouseButtonEventArgs e) {
TreeViewItem item = (e.OriginalSource as FrameworkElement).Parent as TreeViewItem;
if(item == null) {
item = e.Source as TreeViewItem;
if(item == null) {
return;
}
}
item.IsSelected = true;
e.Handled = true;
}
It's because what your looking for is e.Source and not sender
try switching your right-click handler to:
private void TreeViewItem_RightClick(object sender, MouseButtonEventArgs e) {
TreeViewItem item = e.Source as TreeViewItem;
if (item == null)
return;
// item.Focus();
item.IsSelected = true;
e.Handled = true;
}
Sample Download
Related
I want to create an Expander that contains a BusyIndicator and as soon as you click on the Expander, a BusyIndicator will appear until there is content
<Grid>
<Expander Expanded="Expander_OnExpanded">
<syncfusion:SfBusyIndicator x:Name="BusyIndicator">
<ItemsControl x:Name="ItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</syncfusion:SfBusyIndicator>
</Expander>
</Grid>
private void Expander_OnExpanded(object sender, RoutedEventArgs e)
{
BusyIndicator.IsBusy = true;
ItemsControl.ItemsSource = new ObservableCollection<string> { "A", "B", "C" };
Thread.Sleep(1000);
BusyIndicator.IsBusy = false;
}
Thread.Sleep blocks the UI thread. Wait asynchronously or perform any long-running operation on a background thread:
private async void Expander_OnExpanded(object sender, RoutedEventArgs e)
{
BusyIndicator.IsBusy = true;
ItemsControl.ItemsSource = new ObservableCollection<string> { "A", "B", "C" };
await Task.Delay(3000);
BusyIndicator.IsBusy = false;
}
So I have pretty much followed every example I can find on how to set the drag/drop effect on a WPF control to None to show the circle with a line symbol, but to no avail.
Here is the code I have copied from various examples, modifying it to get it work:
This is the code behind for the Window:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TreeViewWithCheckBoxes
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
FooViewModel root = this.tree.Items[0] as FooViewModel;
base.CommandBindings.Add(
new CommandBinding(
ApplicationCommands.Undo,
(sender, e) => // Execute
{
e.Handled = true;
root.IsChecked = false;
this.tree.Focus();
},
(sender, e) => // CanExecute
{
e.Handled = true;
e.CanExecute = (root.IsChecked != false);
}));
this.tree.Focus();
}
private void TreeViewItemDragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.None;
if (e.Data.GetDataPresent(typeof(FooViewModel)))
{
var folderViewModel = e.Data.GetData(typeof(FooViewModel))
as FooViewModel;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>
((DependencyObject)e.OriginalSource);
if (treeViewItem == null)
{
return;
}
var dropTarget = treeViewItem.Header as FooViewModel;
if (dropTarget == null || folderViewModel == null)
{
return;
}
if (dropTarget.Parent == folderViewModel.Parent)
e.Effects = e.AllowedEffects;
else
{
e.Effects = DragDropEffects.None;
}
}
}
private void TreeViewItemDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(FooViewModel)))
{
var folderViewModel = e.Data.GetData(typeof(FooViewModel))
as FooViewModel;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>
((DependencyObject)e.OriginalSource);
var dropTarget = treeViewItem.Header as FooViewModel;
if (dropTarget == null || folderViewModel == null)
return;
if (dropTarget.Parent == folderViewModel.Parent)
{
var dropType = dropTarget.Name;
var dragType = folderViewModel.Name;
var parent = dropTarget.Parent;
if (parent != null)
{
var dropLocation = -1;
var dragLocation = -1;
for (int index = 0; index < parent.Children.Count; ++index)
{
if (parent.Children[index].Name == dropType) dropLocation = index;
if (parent.Children[index].Name == dragType) dragLocation = index;
if (dropLocation != -1 && dragLocation != -1)
break;
}
if (dropLocation != -1 && dragLocation != -1)
{
parent.Children[dropLocation] = folderViewModel;
parent.Children[dragLocation] = dropTarget;
}
}
}
}
}
private void TreeViewItemMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var mousePos = e.GetPosition(null);
var diff = StartPoint - mousePos;
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
var treeView = sender as TreeView;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>((DependencyObject)e.OriginalSource);
if (treeView == null || treeViewItem == null)
return;
var folderViewModel = treeView.SelectedItem as FooViewModel;
if (folderViewModel == null)
return;
var dragData = new DataObject(folderViewModel);
DragDrop.DoDragDrop(treeViewItem, dragData, DragDropEffects.Move);
}
}
}
private void TreeViewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
StartPoint = e.GetPosition(null);
}
public Point StartPoint { get; set; }
private void TreeViewItemDragOver(object sender, DragEventArgs e)
{
var container = sender as FrameworkElement;
if (container == null)
{
return;
}
var scrollViewer = VisualTreeHelperUtils.GetFirstVisualChild<ScrollViewer>(container);
if (scrollViewer == null)
{
return;
}
double tolerance = 30;
double verticalPos = e.GetPosition(container).Y;
double offset = 20;
if (verticalPos < tolerance) // Top of visible list?
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - offset); //Scroll up.
}
else if (verticalPos > container.ActualHeight - tolerance) //Bottom of visible list?
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); //Scroll down.
}
e.Effects = DragDropEffects.None;
var source = e.OriginalSource as DependencyObject;
if (source == null)
{
return;
}
if (e.Data.GetDataPresent(typeof(FooViewModel)))
{
var folderViewModel = e.Data.GetData(typeof(FooViewModel))
as FooViewModel;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>((DependencyObject)e.OriginalSource);
if (treeViewItem == null)
{
return;
}
var dropTarget = treeViewItem.Header as FooViewModel;
if (dropTarget == null || folderViewModel == null)
{
return;
}
if (dropTarget.Parent == folderViewModel.Parent)
e.Effects = e.AllowedEffects;
else
{
e.Effects = DragDropEffects.None;
}
}
}
}
}
And here is the XAML for it, showing that it has registered with the proper events:
<Window
x:Class="TreeViewWithCheckBoxes.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewWithCheckBoxes"
xmlns:dw="clr-namespace:DrWPF.Windows.Controls"
FontSize="13"
Title="TreeView with CheckBoxes"
Width="300" Height="300"
WindowStartupLocation="CenterScreen"
>
<Window.Resources>
<ResourceDictionary>
<!-- Load this specific theme because the Aero theme for CheckBox has issues. -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PresentationFramework.Royale;V3.0.0.0;31bf3856ad364e35;component\themes\royale.normalcolor.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="{Binding IsInitiallySelected, Mode=OneTime}" />
<Setter Property="KeyboardNavigation.AcceptsReturn" Value="True" />
<Setter Property="dw:VirtualToggleButton.IsVirtualToggleButton" Value="True" />
<Setter Property="dw:VirtualToggleButton.IsChecked" Value="{Binding IsChecked}" />
</Style>
<HierarchicalDataTemplate
x:Key="CheckBoxItemTemplate"
ItemsSource="{Binding Children, Mode=OneTime}"
>
<StackPanel Orientation="Horizontal">
<!-- These elements are bound to a FooViewModel object. -->
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<ContentPresenter
Content="{Binding Name, Mode=OneTime}"
Margin="2,0"
/>
</StackPanel>
</HierarchicalDataTemplate>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<ObjectDataProvider
MethodName="CreateFoos"
ObjectType="{x:Type local:FooViewModel}" />
</Window.DataContext>
<TabControl>
<TabItem Header="Original">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height=".2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button
Grid.Row="3"
Command="Undo"
Content="Generate Report"
HorizontalAlignment="Center"
Margin="0,2"
Padding="8,0"
/>
<TreeView
Grid.Row="0"
x:Name="tree"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding Mode=OneTime}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}"
AllowDrop="True"
PreviewMouseLeftButtonDown="TreeViewMouseLeftButtonDown"
PreviewMouseMove="TreeViewItemMouseMove"
DragEnter="TreeViewItemDragEnter"
DragOver="TreeViewItemDragOver"
Drop="TreeViewItemDrop"
/>
<ScrollViewer Grid.Row="2" IsEnabled="{Binding ElementName=tree, Path=SelectedItem.IsChecked}">
<TextBlock Text="Oh Hai"></TextBlock>
<ScrollViewer.Style>
<Style TargetType="ScrollViewer">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"></Setter>
<Setter Property="VerticalScrollBarVisibility" Value="Hidden"></Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Opacity" Value="1"></Setter>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ScrollViewer.Style>
</ScrollViewer>
</Grid>
</TabItem>
<TabItem Header="Modified">
<DockPanel>
<Button
DockPanel.Dock="Bottom"
Command="Undo"
Content="Uncheck All"
HorizontalAlignment="Center"
Margin="0,2"
Padding="8,0"
/>
<TreeView
x:Name="modified"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding Mode=OneTime}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}"
/>
</DockPanel>
</TabItem>
</TabControl>
</Window>
I have not attached any code for the helper methods... those all work fine, and the basic idea of scrolling up/down while dragging, and exchanging items within a block works all fine.
However, the silly icon refuses to change for me. :)
I even tried to set it to None at the very top of the handler, and only set it back to AllowedEffects IF the parents are the same.
Essentially, the icon should switch to the circle with the line through it (None) if dragging to a parent that is not the same...
I have even set breakpoints to ensure that it is going into the case of the parents not being the same and setting the effect to None. Somehow, something must be switching it back, but I've no idea what...
Apparently, you also have to set e.Handled = true within the handler after setting e.Effects. After doing this, it works perfectly.
I wrote DoDragDrop in DataGrid.
However, i can't click and drag scroll bar on DataGrid because Drag Error occurred.
But i can scroll with mouse wheel.
How can I fix it? Here is a part of my code.
.cs file
private void datagrid_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point currentPosition = e.GetPosition(incidentList);
object selectedItem = datagrid.SelectedItem;
if (selectedItem != null)
{
DataGridRow container = (DataGridRow)datagrid.ItemContainerGenerator.ContainerFromItem(selectedItem);
var dataObj = new DataObject();
dataObj.SetData("DragSource", container);
if (container != null)
{
DragDrop.DoDragDrop(container, dataObj, DragDropEffects.Copy);
}
}
}
}
.xaml file
<DataGrid x:Name="datagrid" ColumnHeaderStyle="{StaticResource MyColumnHeader}"
Style="{DynamicResource DataGridStyle}" CanUserAddRows="False" VerticalAlignment="Stretch"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight}"
HorizontalAlignment="Stretch" MinHeight="150" SelectionMode="Single"
ItemsSource="{Binding myListData, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False"
SelectedItem="{Binding SelectMyRow}"
MouseMove="datagrid_MouseMove"> .....
Register your handler on the row instead of the datagrid:
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<EventSetter Event="MouseMove" Handler="RowMoveHandler"/>
</Style>
</DataGrid.RowStyle>
And then take the sender-row as container:
private void RowMoveHandler(object sender, MouseEventArgs e)
{
var container = sender as DataGridRow;
if (container != null && e.LeftButton == MouseButtonState.Pressed)
{
var dataObj = new DataObject();
dataObj.SetData("DragSource", container);
DragDrop.DoDragDrop(container,
dataObj,
DragDropEffects.Copy);
}
}
I have a treeview Item as such in a treeview that will have a list bound to it:
<TreeViewItem Name="tviOffline" Foreground="Red" FontWeight="Bold"
Header="Offline">
<TreeViewItem.ItemTemplate>
<DataTemplate DataType="{x:Type local:Buddy}">
<StackPanel>
<TextBlock Text="{Binding Nick}" FontSize="10" Foreground="#8CFFD528" />
</StackPanel>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
I cannot figure out how to get each of its childs to have a double click event.
any help is appreciated. thanks much.
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
...
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
...
And THEN, the handler has to be written as follows in order to prevent the double-click from firing on successive parent TreeViewItems:
private void OnItemMouseDoubleClick(object sender, MouseButtonEventArgs args)
{
if (sender is TreeViewItem)
{
if (!((TreeViewItem)sender).IsSelected)
{
return;
}
}
.... do stuff.
}
Thanks to Aurelien Ribon for getting 90% of the way there. The double-click problem seems to be well-known in other postings on Stack Exchange. Just consolidating both solutions into one answer.
This is the only way I managed to get it to work for all the cases:
void MyTreeView_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var clickedItem = TryGetClickedItem(myTreeView, e);
if (clickedItem == null)
return;
e.Handled = true; // to cancel expanded/collapsed toggle
DoStuff(clickedItem);
}
TreeViewItem TryGetClickedItem(TreeView treeView, MouseButtonEventArgs e)
{
var hit = e.OriginalSource as DependencyObject;
while (hit != null && !(hit is TreeViewItem))
hit = VisualTreeHelper.GetParent(hit);
return hit as TreeViewItem;
}
i have a nested datagrid(which is of three data grid). here i am able to show data with no issue.
the first datagrid has 5 rows
the main problem that comes here is when you click any row in first datagrid i show 2 datagrid( which has 10 rows)
lets say i click 3 row in 2 data grid. it show further records in third datagrid.
again when i click 5 row in 2 data grid it show further records in third datagrid.
now all the recods are shown fine
when u try to collpase the 3 row in 2 data grid it collpase but the issue is the height what the third datagrid which took space for showing the records( we can see a blank space showing between the main 2 datagrid and third data grid)
in every grid first column i have an button where i am writing this code for expand and collpase
this is the functionality i am implementing in all the datagrid button where i do expand collpase.
hope my question is clear
. any help would great
xaml page
<sdk:DataGrid MinHeight="100" x:Name="dgStudent " Width="300" SelectionMode="Extended" RowDetailsVisibilityMode="VisibleWhenSelected" >
<sdk:DataGrid.Columns >
<sdk:DataGridTemplateColumn >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btn"
Click="btn1_Click" >
</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header="ID" Width="100" >
<sdk:DataGridTemplateColumn.CellTemplate >
<DataTemplate >
<sdk:Label Content="{Binding ID}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header="Name" Width="100" >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<sdk:Label Content="{Binding Name}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel x:Name="spCollege" Width="300">
<sdk:DataGrid x:Name="dgCollege" Width="290" SelectionMode="Extended" RowDetailsVisibilityMode="VisibleWhenSelected" HeadersVisibility ="None" >
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn >
<sdk:DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<Button x:Name="btn2"
Click="btn3_Click">
</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header="College" Text="{Binding College}" >
<sdk:DataGridTemplateColumn Header="Subjects" Text="{Binding Subjects}" >
</sdk:DataGrid.Columns>
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel x:Name="spSchool" Width="300" >
<sdk:DataGrid x:Name="dgSchool" Width="280" SelectionMode="Extended" RowDetailsVisibilityMode="VisibleWhenSelected" HeadersVisibility ="None">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Width="24">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btn" Height="15"
Click="btn3_Click">
</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header="School" Text="{Binding School}" >
<sdk:DataGridTemplateColumn Header="Subjects" Text="{Binding Subjects}" >
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</StackPanel>
</DataTemplate>
</sdk:DataGrid.RowDetailsTemplate>
</sdk:DataGrid>
private void btn1_Click(object sender, RoutedEventArgs e)
{
try
{
Button btnExpandCollapse = sender as Button;
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
// if we found the clicked row
if (dep != null && dep is DataGridRow)
{
DataGridRow row = (DataGridRow)dep;
// change the details visibility
if (row.DetailsVisibility == Visibility.Collapsed)
{
row.DetailsVisibility = Visibility.Visible;
}
else
{
row.DetailsVisibility = Visibility.Collapsed;
}
}
}
catch (System.Exception)
{
}
}
---------------------------------------
2 datagrid
private void btn2_Click(object sender, RoutedEventArgs e)
{
try
{
Button btnExpandCollapse = sender as Button;
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep != null && dep is DataGridRow)
{
DataGridRow row = (DataGridRow)dep;
// change the details visibility
if (row.DetailsVisibility == Visibility.Collapsed)
{
row.DetailsVisibility = Visibility.Visible;
}
else
{
row.DetailsVisibility = Visibility.Collapsed;
}
}
}
catch (System.Exception)
{
}
}
-----------------
3 datagrid
private void btn1_Click(object sender, RoutedEventArgs e)
{
try
{
Button btnExpandCollapse = sender as Button;
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
// if we found the clicked row
if (dep != null && dep is DataGridRow)
{
DataGridRow row = (DataGridRow)dep;
// change the details visibility
if (row.DetailsVisibility == Visibility.Collapsed)
{
row.DetailsVisibility = Visibility.Visible;
}
else
{
row.DetailsVisibility = Visibility.Collapsed;
}
}
}
catch (System.Exception)
{
}
}
Look at solution here:
Resize Datagrid Height after deselecting a RowDetailsTemplate
This is apparently a bug in the SL grid. I was able to get around it by quickly closing and reopening the parent RowDetailsTemplate after the the user collapses a row in the second-level grid:
void secondLevelGrid_RowDetailsVisibilityChanged(object sender, DataGridRowDetailsEventArgs e)
{
if (e.Row.DetailsVisibility == Visibility.Visible) return;
var parentRow = this.GetVisualAncestors().OfType<DataGridRow>().FirstOrDefault();
parentRow.DetailsVisibility = Visibility.Collapsed;
var timer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 200) };
timer.Tick += (ts, te) =>
{
Dispatcher.BeginInvoke(() => parentRow.DetailsVisibility = Visibility.Visible);
timer.Stop();
};
timer.Start();
}
When the parent row reopens, the correct row in the second-level grid is still selected, so visually this creates a fairly seamless experience.