簡體   English   中英

UWP:使用鍵盤箭頭禁用* specific * ListViewItem的選擇

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

我有一個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”和其他與邏輯相關的東西均不起作用。

經過數小時的努力,我找到了解決問題的方法。 該解決方案的工作方式與我在問題中發布的方式有所不同。 它不會跳過禁用項(遇到帶有箭頭鍵的禁用項時,跳到第一個啟用項)。 取而代之的是,它使用戶可以像其他任何一個項目一樣突出顯示一個禁用的項目,但不允許他使用“ 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。 彈出窗口根據其上方的文本框中的文本包含“建議”的ListView。 它們的名稱分別為“ TextBox”和“ SuggestionsList”。

TextBox(其原始名稱為“ TextBox”)是要捕獲“ KeyDown”事件的主要元凶。 這個想法是,當您使用上下箭頭鍵瀏覽建議列表時,您的焦點始終停留在同一文本框中,而不是列表視圖或其他任何內容上。

因此,我完成的操作是在文本框(上面的樣式中的一個)中添加了一個Behavior,如下所示:

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

行為類代碼:我添加了一些注釋以進一步解釋重要部分

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也來自博客。 非常感謝其作者SuperJMN。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM