The behaviour I am looking for is that a selection of a Item in a ListView results in focusing the first focusable visualchild.
Problem: datatemplated data in a ItemsControler which does not get an initial focus.
In the example below there are 4 Strings which are then stuffed into a TextBox via Datatemplate.
Example:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ListView>
<ListView.Resources>
<DataTemplate DataType="{x:Type sys:String}" >
<TextBox Width="100" Text="{Binding Mode=OneWay}"/>
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<x:Array Type="{x:Type sys:String}">
<sys:String>test</sys:String>
<sys:String>test</sys:String>
<sys:String>test</sys:String>
<sys:String>test</sys:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
</Grid>
</Window>
I already tried some combinations of
FocusManager.FocusedElement="{Binding ElementName=[...]}"
pointless to say: without success.
Anyone a clou how I could get what I want without traversing the visual tree in c#?
It should be possible to do this, shouldn't it?
The FocusManager works sweet for this.
<DataTemplate x:Key="MyDataTemplate" DataType="ListBoxItem">
<Grid>
<WrapPanel Orientation="Horizontal" FocusManager.FocusedElement="{Binding ElementName=tbText}">
<CheckBox IsChecked="{Binding Path=Completed}" Margin="5" />
<Button Style="{StaticResource ResourceKey=DeleteButtonTemplate}" Margin="5" Click="btnDeleteItem_Click" />
<TextBox Name="tbText"
Text="{Binding Path=Text}"
Width="200"
TextWrapping="Wrap"
AcceptsReturn="True"
Margin="5"
Focusable="True"/>
<DatePicker Text="{Binding Path=Date}" Margin="5"/>
</WrapPanel>
</Grid>
</DataTemplate>
Using the binding syntax in C# will not work, because that is how bindings are described in XAML. It might work to create a new instance of System.Windows.Data.Binding
, and set its properties to the equivalent of what you would set in XAML, but I'm not sure what ElementName
that you would be able to bind to in order to get this working correctly.
The only other option that I can think of is to add a handler for the SelectionChanged
event, and set the focus manually, but that doesn't sound like the solution that you want.
Upon further inspection, I don't think that a Binding solution is possible. FocusManager.FocusedElement
is an IInputElement
, which doesn't have any members related to binding. I think that you need to traverse the visual tree in order to find the first object that is focusable. Something like this should work:
// Event handler for the ListBox.SelectionChanged event
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox listBox = sender as ListBox;
ItemContainerGenerator = generator.listBox.ItemContainerGenerator;
ListBoxItem selectedItem =
(ListBoxItem)generator.ContainerFromIndex(listBox.SelectedIndex);
IInputElement firstFocusable = FindFirstFocusableElement(selectedItem);
firstFocusable.Focus();
}
private IInputElement FindFirstFocusableElement(DependencyObject obj)
{
IInputElement firstFocusable = null;
int count = VisualTreeHelper.GetChildrenCount(obj);
for(int i = 0; i < count && null == firstFocusable; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
IInputElement inputElement = child as IInputElement;
if(null != inputElement && inputElement.Focusable)
{
firstFocusable = inputElement;
}
else
{
firstFocusable = FindFirstFocusableElement(child);
}
}
return firstFocusable;
}