Как получить доступ к определенному элементу в списке с помощью DataTemplate?
У меня есть ListBox, включая ItemTemplate с 2 StackPanels. Во второй StackPanel есть доступ к TextBox, к которому я хочу получить доступ. (Измените его видимость на true и примите ввод пользователя). Триггером должен быть SelectionChangedEvent. Таким образом, если пользователь нажимает на ListBoxItem, TextBlock становится невидимым, а TextBox становится видимым.
КОД 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>
Я думаю, что есть несколько способов решить эту проблему, но ничего, что я пробовал, не сработало.
Мой нынешний подход выглядит так
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;
}
5 ответов
Спасибо за вашу помощь, ребята! Наконец то я понял. Решил проблему с VisualTreeHelper. Какая замечательная функция ^^
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;
}
С помощью этой отредактированной функции вы также можете искать элемент управления по имени (он преобразован из 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;
}
Я не могу дать вам полный ответ...
Но я думаю, что вы можете использовать VisualTreeHelper для перебора дочерних элементов любого элемента управления http://blogs.msdn.com/b/kmahone/archive/2009/03/29/visualtreehelper.aspx
Однако, для эффекта, который вы ищете, тогда я думаю, что использование стиля SelectedItem могло бы быть лучшим решением - например, см. Эту статью - http://joshsmithonwpf.wordpress.com/2007/07/30/customizing-the-selected-item-in-a-listbox/
Использование 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");
}
}
Поскольку DataTemplate является универсальным шаблоном, который можно многократно использовать в коде, нет доступа к нему по имени (x:Name="numberTextBox").
Я решил аналогичную проблему, создав коллекцию элементов управления - во время заполнения Listbox я добавляю элемент управления Textbox в коллекцию.
string text = myCollectionOfTextBoxes[listbox.SelectedIndex].Text;
Пока я не нашел лучшего решения - свойство Tag. В вашем ListboxItem вы привязываете свойство Tag к имени
Tag="{Binding Name}"
и доступ к нему
ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem;
string name = listBoxItem.Tag.ToString();