【问题标题】:UWP: Disable *specific* ListViewItem's selection with keyboard arrowsUWP:使用键盘箭头禁用*特定* ListViewItem 的选择
【发布时间】:2018-04-11 21:11:19
【问题描述】:

我有一个 AutoSuggestBox。当我输入一些内容时,它会给我一个“建议”列表。该列表是 Popup 内的 ListView。无论哪种方式,我都希望禁用列表中的某些项目,因此用户无法选择它们。

我做得很好,我的实现如下:

<AutoSuggestBox x:Name="AutoSuggest" ...
                ItemContainerStyle="{StaticResource 
                MyPopUpListViewItemStyle}"
/>


<Style x:Key="MyPopUpListViewItemStyle" TargetType="ListViewItem">
...
        <Setter Property="helpers:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <helpers:SetterValueBindingHelper
                    Property="IsEnabled"
                    Binding="{Binding Path=Content.IsItemEnabled, RelativeSource={RelativeSource Self}}" />
            </Setter.Value>
        </Setter>
...
</Style>

我在样式内绑定属性时遇到了一些问题。但现在一切正常,所有 ListViewItems 的“IsEnabled”属性都绑定到其 Content 中的一个属性。所以现在,用户不能用鼠标选择特定的项目。

我的问题是!虽然用户不能用鼠标选择一个项目,但他仍然可以通过向上 ↑ 和向下 ↓ 箭头来选择它(而不只是将它们设置为选中,而是实际使用 Enter 选择)。我希望用户跳过禁用的项目(仍然可以使用箭头选择常规项目)。

我搜索了很长一段时间,我确实找到了一个看起来不错的解决方案,将 ListViewItem 中的“Focusable”属性绑定到我自己的任何属性,但这只是 WPF,因为我的 ListViewItem 没有“Focusable”属性。

所有可能的 ListViewItem 属性,包括:“AllowFocusOnInteraction”、“IsTabStop”、“AllowFocusWhenDisabled”、“IsHitTestVisible”和其他逻辑相关的东西都不起作用。

【问题讨论】:

  • 为什么不过滤无用的项目不显示在列表中?您能否也提供SetterValueBindingHelper 课程?
  • 好吧,我希望用户看到它们并认为它们被禁用是有原因的(我在 listviewitem 中有一个指示图标,说明该项目被禁用的原因)。 SetterValueBindingHelper 与问题无关,它只是帮助将属性绑定到样式中,没有它就无法工作。老实说,我已经解决了问题,只是暂时没有时间回答我自己的问题

标签: c# listview uwp listviewitem


【解决方案1】:

经过几个小时的努力,我找到了解决问题的方法。该解决方案的工作方式与我在问题中发布的内容有所不同。它不会跳过禁用的项目(当遇到带有箭头键的禁用项目时,会跳到第一个启用的项目)。相反,它允许用户像其他任何项目一样突出显示禁用的项目,但它不允许他使用“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。

【讨论】:

    猜你喜欢
    • 2011-10-10
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多