在ListBox中没有"None"选择模式,是否有其他方法禁用列表框的选择功能?

209

如何在ListBox中禁用选择?


1
你能提供一个例子,说明在哪种情况下拥有一个无法选择的ListBox是有效的吗?因为主要行为是选择项目。我可能会选择另一种方式来显示它。(这不是我试图批评,而是对可能发生的情况感到真正的兴趣) - Marthin
3
@Martin:例如,如果你想从一个列表框项中拖动内容,那么在这种情况下,你可能不需要选择该项。此外,在拖动项时,列表框的选定项会随着你在列表框内拖动而改变-请参阅此帖子https://dev59.com/D2sz5IYBdhLWcg3w5ME0 - Danield
1
我相信Shimmy想使用ListBox的原因是,提问者有时可以使列表框可选择。这个问题对我也很有价值。比如你正在构建一款纸牌游戏。你可以从你的牌中选择一张牌,有时候你可以选择多张,而在其他时候,你不能选择任何一张。 - Gqqnbig
1
有时候你会有10张卡片,但只有其中4张是可选的。在这4张卡片中,你最多可以选择3张。 - Gqqnbig
1
@Marthin:当您在ListBox中使用GridView时,GridView标题提供了许多其他地方无法提供的功能。而且,您在网格视图单元格中有编辑控件。 - Robin Davies
17个回答

292

方法1 - ItemsControl

如果你不需要ListBox的其他方面,你可以使用ItemsControl代替。它将项目放置在ItemsPanel中,并且没有选择的概念。

<ItemsControl ItemsSource="{Binding MyItems}" />

默认情况下,ItemsControl 不支持其子元素的虚拟化。如果您有大量项目,虚拟化可以减少内存使用并提高性能,这种情况下您可以使用方法2并为 ListBox 设置样式,或者向您的 ItemsControl 添加虚拟化

方法2 - 为 ListBox 设置样式

或者,只需为 ListBox 设置样式,以使选择不可见。

<ListBox.Resources>
  <Style TargetType="ListBoxItem">
    <Style.Resources>
      <!-- SelectedItem with focus -->
      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                       Color="Transparent" />
      <!-- SelectedItem without focus -->
      <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
                       Color="Transparent" />
      <!-- SelectedItem text foreground -->
      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}"
                       Color="Black" />
    </Style.Resources>
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
  </Style>
</ListBox.Resources>

27
不会改变实际选择行为,只会改变视觉效果。 - Thomas Levesque
我的主要愿望是视觉效果。 - Shimmy Weitzhandler
10
我的第一个建议是使用ItemsControl。你有没有注意到呢? :) - Drew Noakes
5
再次阅读这些评论,我想指出@Thomas Levesque的评论只适用于我展示的第二种方法。使用普通的“ItemsControl”将完全消除任何选择的概念。 - Drew Noakes
1
ItemsControl的解决方案可以移除默认的滚动支持(滚动条和鼠标滚轮)。 - MuiBienCarlota
2
如果我们有一个需要滚动的巨大页面,当用户将鼠标悬停在ListBox上时,使用方法1-ItemsControl会得到+1分。这是因为ListBox会抓取MouseWheel事件,从而有效地禁用了MouseWheel。这意味着,根据鼠标是否悬停在listbox上,用户会感到沮丧,因为用于滚动整个页面的鼠标滚轮会随机停止工作。 - Contango

174

我找到了一个非常简单明了的解决方案,它对我很有用,我希望它也能对你有所帮助。

<ListBox ItemsSource="{Items}">
    <ListBox.ItemContainerStyle>
       <Style TargetType="{x:Type ListBoxItem}">
           <Setter Property="Focusable" Value="False"/>
       </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

3
很完美,它可以防止所选项目和其他控件(如按钮)仍然起作用。正是我想要的。 - Franck
1
这种方法很好。如果我们有一个需要滚动的巨大页面,当用户将鼠标悬停在列表框上时,它会有效地禁用鼠标滚轮,因为列表框会抓取鼠标滚轮事件。这意味着,根据鼠标是否悬停在列表框上,用户会感到沮丧,因为用于滚动整个页面的鼠标滚轮会随机停止工作。 - Contango
不幸的是,这并不是UWP的解决方案。 - M.kazem Akhgary
正如Contago所回复的那样,这禁用了滚动功能,使其无法帮助。 - user99999991
5
请将以下内容翻译成中文:Add this additional property to remove the mouseover highlighting as well: <Setter Property="IsHitTestVisible" Value="False" />请添加这个额外的属性来移除鼠标悬停时的高亮显示效果: <Setter Property="IsHitTestVisible" Value="False" /> - DonBoitnott
显示剩余3条评论

31

你可以考虑使用 ItemsControl 而不是 ListBox。一个 ItemsControl 没有选择的概念,因此没有需要关闭的东西。


2
迷人的。我从来不知道你可以直接声明ItemsControl,我以为它是虚拟的(MustOverride),谢谢!!! - Shimmy Weitzhandler
但是,ItemsControl仍然会将我的项渲染成一行吗? - Chry Cheng
@Chry 是的,而且此外,您还可以手动设置 ItemTemplate. - Shimmy Weitzhandler
3
这样做会失去太多的功能,例如滚动。 - Jeff
@Jeff,你可以将ItemsControl包裹在 ScrollViewer 中以实现滚动。 - Wilka
你可以将它包装在一个ScrollViewer中,但这样会失去ListBox的虚拟化功能。 - user99999991

13

另一个值得考虑的选项是禁用ListBoxItems。可以通过设置ItemContainerStyle来实现,代码示例如下:

<ListBox ItemsSource="{Binding YourCollection}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsEnabled" Value="False" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

如果您不想将文本设置为灰色,可以通过在样式资源中添加刷子并使用以下键{x:Static SystemColors.GrayTextBrushKey}来指定禁用颜色。另一个解决方案是覆盖ListBoxItem控件模板。


简单易用,谢谢!并且它也适用于 WP 8.1 Runtime。 - Malutek

13

如果我需要使用listbox而不是itemscontrol来显示不可选的项,则可以使用以下代码:

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="IsHitTestVisible" Value="False" />
    </Style>
</ListBox.ItemContainerStyle>

1
这是最好的答案!至少对于我现有的列表框项目复杂样式的情况来说是如此。我只需使用这个setter进行扩展,它就像魔法一样奏效了。谢谢! - Antiohia
是的,如果您想保留 ListBox 功能并且无法使用 ItemControl,则这很完美。 - johnc

4

这里有很好的答案,但我正在寻找略微不同的东西:我想要选择,但只是不希望它显示出来(或以不同的方式显示)。

以上的解决方案对我没有完全起作用,所以我做了另外一件事情:我为我的列表框使用了一个新样式,该样式完全重新定义了模板:

<Style x:Key="PlainListBoxStyle" TargetType="ListBox">
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="ListBoxItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <ContentPresenter />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <ItemsPresenter/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

从这里开始,您可以轻松地添加自己的选择高亮,或者如果您不想要任何高亮,可以将其保留在原样。


3
我提供另一个解决方案。只需重新将ListBoxItem的模板制作成一个ContentPresenter即可,就像这样... "最初的回答"
<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <ContentPresenter />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我这样做的原因如下:
  1. 在我的情况下,我不想禁用用户与我的 ListBoxItems 内容的交互,所以设置 IsEnabled 的解决方案对我无效。

  2. 另一个解决方案试图通过覆盖与颜色相关的属性来重新设计 ListBoxItem,但只适用于那些您确定模板使用这些属性的情况。这对于默认样式很好,但对于自定义样式则无效。

  3. 使用 ItemsControl 的解决方案会破坏太多其他内容,因为 ItemsControl 与标准 ListBox 有完全不同的外观,并且不支持虚拟化,这意味着您必须重新设计 ItemsPanel

以上方法不会改变 ListBox 的默认外观,也不会禁用数据模板中的项目,通过默认支持虚拟化,而且与应用程序中可能存在的任何样式无关。这是 KISS 原则。


"最初的回答"

2
虽然 @Drew Noakes 的答案是大多数情况下的快速解决方案,但设置 x:Static 画笔存在一些缺陷。
当您按照建议设置 x:Static 画笔时,列表框项内的所有子控件都将继承此样式。
这意味着,虽然这将用于禁用列表框项的高亮显示,但它可能会对子控件产生不良影响。
例如,如果您在 ListBoxItem 中有一个 ComboBox,则会禁用 ComboBox 中的鼠标悬停高亮显示。
相反,考虑设置所选、未选和鼠标悬停事件的 VisualStates,如本 stackoverflow 主题中提到的解决方案所述:Remove Control Highlight From ListBoxItem but not children controls
-Frinny

1
也许你只需要ItemsControl的功能?它不允许选择:
<ItemsControl ItemsSource="{Binding Prop1}" ItemTemplate="{StaticResource DataItemsTemplate}" />

3
@Shimmy: 平凡的答案往往相似,这里没有值得标记的重复。如果您对此有更多问题,请在[元社区]上提问。 - user1228

1
我找到了一个完美的方法。 将ListBox的IsHitTestVisible设置为false,这样用户就不能用鼠标悬停或向下/向上滚动。 捕获PreviewGotKeyboardFocus并设置e.Handled = true,这样用户就不能使用键盘Tab、箭头向上或箭头向下选择项目。
这种方法的优点是:
  1. ListBox项目的前景色不会变成灰色。
  2. ListBox背景可以设置为透明。
xmal
<ListBox Name="StudentsListBox" ItemsSource="{Binding Students}" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0" Background="Transparent" IsHitTestVisible="False" PreviewGotKeyboardFocus="StudentsListBox_PreviewGotKeyboardFocus">

    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Padding" Value="0"/>
            <Setter Property="Margin" Value="0"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border x:Name="Bd">
                            <ContentPresenter/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="False" />
                                    <Condition Property="IsSelected" Value="True" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Bd" Property="Background" Value="Yellow" />
                                <Setter TargetName="Bd" Property="BorderBrush" Value="Transparent" />
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid Margin="0,0,0,0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Name="GradeBlock" Text="{Binding Grade}" FontSize="12" Margin="0,0,5,0"/>
                <TextBlock Grid.Column="1" Name="NameTextBlock" Text="{Binding Name}" FontSize="12" TextWrapping="Wrap"/>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>

</ListBox>

代码

private void StudentsListBox_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    e.Handled = true;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接