简体   繁体   English

UWP:使用键盘箭头禁用* specific * ListViewItem的选择

[英]UWP: Disable *specific* ListViewItem's selection with keyboard arrows

I have an AutoSuggestBox. 我有一个AutoSuggestBox。 When I type something in, it gives me a list of "suggestions". 当我输入内容时,它会给我一个“建议”列表。 The list is a ListView inside a Popup. 该列表是Popup内部的ListView。 Either way, I wanted some of the items in the list to be disabled, so user can't choose them. 无论哪种方式,我都希望禁用列表中的某些项目,因此用户无法选择它们。

I did it just fine, my implementation is as follows: 我做的很好,我的实现如下:

<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>

I had some problem with binding property inside a style. 我在样式内绑定属性时遇到了一些问题。 But everything works fine now, all the ListViewItems' "IsEnabled" property are binded to a property inside its Content. 但是现在一切正常,所有ListViewItems的“ IsEnabled”属性都绑定到其Content内部的属性。 So now, user can't choose specific items with a mouse. 因此,现在,用户无法使用鼠标选择特定项目。

My problem is! 我的问题是! Although user can't choose an item with a mouse, he still can choose it with Up ↑ and Down ↓ arrows (and not just set them selected, but actually choose with Enter). 尽管用户无法用鼠标选择一个项目,但他仍然可以使用向上↑和向下↓箭头来选择它(不仅将它们设置为选中状态,而是使用Enter键进行选择)。 I want the user to skip the disabled items (still being able to use the arrows to choose the regular items). 我希望用户跳过禁用的项目(仍然能够使用箭头选择常规项目)。

I searched for quite a while, I did find a nice looking solution, to bind "Focusable" property from ListViewItem to whatever property of my own, but it's WPF only, since there's no "Focusable" property for my ListViewItem. 我搜索了很长时间,确实找到了一个不错的解决方案,将ListViewItem的“ Focusable”属性绑定到我自己的任何属性,但这仅是WPF,因为ListViewItem没有“ Focusable”属性。

All the possible ListViewItem properties, including: "AllowFocusOnInteraction", "IsTabStop", "AllowFocusWhenDisabled", "IsHitTestVisible" and other logically relevant things didn't work. 所有可能的ListViewItem属性,包括:“ AllowFocusOnInteraction”,“ IsTabStop”,“ AllowFocusWhenDisabled”,“ IsHitTestVisible”和其他与逻辑相关的东西均不起作用。

I've found a solution to the problem after several hours of struggling. 经过数小时的努力,我找到了解决问题的方法。 The solution works some way different from what I posted in the question. 该解决方案的工作方式与我在问题中发布的方式有所不同。 It doesn't skip on disabled items (skipping to the first enabled item when encountering a disabled one with arrow keys). 它不会跳过禁用项(遇到带有箭头键的禁用项时,跳到第一个启用项)。 Instead, it lets the user have a disabled item highlighted just as any other, but it doesn't let him select it with "Enter" key. 取而代之的是,它使用户可以像其他任何一个项目一样突出显示一个禁用的项目,但不允许他使用“ Enter”键选择它。 Either way, the user can understand that the item is disabled, first because it's greyed out (since its "IsEnabled" is set to false), and second, I made the foreground of the text inside a disabled ItemListView be Red in color. 无论哪种方式,用户都可以理解该项目已被禁用,首先是因为它是灰色的(因为它的“ IsEnabled”设置为false),其次,我使禁用了ItemListView内的文本的前景为红色。

It doesn't let the user select the item with "Enter" by simply catching the "Enter" in the KeyDown method and returning "without doing nothing". 它不允许用户通过简单地在KeyDown方法中捕获“ Enter”并返回“不执行任何操作”来选择带有“ Enter”的项目。 The problem is where to get the needed KeyDown method. 问题是从哪里获取所需的KeyDown方法。

I have my own Style for AutoSuggestBox (which is mostly the original Windows' AutoSuggestBox, I don't recall anyone changing anything about it), which goes as follows: 我对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>

As one can see, the idea of AutoSuggestBox is a TextBox with search, and a Popup on the same level with it. 可以看到,AutoSuggestBox的想法是带有搜索的TextBox,以及与之处于同一级别的Popup。 The Popup contains a ListView of "suggestions" according to the text in the TextBox above it. 弹出窗口根据其上方的文本框中的文本包含“建议”的ListView。 They go by names "TextBox" & "SuggestionsList". 它们的名称分别为“ TextBox”和“ SuggestionsList”。

The TextBox (that goes by the original name "TextBox") is the main culprit which you want to catch the "KeyDown" event onto. TextBox(其原始名称为“ TextBox”)是要捕获“ KeyDown”事件的主要元凶。 The idea is that when you go through the list of suggestions with up-down arrow keys, your focus always stays on that same textbox and not on the listview or whatever else. 这个想法是,当您使用上下箭头键浏览建议列表时,您的焦点始终停留在同一文本框中,而不是列表视图或其他任何内容上。

So what I've done is added a Behavior to the TextBox (the one in the style above), as follows: 因此,我完成的操作是在文本框(上面的样式中的一个)中添加了一个Behavior,如下所示:

    <TextBox x:Name="TextBox"
             ... >
        <interactivity:Interaction.Behaviors>
            <TheNamespaceContainingClass:BehaviorClassName />
        </interactivity:Interaction.Behaviors>
    </TextBox>

The Behavior class code: I added several comments to further explain the important parts 行为类代码:我添加了一些注释以进一步解释重要部分

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;
    }
}

It might not be the most simple explanation and solution, but the problem is not quite simple either. 这可能不是最简单的解释和解决方案,但是问题也不是那么简单。 Hope that you can figure the whole idea if you encounter this specific problem. 希望您在遇到此特定问题时能够理解整个想法。 The whole problem can be solved in simpler way if you don't follow MVVM and/or don't care about the quality, but the main idea stays the same. 如果您不遵循MVVM和/或不关心质量,则可以用更简单的方法解决整个问题,但是主要思想保持不变。

Also, the SetterValueBindingHelper I posted in the question is taken from this blog. 另外,我在问题中发布的SetterValueBindingHelper也来自博客。 Many thanks to its author, SuperJMN. 非常感谢其作者SuperJMN。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM