我有一个显示文本框列表的WPF ListBox。当我点击文本框时,ListBox的选择不会改变。我必须在文本框旁边单击才能选择ListBox项。是否需要设置某个属性使文本框将点击事件转发到ListBox?
我有一个显示文本框列表的WPF ListBox。当我点击文本框时,ListBox的选择不会改变。我必须在文本框旁边单击才能选择ListBox项。是否需要设置某个属性使文本框将点击事件转发到ListBox?
<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;
}
请确保使用适当的 TargetType:ListViewItem、ListBoxItem 或 TreeViewItem。
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="true" />
</Trigger>
</Style.Triggers>
</Style>
我没有足够的声望来评论,所以我将我的评论作为答案发布。Grazer在上面提供的解决方案不能在以下情况下工作:当您拥有另一个控件(如Button
)需要SelectedItem
时。这是因为根据Style Trigger
,当您单击该Button
时,IsKeyboardFocusWithin
变为false,并且SelectedItem
变为null。
我将使用类似于Robert的解决方案,但不需要代码后台(使用附加行为)。
具体做法如下:
首先,创建一个名为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属性的setter。您应该在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>
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof (ListViewItem),
ListViewItem.PreviewGotKeyboardFocusEvent,
new RoutedEventHandler((x,_) => (x as ListViewItem).IsSelected = true));
}
有没有需要设置的属性,使得文本框可以将点击事件转发到列表框?
这不是一个简单的属性,但你可以在你的TextBox
上处理GotFocus
事件,然后使用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;
}
虽然讨论过时,但也许我的回答能帮助其他人...
Ben的解决方案和Grazer的解决方案有同样的问题。糟糕的是,选择取决于文本框的[键盘]焦点。如果在对话框上有另一个控件(例如按钮),则单击按钮时将失去焦点,并且列表框项将变为未选定状态(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"/>
GridView > GridViewColumn > TextBox
的ListView上进行了测试。<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>
private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
((sender as FrameworkElement).TemplatedParent as ListBoxItem).IsSelected = true;
}
XAML
...
<ComboBox PreviewMouseDown="Element_PreviewMouseDown"/>
<TextBox PreviewMouseDown="Element_PreviewMouseDown"/>
...