经过几个小时的努力,我找到了解决问题的方法。该解决方案的工作方式与我在问题中发布的内容有所不同。它不会跳过禁用的项目(当遇到带有箭头键的禁用项目时,会跳到第一个启用的项目)。相反,它允许用户像其他任何项目一样突出显示禁用的项目,但它不允许他使用“Enter”键选择它。无论哪种方式,用户都可以理解该项目被禁用,首先因为它是灰色的(因为它的“IsEnabled”设置为 false),其次,我将禁用的 ItemListView 内的文本的前景设置为红色。
它不会让用户通过简单地在 KeyDown 方法中捕获“Enter”并返回“不做任何事情”来选择带有“Enter”的项目。问题是从哪里获得所需的 KeyDown 方法。
我有自己的 AutoSuggestBox 样式(主要是原始 Windows 的 AutoSuggestBox,我不记得有人对其进行了任何更改),如下所示:
<Style TargetType="AutoSuggestBox">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="AutoSuggestBox">
<Grid>
...
<TextBox x:Name="TextBox"
...
/>
<Popup x:Name="SuggestionsPopup">
<Border x:Name="SuggestionsContainer">
<Border.RenderTransform>
<TranslateTransform x:Name="UpwardTransform" />
</Border.RenderTransform>
<ListView
x:Name="SuggestionsList"
...
>
<ListView.ItemContainerTransitions>
<TransitionCollection />
</ListView.ItemContainerTransitions>
</ListView>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
可以看出,AutoSuggestBox 的概念是一个带有搜索功能的 TextBox,以及一个与之处于同一级别的 Popup。 Popup 根据其上方 TextBox 中的文本包含“建议”的 ListView。它们的名称为“TextBox”和“SuggestionsList”。
TextBox(原名“TextBox”)是您想要捕捉“KeyDown”事件的罪魁祸首。这个想法是,当您使用上下箭头键浏览建议列表时,您的焦点始终停留在同一个文本框上,而不是列表视图或其他任何内容上。
所以我所做的是在TextBox中添加了一个Behavior(上面样式中的那个),如下:
<TextBox x:Name="TextBox"
... >
<interactivity:Interaction.Behaviors>
<TheNamespaceContainingClass:BehaviorClassName />
</interactivity:Interaction.Behaviors>
</TextBox>
行为类代码:
我添加了几个 cmets 来进一步解释重要部分
public class BehaviorClassName: DependencyObject, IBehavior
{
private TextBox _associatedObject;
private ListView _listView;
public DependencyObject AssociatedObject
{
get
{
return _associatedObject;
}
}
public void Attach(DependencyObject associatedObject)
{
_associatedObject = associatedObject as TextBox;
if (_associatedObject != null)
{
_associatedObject.KeyDown -= TextBox_OnKeyDown;
_associatedObject.KeyDown += TextBox_OnKeyDown;
}
}
private void TextBox_OnKeyDown(object sender, KeyRoutedEventArgs e)
{
if (_associatedObject != null)
{
if (_listView == null)
{
// Gets ListView through visual tree. That's a hack of course. Had to put it here since the list initializes only after the textbox itself
_listView = (ListView)((Border)((Popup)((Grid)_associatedObject.Parent).Children[1]).Child).Child;
}
if (e.Key == VirtualKey.Enter && _listView.SelectedItem != null)
{
// Here I had to make sure the Enter key doesn't work only on specific (disabled) items, and still works on all the others
// Reflection I had to insert to overcome the missing reference to the needed ViewModel
if (!((bool)_listView.SelectedItem.GetType().GetProperty("PropertyByWhichIDisableSpecificItems").GetValue(_listView.SelectedItem, null)))
e.Handled = true;
}
}
}
public void Detach()
{
_associatedObject.KeyDown -= TextBox_OnKeyDown;
}
}
这可能不是最简单的解释和解决方案,但问题也不是很简单。如果您遇到这个特定问题,希望您能弄清楚整个想法。如果您不遵循 MVVM 和/或不关心质量,则可以以更简单的方式解决整个问题,但主要思想保持不变。
另外,我在问题中发布的 SetterValueBindingHelper 取自 this 博客。非常感谢它的作者 SuperJMN。