views:

1882

answers:

1

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"&gt;
   <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?

+1  A: 

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;
}
Andy
well even if I wanted to do it manually in c# I would need to get the reference to the textbox from the Datatemplate and with a little bad luck there will be more than one cascaded datatemplates inside so I would need to search for it...It has to be possible to get the focus to the first input.
Sven Hecht
Strange that a thing as basic as this is not as elegant as the rest of WPF. I still hope to get an easy XAML based answer perhaps with .net 4.0. I hope you understand why I won't mark your post as the answer even though I will probably use a variation of that solution.
Sven Hecht
I understand. A few days ago I was playing around with a potential solution (FocusManager.FocusedElement is an attached property to most controls), but I still couldn't get anything to work.
Andy