c# xaml windows-phone-7 silverlight-4.0

c# - ¿Cómo acceder a un elemento específico en un Listbox con DataTemplate?



xaml windows-phone-7 (5)

Tengo un ListBox que incluye una ItemTemplate con 2 StackPanels. Hay un TextBox en el segundo StackPanel al que quiero acceder. (Cambie su visibilidad a verdadera y acepte la entrada del usuario) El desencadenador debe ser SelectionChangedEvent. Entonces, si un usuario hace clic en un ListBoxItem, el TextBlock se vuelve invisible y el TextBox se vuelve visible.

CÓDIGO XAML:

<ListBox Grid.Row="1" Name="ContactListBox" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Contacts}" Margin="0,36,0,0" SelectionChanged="ContactListBox_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Margin="0,0,0,0"> <toolkit:ContextMenuService.ContextMenu> <toolkit:ContextMenu> <toolkit:MenuItem Header="Edit Contact" Click="ContactMenuItem_Click"/> <toolkit:MenuItem Header="Delete Contact" Click="ContactMenuItem_Click"/> </toolkit:ContextMenu> </toolkit:ContextMenuService.ContextMenu> <Grid> <Rectangle Fill="{StaticResource PhoneAccentBrush}" Width="72" Height="72"> <Rectangle.OpacityMask> <ImageBrush ImageSource="/Images/defaultContactImage.png" Stretch="UniformToFill"/> </Rectangle.OpacityMask> </Rectangle> </Grid> <StackPanel> <TextBox Text="{Binding Name}" TextWrapping="Wrap" Visibility="Collapsed"/> <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" /> <TextBlock Text="{Binding Number}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextAccentStyle}"/> </StackPanel> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

Supongo que hay varias formas de resolver esto, pero nada de lo que probé funcionó.

Mi enfoque actual se ve así

private void ContactListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem; DataTemplate listBoxTemplate = listBoxItem.ContentTemplate; // How to access the DataTemplate content? StackPanel outerStackPanel = listBoxTemplate.XXX as StackPanel; StackPanel innerStackPanel = outerStackPanel.Children[1] as StackPanel; TextBox nameBox = innerStackPanel.Children[0] as TextBox; TextBlock nameBlock = innerStackPanel.Children[1] as TextBlock; nameBox.Visibility = System.Windows.Visibility.Visible; nameBlock.Visibility = System.Windows.Visibility.Collapsed; }


Dado que DataTemplate es una plantilla genérica que podría usarse muchas veces en el código, no hay forma de acceder a ella por su nombre (x: Name = "numberTextBox").

Resolví un problema similar al hacer una colección de controles: mientras Listbox se estaba poblando, agregué el control Textbox a la colección.

string text = myCollectionOfTextBoxes[listbox.SelectedIndex].Text;

Hasta que encontré una alma mejor - Propiedad de etiqueta. En su ListboxItem enlaza la propiedad Tag al nombre

Tag="{Binding Name}"

y el para acceder

ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem; string name = listBoxItem.Tag.ToString();



Use ItemContainerGenerator .

private void ContactListBox_SelectionChanged (object sender, SelectionChangedEventArgs e) { if (e.AddedItems.Count == 1) { var container = (FrameworkElement)ContactListBox.ItemContainerGenerator. ContainerFromItem(e.AddedItems[0]); StackPanel sp = container.FindVisualChild<StackPanel>(); TextBox tbName = (TextBox) sp.FindName("tbName"); TextBlock lblName = (TextBlock)sp.FindName("lblName"); TextBlock lblNumber = (TextBlock)sp.FindName("lblNumber"); } }


¡¡Gracias por su ayuda chicos!! Finalmente lo tengo. Resolvió el problema con VisualTreeHelper. Qué gran función ^^

private void ContactListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (ContactListBox.SelectedIndex == -1) return; currentSelectedListBoxItem = this.ContactListBox.ItemContainerGenerator.ContainerFromIndex(ContactListBox.SelectedIndex) as ListBoxItem; if (currentSelectedListBoxItem == null) return; // Iterate whole listbox tree and search for this items TextBox nameBox = helperClass.FindDescendant<TextBox>(currentSelectedListBoxItem); TextBlock nameBlock = helperClass.FindDescendant<TextBlock>(currentSelectedListBoxItem);

helperFunction

public T FindDescendant<T>(DependencyObject obj) where T : DependencyObject { // Check if this object is the specified type if (obj is T) return obj as T; // Check for children int childrenCount = VisualTreeHelper.GetChildrenCount(obj); if (childrenCount < 1) return null; // First check all the children for (int i = 0; i < childrenCount; i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child is T) return child as T; } // Then check the childrens children for (int i = 0; i < childrenCount; i++) { DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i)); if (child != null && child is T) return child as T; } return null; }


Con esta función editada también puede buscar el control por nombre (se convierte de VB.NET):

public T FindDescendantByName<T>(DependencyObject obj, string objname) where T : DependencyObject { string controlneve = ""; Type tyype = obj.GetType(); if (tyype.GetProperty("Name") != null) { PropertyInfo prop = tyype.GetProperty("Name"); controlneve = prop.GetValue((object)obj, null); } else { return null; } if (obj is T && objname.ToString().ToLower() == controlneve.ToString().ToLower()) { return obj as T; } // Check for children int childrenCount = VisualTreeHelper.GetChildrenCount(obj); if (childrenCount < 1) return null; // First check all the children for (int i = 0; i <= childrenCount - 1; i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child is T && objname.ToString().ToLower() == controlneve.ToString().ToLower()) { return child as T; } } // Then check the childrens children for (int i = 0; i <= childrenCount - 1; i++) { string checkobjname = objname; DependencyObject child = FindDescendantByName<T>(VisualTreeHelper.GetChild(obj, i), objname); if (child != null && child is T && objname.ToString().ToLower() == checkobjname.ToString().ToLower()) { return child as T; } } return null; }