1

摘要:单击列表框项,DataTemplate 中的文本框获得焦点,但未选择列表框项。

我确定这与事件冒泡有关,但我在这里遗漏了一些东西。

我有列表框。每个 ListBoxItem 的 ContentTemplate 都分配给一个包含简单文本框的 DataTemplate。

此 TextBox 旨在显示为虚假的可编辑标签。

问题:单击文本框时,ListBox 的 selectedItem 没有被更新。文本框正在吞噬 mousedown 事件,并且永远不会通知列表框更新到新项目。

我觉得我在这里错过了一些愚蠢的东西。有任何想法吗?有没有办法强制事件冒泡到父 ListView?

我已经尝试了从使文本框的背景为 Null 到处理 previewmousedown 事件和设置 e.handled = false; 的所有方法。

数据模板:

<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>
4

2 回答 2

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">
于 2012-11-27T16:51:27.483 回答
1

我将事件处理程序更改为更通用,支持使用 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;
                }
            }
        }
    }
于 2013-08-22T20:50:48.227 回答