I have a datagrid in which I have defined different edit controls for editing data in one particular column. The visibility of each edit control is bound to the view model, to ensure only one of the edit controls is visible. When the grid switches into edit mode (say, the user selects cell and hits F2), when the edit control becomes displayed, I want it to have focus. However, it only works when I have one edit control. When I have more than 1, it doesn't work. I have determined that this is because the focus code is being executed for the last edit control as defined in the xaml (irrespective of whether it is visible or not).
There are multiple edit controls but for berevity I'll just limit the example here to two.
Within the grid I defined the edit controls and bind the visibility:
<toolkit:DoubleUpDown
Value="{Binding TimeResult, Converter={StaticResource TickToDblSecondsConverter}}"
Visibility="{Binding ResultType, Converter={StaticResource IsTimeValueConverter}}"/>
<toolkit:DoubleUpDown Value="{Binding DistanceResult}"
Visibility="{Binding ResultType, Converter={StaticResource IsDistanceValueConverter}}"/>
This works just fine and the correct control is visible and all the others are hidden. I then wanted to have focus set so I added a call to set the FocusManager.FocusedElement:
<toolkit:DoubleUpDown
Value="{Binding TimeResult, Converter={StaticResource TickToDblSecondsConverter}}"
Visibility="{Binding ResultType, Converter={StaticResource IsTimeValueConverter}}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>
<toolkit:DoubleUpDown Value="{Binding DistanceResult}"
Visibility="{Binding ResultType, Converter={StaticResource IsDistanceValueConverter}}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>
However, the focus only gets set correctly if the 2nd control happens to be visible. I need a way to set the FocusedElement to the control that is visible. I thought maybe if it was implemented as a trigger, it would only get fired for the visible control. So I tried giving each control a name and moved the focus code to a DataTemplate.Trigger:
<toolkit:DoubleUpDown Name="timeResultEdit"
Value="{Binding TimeResult, Converter={StaticResource TickToDblSecondsConverter}}"
Visibility="{Binding ResultType, Converter={StaticResource IsTimeValueConverter}}"/>
<toolkit:DoubleUpDown Name="distanceResultEdit" Value="{Binding DistanceResult}"
Visibility="{Binding ResultType, Converter={StaticResource IsDistanceValueConverter}}"/>
<DataTemplate.Triggers>
<Trigger SourceName="timeResultEdit" Property="Visibility" Value="Visible">
<Setter TargetName="timeResultEdit" Property="FocusManager.FocusedElement"
Value="{Binding RelativeSource={RelativeSource Self}}" />
</Trigger>
<Trigger SourceName="distanceResultEdit" Property="Visibility" Value="Visible">
<Setter TargetName="distanceResultEdit" Property="FocusManager.FocusedElement"
Value="{Binding RelativeSource={RelativeSource Self}}" />
</Trigger>
</DataTemplate.Triggers>
But this makes not the slightest bit of difference.
So, how do I change this so that I can have x number of edit controls, and focus will be set to that visible control when edit mode is invoked in the grid?
Using FocusManager.FocusedElement
in this way does nothing. That property tells an element which of its descendant controls has logical focus, i.e., which control should receive keyboard focus when keyboard focus enters the target element. You are setting this property on the elements you wish to receive keyboard focus, and since the problem was that they don't have keyboard focus to begin with, nothing is accomplished.
Set FocusManager.FocusedElement
on the root element within your cell template instead.
Another solution would be to add a PreparingCellForEdit
handler on your DataGrid
, then schedule a focus traversal request to the first visible and focusable element within the cell editor:
private void OnDataGridPreparingCellForEdit(
object sender,
DataGridPreparingCellForEditEventArgs e)
{
if (e.Column == YourColumn) // replace with an appropriate comparison
{
var element = e.EditingElement;
element.Dispatcher.BeginInvoke(
DispatcherPriority.Input,
new Action(() => element.MoveFocus(
new TraversalRequest(FocusNavigationDirection.First))));
}
}