WPF - ListBox ContentTemplate文本框MouseDown导致列表框项目未被选中

时间:2012-11-27 16:37:03

标签: c# wpf events datatemplate listboxitem

摘要:单击列表框项,DataTemplate中的文本框会聚焦,但未选中listBox项。

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

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

此TextBox旨在显示为伪造的可编辑标签。

问题:单击文本框时,ListBox的selectedItem未更新。文本框正在吞下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 个答案:

答案 0 :(得分: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">

答案 1 :(得分: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;
                }
            }
        }
    }