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();
No puedo darte una respuesta completa ...
Pero creo que puede usar el VisualTreeHelper para iterar a través de los elementos secundarios de cualquier control http://blogs.msdn.com/b/kmahone/archive/2009/03/29/visualtreehelper.aspx
Sin embargo, para el efecto que está buscando, entonces creo que usar el estilo SelectedItem podría ser una mejor solución. Por ejemplo, vea este artículo: http://joshsmithonwpf.wordpress.com/2007/07/30/customizing-the-selected- item-in-a-listbox /
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;
}