What exactly is the difference between these two snippets:
A:
MyDataGrid.IsKeyboardFocusWithinChanged += (sender, e) => {
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
// do something
}));
}
};
B:
MyDataGrid.IsKeyboardFocusWithinChanged += (sender, e) => {
if ((bool)e.NewValue == true)
{
Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() =>
{
// do something
}));
}
};
I thought the only difference between Invoke
and BeginInvoke
is that the first one waits for the task to be finished while the latter retuns instantly. Since the Dispatcher
call is the only thing that happens in the EventHandler
, I assumed that using Invoke
would have the exact same effect as using BeginInvoke
in this case. However I have a working example where this is clearly not the case. Take a look for yourself:
MainWindow.xaml
<Window x:Class="DataGridFocusTest.MainWindow"
x:Name="MyWindow"
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:DataGridFocusTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<TextBox DockPanel.Dock="Top"></TextBox>
<DataGrid x:Name="MyDataGrid" DockPanel.Dock="Top" SelectionUnit="FullRow"
ItemsSource="{Binding SomeNumbers, ElementName=MyWindow}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Data" Binding="{Binding Data}"
IsReadOnly="True" />
<DataGridTemplateColumn Header="CheckBox">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace DataGridFocusTest
{
public class MyDataClass
{
public string Data { get; set; }
}
public partial class MainWindow : Window
{
public IList<MyDataClass> SomeNumbers
{
get
{
Random rnd = new Random();
List<MyDataClass> list = new List<MyDataClass>();
for (int i = 0; i < 100; i++)
{
list.Add(new MyDataClass() { Data = rnd.Next(1000).ToString() });
}
return list;
}
}
public MainWindow()
{
InitializeComponent();
MyDataGrid.IsKeyboardFocusWithinChanged += (sender, e) =>
{
if ((bool)e.NewValue == true)
{
// whenever MyDataGrid gets KeyboardFocus set focus to
// the selected item
// this has to be delayed to prevent some weird mouse interaction
Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() =>
{
if (MyDataGrid.IsKeyboardFocusWithin &&
MyDataGrid.SelectedItem != null)
{
MyDataGrid.UpdateLayout();
var cellcontent = MyDataGrid.Columns[1].GetCellContent(MyDataGrid.SelectedItem);
var cell = cellcontent?.Parent as DataGridCell;
if (cell != null)
{
cell.Focus();
}
}
}));
}
};
}
}
}
What we have here is a DataGrid
which rows include a CheckBox
. It implements two specific behaviors:
- When the
DataGrid
gets keyboard focus it will focus it's selected row (which for some reason is not the default behavior). - You can change the state of a
CheckBox
with a single click even when theDataGrid
is not focused.
This works already. With the assumption at the top of the post, I thought that it would not matter whether I use Invoke
or BeginInvoke
. If you change the code to BeginInvoke
however, for some reason the 2cnd behavior (one-click-checkbox-selection) does not work anymore.
I don't look for a solution (the solution is just using Invoke
) but rather want to know why Invoke
and BeginInvoke
behave so differently in this case.