WPF - текстовое поле ListBox ContentTemplate MouseDown, в результате чего элемент списка не будет выбран
РЕЗЮМЕ: Нажмите на элемент списка, текстовое поле в DataTemplate будет сфокусировано, но элемент listBox не выбран.
Я уверен, что это как-то связано с буллингом событий, но мне здесь чего-то не хватает.
У меня есть ListBox. ContentTemplate каждого ListBoxItem присваивается DataTemplate, который содержит простое текстовое поле.
Этот TextBox разработан как поддельная редактируемая метка.
Проблема: при щелчке по текстовому полю выбранный элемент списка не обновляется. Текстовое поле проглатывает событие mousedown, и в списке никогда не появляется уведомление об обновлении до нового элемента.
Я чувствую, что мне здесь не хватает чего-то глупого. Есть идеи? Есть ли способ заставить событие пузыриться до родительского ListView?
Я перепробовал все, от создания фона текстового поля Null до обработки события previewmousedown и установки e.handled = false;.
DataTemplate:
<DataTemplate x:Key="ItemTempl">
<TextBox Height="20" Width="200" Name="tbox" Text="{Binding WordText}" HorizontalAlignment="Stretch">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="{x:Null}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsFocused, ElementName=tbox}" Value="True">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
Посмотреть список:
<ListView HorizontalAlignment="Stretch" ItemsSource="{Binding Something.Words}" Name="MainListView" SelectedItem="{Binding CurrentItem, Mode=TwoWay}" BorderThickness="0" ItemContainerStyle="{StaticResource ContainerStyle}">
</ListView>
2 ответа
Я обошел эту конкретную проблему с ListView, создав свой собственный просмотр списка, который обрабатывал событие предварительного просмотра мыши вниз и выбрал элемент, вы могли бы адаптировать это к вашей ситуации, вероятно, было бы лучше сделать это в присоединенном свойстве, чтобы вы не должны создать новый класс.
Я в основном ищу исходный источник мыши, который является текстовым полем, использую помощник визуального дерева, чтобы проследить его визуальное дерево до самого элемента представления списка, в котором он находится, и выбрать его.
public class MyListView : ListView
{
protected override void OnPreviewMouseDown(System.Windows.Input.MouseButtonEventArgs e)
{
DependencyObject listViewItem = (DependencyObject)e.OriginalSource;
while (listViewItem != null && !(listViewItem is ListViewItem))
listViewItem = VisualTreeHelper.GetParent(listViewItem);
SelectedItem = ((ListViewItem)listViewItem).Content;
base.OnPreviewMouseDown(e);
}
}
РЕДАКТИРОВАТЬ: вот прикрепленная версия свойства.
public class ListViewExtras : DependencyObject
{
public static bool GetWillAlwaysSelect(DependencyObject obj)
{
return (bool)obj.GetValue(WillAlwaysSelectProperty);
}
public static void SetWillAlwaysSelect(DependencyObject obj, bool value)
{
obj.SetValue(WillAlwaysSelectProperty, value);
}
// Using a DependencyProperty as the backing store for WillAlwaysSelect. This enables animation, styling, binding, etc...
public static readonly DependencyProperty WillAlwaysSelectProperty =
DependencyProperty.RegisterAttached("WillAlwaysSelect", typeof(bool), typeof(ListViewExtras), new PropertyMetadata(false, new PropertyChangedCallback((s, e) =>
{
ListView listView = s as ListView;
if (listView != null)
{
if ((bool)e.NewValue) listView.PreviewMouseDown += listView_PreviewMouseDown;
if (!(bool)e.NewValue && (bool)e.OldValue) listView.PreviewMouseDown -= listView_PreviewMouseDown;
}
})));
static void listView_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ListView listView = sender as ListView;
if (listView != null)
{
DependencyObject listViewItem = (DependencyObject)e.OriginalSource;
while (listViewItem != null && !(listViewItem is ListViewItem))
listViewItem = VisualTreeHelper.GetParent(listViewItem);
listView.SelectedItem = ((ListViewItem)listViewItem).Content;
}
}
}
и использовать его с
<ListView HorizontalContentAlignment="Stretch" local:ListViewExtras.WillAlwaysSelect="True">
Я изменил обработчик событий на более общий, поддерживая любой селектор, используя методы ContainerFromElement и ItemContainerGenerator.IndexFromContainer.
private static void OnPreviewListBoxMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var listBox = sender as Selector;
if (listBox != null)
{
DependencyObject mouseItem = e.OriginalSource as DependencyObject;
if (mouseItem != null)
{
// Get the container based on the element
var container = listBox.ContainerFromElement(mouseItem);
if (container != null)
{
var index = listBox.ItemContainerGenerator.IndexFromContainer(container);
Debug.Assert(index >= 0);
listBox.SelectedIndex = index;
}
}
}
}