47

我有一个显示文本框列表的 wpf 列表框。当我单击文本框时,列表框选择不会改变。我必须单击 TextBox 旁边的以选择列表框项。我需要为文本框设置一些属性以将点击事件转发到列表框吗?

4

15 回答 15

48

我们使用以下样式来设置一个 PreviewGotKeyboardFocus 来处理 TextBox 控件和 ComboBoxes 等的所有事件:

    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
        </Style>
    </ListView.ItemContainerStyle>

然后我们选择后面代码中的行:

    protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e)
    {
        ListViewItem item = (ListViewItem) sender;
        item.IsSelected = true;
    }
于 2009-05-14T12:55:11.313 回答
40

请务必使用适当的 TargetType:ListViewItem、ListBoxItem 或 TreeViewItem。

<Style TargetType="ListViewItem">
    <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="true">
            <Setter Property="IsSelected" Value="true" />
        </Trigger>
    </Style.Triggers>
</Style>
于 2011-11-03T05:03:42.350 回答
6

我没有足够的代表发表评论,所以我发布我的评论作为答案。Grazer 的上述解决方案在您有另一个控件(例如 aButton需要SelectedItem. 这是因为根据,当您单击该 时Style Trigger,变为假,而变为空。IsKeyboardFocusWithinButtonSelectedItem

于 2013-12-23T01:07:43.480 回答
4

我使用了类似于罗伯特的解决方案,但没有后面的代码(使用附加行为)。

为此,

第一的。创建单独的类 FocusBehaviour:


using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyBehaviours
{
    public class FocusBehaviour
    {
        #region IsFocused
        public static bool GetIsFocused(Control control)
        {
            return (bool) control.GetValue(IsFocusedProperty);
        }

        public static void SetIsFocused(Control control, bool value)
        {
            control.SetValue(IsFocusedProperty, value);
        }

        public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached(
            "IsFocused", 
            typeof(bool),
            typeof(FocusBehaviour), 
            new UIPropertyMetadata(false, IsFocusedPropertyChanged));

        public static void IsFocusedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = sender as Control;
            if (control == null || !(e.NewValue is bool))
                return;
            if ((bool)e.NewValue && !(bool)e.OldValue)
                control.Focus();
        }

        #endregion IsFocused

        #region IsListBoxItemSelected

        public static bool GetIsListBoxItemSelected(Control control)
        {
            return (bool) control.GetValue(IsListBoxItemSelectedProperty);
        }

        public static void SetIsListBoxItemSelected(Control control, bool value)
        {
            control.SetValue(IsListBoxItemSelectedProperty, value);
        }

        public static readonly DependencyProperty IsListBoxItemSelectedProperty = DependencyProperty.RegisterAttached(
            "IsListBoxItemSelected", 
            typeof(bool),
            typeof(FocusBehaviour), 
            new UIPropertyMetadata(false, IsListBoxItemSelectedPropertyChanged));

        public static void IsListBoxItemSelectedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = sender as Control;
            DependencyObject p = control;
            while (p != null && !(p is ListBoxItem))
            {
                p = VisualTreeHelper.GetParent(p);
            } 

            if (p == null)
                return;

            ((ListBoxItem)p).IsSelected = (bool)e.NewValue;
        }

        #endregion IsListBoxItemSelected
    }
}

第二。在资源部分添加一个样式(我的样式在焦点上是圆形的黑色)。注意 FocusBehaviour.IsListBoxItemSelected 属性的设置器。你应该参考它xmlns:behave="clr-namespace:MyBehaviours"

`

    <Style x:Key="PreviewTextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="Background" Value="White"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border
                        Margin="6,2,0,4"
                        BorderBrush="#FFBDBEBD"
                        BorderThickness="1"
                        CornerRadius="8"
                        Background="White"
                        VerticalAlignment="Stretch"
                        HorizontalAlignment="Stretch"
                        MinWidth="100"
                        x:Name="bg">
                        <ScrollViewer 
                            x:Name="PART_ContentHost" 
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsKeyboardFocusWithin" Value="True">
                            <Setter Property="Background" TargetName="bg" Value="Black"/>
                            <Setter Property="Background" Value="Black"/><!-- we need it for caret, it is black on black elsewise -->
                            <Setter Property="Foreground" Value="White"/>
                            <Setter Property="behave:FocusBehaviour.IsListBoxItemSelected" Value="True"/>
                        </Trigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

`

第三。(可选,用于反向任务)

您将遇到(如果没有的话)反向任务 - 当 ListBoxItem 被选中时专注于 TextBox。我建议使用 Behavior 类的另一个属性 IsFocused。这是一个示例模板ListBoxItem,请注意Property="behave:FocusBehaviour.IsFocused"FocusManager.IsFocusScope="True"

    <DataTemplate x:Key="YourKey" DataType="{x:Type YourType}">
            <Border
            Background="#FFF7F3F7"
            BorderBrush="#FFBDBEBD"
            BorderThickness="0,0,0,1"
            FocusManager.IsFocusScope="True"
            x:Name="bd"
            MinHeight="40">
                <TextBox
                    x:Name="textBox"
                    Style="{StaticResource PreviewTextBox}"
                    Text="{Binding Value}" />
        </Border>
        <DataTemplate.Triggers>
            <DataTrigger
                Binding="{Binding IsSelected,RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
                Value="True">
                <Setter
                    TargetName="textBox"
                    Property="behave:FocusBehaviour.IsFocused" 
                    Value="True" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
于 2010-11-09T22:34:36.660 回答
3

我使用类处理程序来设置此行为。这样做将修复应用程序中的所有列表视图。我不知道为什么这不是默认行为。

在您的 App.xaml.cs 中,将以下内容添加到 OnStartup:

protected override void OnStartup(StartupEventArgs e)
    {
        EventManager.RegisterClassHandler(typeof (ListViewItem), 
                                          ListViewItem.PreviewGotKeyboardFocusEvent,
                                          new RoutedEventHandler((x,_) => (x as ListViewItem).IsSelected = true));
    }
于 2011-06-16T02:01:22.797 回答
2

我需要为文本框设置一些属性以将点击事件转发到列表框吗?

这不是一个简单的属性,但您可以在GotFocus上处理事件TextBox,然后使用VisualTreeHelper查找ListBoxItem并选择它:

private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
    TextBox myTextBox = sender as TextBox;
    DependencyObject parent = VisualTreeHelper.GetParent(myTextBox);
    while (!(parent is ListBoxItem))
    {
        parent = VisualTreeHelper.GetParent(parent);
    }
    ListBoxItem myListBoxItem = parent as ListBoxItem;
    myListBoxItem.IsSelected = true;
}
于 2009-03-17T16:01:09.443 回答
1

这是您正在寻找的答案:当其内部 ComboBox 聚焦时选择一个 ListBoxItem

于 2009-10-27T10:03:38.163 回答
1

我能找到的最简单的方法是使用 PreviewMouseDown 事件并设置模板化父级的 IsSelected 属性。由于预览事件冒泡,一旦用户单击文本框、组合框或您设置事件的任何其他控件,ListBoxItem 就会处理该事件。

关于这一点的一件好事是,您可以对所有类型的控件使用相同的事件,因为它们都派生自 Framework 元素。此外,当您将列表框的 SelectionMode 设置为“扩展”时,设置 IsSelected(而不是设置 SelectedItem)将导致选择多个项目,这可能是也可能不是您要查找的内容。

IE:

c# 代码

private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    ((sender as FrameworkElement).TemplatedParent as ListBoxItem).IsSelected = true;
}

xml

    ...
    <ComboBox PreviewMouseDown="Element_PreviewMouseDown"/>
    <TextBox PreviewMouseDown="Element_PreviewMouseDown"/>
    ...
于 2009-04-04T00:26:34.310 回答
1

旧的讨论,但也许我的回答对其他人有帮助....

Ben 的解决方案与 Grazer 的解决方案存在相同的问题。不好的是,选择取决于文本框的 [keyboard] 焦点。如果您的对话框上有另一个控件(即按钮),则单击按钮时焦点会丢失,并且列表框项变为未选中(SelectedItem == null)。因此,单击项目(在文本框外)和单击文本框有不同的行为。这处理起来很繁琐,看起来很奇怪。

我很确定对此没有纯 XAML 解决方案。为此,我们需要代码隐藏。解决方案与 Mark 建议的很接近。

(在我的示例中,我使用 ListViewItem 而不是 ListBoxItem,但该解决方案适用于两者)。

代码隐藏:

private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        var frameworkElement = sender as FrameworkElement;
        if (frameworkElement != null)
        {
            var item = FindParent<ListViewItem>(frameworkElement);
            if (item != null)
                item.IsSelected = true;
        }
    }

使用 FindParent(取自http://www.infragistics.com/community/blogs/blagunas/archive/2013/05/29/find-the-parent-control-of-a-specific-type-in ​​-wpf-and -silverlight.aspx):

public static T FindParent<T>(DependencyObject child) where T : DependencyObject
    {
        //get parent item
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        T parent = parentObject as T;
        if (parent != null)
            return parent;

        return FindParent<T>(parentObject);
    }

在我的数据模板中:

<TextBox Text="{Binding Name}"
        PreviewMouseDown="Element_PreviewMouseDown"/>
于 2015-01-08T22:02:19.343 回答
1

以下是@Ben 的答案的简化,无需覆盖DataTemplate。它甚至可以用作静态样式。使用包含GridView > GridViewColumn > TextBox.

例子:

<ListView.Resources>
    <Style TargetType="{x:Type ListViewItem}">
        <Style.Triggers>
            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                <Setter Property="IsSelected" Value="True"></Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
</ListView.Resources>
于 2017-01-31T20:50:27.177 回答
1

基于Arcturus Answer的附加行为,使其可重用并且不隐藏在后面的代码中。

创建具有附加行为的文件(= 附加属性)

public static class SelectListBoxItemWhenControlInsideTheItemIsClickedBehavior
{
    public static readonly DependencyProperty EnableProperty = DependencyProperty.RegisterAttached(
        "Enable",
        typeof(bool),
        typeof(SelectListBoxItemWhenControlInsideTheItemIsClickedBehavior),
        new FrameworkPropertyMetadata(false, OnEnableChanged));


    public static bool GetEnable(FrameworkElement frameworkElement)
    {
        return (bool)frameworkElement.GetValue(EnableProperty);
    }


    public static void SetEnable(FrameworkElement frameworkElement, bool value)
    {
        frameworkElement.SetValue(EnableProperty, value);
    }


    private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ( d is ListBoxItem listBoxItem)
            listBoxItem.PreviewGotKeyboardFocus += ListBoxItem_PreviewGotKeyboardFocus;
    }

    private static void ListBoxItem_PreviewGotKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
    {
        var listBoxItem = (ListBoxItem)sender;
        listBoxItem.IsSelected = true;
    }
}

将其添加到您喜欢的资源中。

例如<Windows.Resources>

<Window.Resources>
    <Style TargetType="ListViewItem">
        <Setter Property="myBehavior:SelectListBoxItemWhenControlInsideTheItemIsClickedBehavior.Enable" Value="true"/>
    </Style>
</Window.Resources>

添加命名空间

如果 Visual Studio 不够聪明,无法自动添加它。例如,当您的项目名为“MyApp”并且您将文件保存在文件夹“MyBehaviors”中时,命名空间将位于 Window 下:

<Window
    xmlns:myBehavior="clr-namespace:MyApp.MyBehaviors"
    >
于 2019-11-16T17:46:55.450 回答
0

您对您的初始情况不是很具体。但我假设您使用 DataBinding 和 ItemTemplate。这是一种简单的方法,如果你是这个主题的初学者。这应该有效:

<ListBox ItemsSource="{Binding someDataCollection}" Name="myListBox">
   <ListBox.ItemTemplate>
      <DataTemplate>
         <TextBox Text="{Binding datafield}" Tag="{Binding .}"
                  GotFocus="TextBox_GotFocus"/>
      </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>

private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
   myListBox.SelectedItem = (sender as TextBox).Tag; /* Maybe you need to cast to the type of the objects contained in the collection(bound as ItemSource above) */
}
于 2009-03-26T02:03:25.623 回答
0

我不完全确定您是否要按照上一个答案中的描述直接设置选择,因为我认为它会破坏多选和其他一些场景

. 您可能想尝试重新设置如下按钮的样式,看看会发生什么。

<Button ClickMode="Pressed" Focusable="False">
<Button.Template>
    <ControlTemplate>  // change the template to get rid of all the default chrome 
        <Border Background="Transparent"> // Button won't be clickable without some kind of background set
            <ContentPresenter />
        </Border>
    </ControlTemplate>
</Button.Template>
<TextBox />

于 2009-03-20T16:44:36.443 回答
0

列表框处理项目选择,但不知道嵌入其中的文本框的焦点。如果您想在文本框获得输入焦点时更改选择,那么您需要手动更改列表框选择,afaik。

于 2009-03-17T09:34:22.257 回答
0

试试这个代码:

foreach (object item in this.listBox1.Items) {
    if (textbox1.text.equals(item.toString())) {
        //show error message; break
    }
}
于 2012-06-28T06:00:36.787 回答