locked
Correlating Items with controls in a Listbox RRS feed

  • Question

  • I'm getting my feet wet with Expression Blend 3 and I need help figuring out how to do something in Blend and/or WPF. I've got a data structure of custom objects (Task, since my application is a time-management app) displayed in a Listbox via a DataTemplate. The DataTemplate contains some graphics and DateTime info, and the name of the task in a TextBox. I am trying to make the app as keyboard-friendly as possible, so I've added a KeyUp event to my main window: what I want to happen is that when I press Control-R, the selected Task's TextBox becomes editable and focus jumps to the associated TextBox so you can start typing immediately. I've figured out how to do everything except for transferring focus. The Listbox offers a SelectedItems property which gives me access to the selected Task, but how do I get the stuff that was created through the DataTemplate, i.e. the Textbox?

    I tried to Bing the answer but I don't know how to ask the right question. "DataTemplate SelectedItems focus" didn't lead to anything useful.

    -Max


     

    Friday, April 2, 2010 1:20 AM

Answers

  • You will probably need to find the TextBox in the visual tree.  Make sure the TextBox inside the template has an x:Name, such as "TB" in my example below.  Then in your event handler, you can get the ListBoxItem visual from your selectedIndex, and search visually under it for the appropriate visual.  You don't have to do this by name, you would search for object type, etc... plug in whatever search criteria you wish into that method.

    The visual search method below is from: http://flatlinerdoa.spaces.live.com/Blog/cns!17124D03A9A052B0!566.entry?sa=264265005  I started writing it myself and realized someone else had probably already done it :)

    private void KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    		{
    			if (this.MyListBox.SelectedIndex != -1)
    			{
    				var container = this.MyListBox.ItemContainerGenerator.ContainerFromIndex(this.MyListBox.SelectedIndex) as ListBoxItem;
    				var tb = FindVisualChildByName<TextBox>(container, "TB");
    				
    			}
    		}
    
    		public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
    		{
    			for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    			{
    				var child = VisualTreeHelper.GetChild(parent, i);
    				string controlName = child.GetValue(Control.NameProperty) as string;
    				if (controlName == name)
    				{
    					return child as T;
    				}
    				else
    				{
    					T result = FindVisualChildByName<T>(child, name);
    					if (result != null)
    						return result;
    				}
    			}
    			return null;
    		}

    The data template with a name:

    <DataTemplate x:Key="ItemTemplate">
    			<StackPanel>
    				<TextBox x:Name="TB" Text="{Binding Property1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    				<CheckBox IsChecked="{Binding Property2}"/>
    			</StackPanel>
    		</DataTemplate>

    • Marked as answer by Max Wilson Friday, April 2, 2010 4:27 PM
    Friday, April 2, 2010 1:49 PM
    Moderator

All replies

  • You will probably need to find the TextBox in the visual tree.  Make sure the TextBox inside the template has an x:Name, such as "TB" in my example below.  Then in your event handler, you can get the ListBoxItem visual from your selectedIndex, and search visually under it for the appropriate visual.  You don't have to do this by name, you would search for object type, etc... plug in whatever search criteria you wish into that method.

    The visual search method below is from: http://flatlinerdoa.spaces.live.com/Blog/cns!17124D03A9A052B0!566.entry?sa=264265005  I started writing it myself and realized someone else had probably already done it :)

    private void KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    		{
    			if (this.MyListBox.SelectedIndex != -1)
    			{
    				var container = this.MyListBox.ItemContainerGenerator.ContainerFromIndex(this.MyListBox.SelectedIndex) as ListBoxItem;
    				var tb = FindVisualChildByName<TextBox>(container, "TB");
    				
    			}
    		}
    
    		public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
    		{
    			for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    			{
    				var child = VisualTreeHelper.GetChild(parent, i);
    				string controlName = child.GetValue(Control.NameProperty) as string;
    				if (controlName == name)
    				{
    					return child as T;
    				}
    				else
    				{
    					T result = FindVisualChildByName<T>(child, name);
    					if (result != null)
    						return result;
    				}
    			}
    			return null;
    		}

    The data template with a name:

    <DataTemplate x:Key="ItemTemplate">
    			<StackPanel>
    				<TextBox x:Name="TB" Text="{Binding Property1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    				<CheckBox IsChecked="{Binding Property2}"/>
    			</StackPanel>
    		</DataTemplate>

    • Marked as answer by Max Wilson Friday, April 2, 2010 4:27 PM
    Friday, April 2, 2010 1:49 PM
    Moderator
  • Awesome, thank you! ItemContainerGenerator.ContainerFromIndex/FromItem is exactly what I was looking for, and the FindVisualChildByName helper turned out to be useful too because the visual tree was more complex than I expected (e.g. had a ListBoxItem + "Border" element in the place where I was expecting to find just my DataTemplate-created controls).

     

    -Max

    Friday, April 2, 2010 4:29 PM