WPF:禁用ListBox的同时启用滚动

8

我整个早上都在苦思冥想这个问题。

基本上,我有一个列表框,我想在长时间运行的过程中防止人们更改选择,但仍允许他们滚动。

解决方案:

所有答案都很好,我选择了吞噬鼠标事件,因为这是最直接的方法。我将PreviewMouseDown和PreviewMouseUp连接到单个事件,该事件检查我的backgroundWorker.IsBusy,如果设置了IsHandled属性,则将其设置为true。

11个回答

8

如果你查看ListBox的控制模板,会发现其中有一个ScrollBar和ItemsPresenter。所以禁用ItemsPresenter就可以轻松地实现这一点。在ListBox上使用下面的样式,你就能顺利运行了。

    <Style x:Key="disabledListBoxWithScroll" TargetType="{x:Type ListBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBox}">
                    <Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="1">
                        <ScrollViewer Padding="{TemplateBinding Padding}" Focusable="false">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" IsEnabled="False" IsHitTestVisible="True"/>
                        </ScrollViewer>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsGrouping" Value="true">
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

在ListBox上使用样式。
<ListBox    Style="{DynamicResource disabledListBoxWithScroll}" ..... />

2
我发现将一个禁用的ListBox放在启用自动滚动的ScrollViewer中可以实现所需的效果。

1

1

诀窍在于不要真正禁用。禁用将锁定滚动框中的所有消息。

在长时间操作期间,使用其.ForeColor属性使列表框中的文本变灰,并吞噬所有鼠标点击。这将模拟禁用控件并允许无障碍滚动。


4
这种方法的问题在于,键盘仍然可以用于进行选择。 - Caleb Vear

1
我使用了这个解决方案,它非常简单并且完美地工作:
对于你放入 Listbox 中的每个 SurfaceListBoxItem item,请执行以下操作:
item.IsHitTestVisible = false;

只要项目中没有交互控件,这个工作就完美无缺,否则这些控件将无法使用。 - Jarvan

1

这对我来说效果最好。它很简单,整个代码都在XAML中,我认为非常整洁。

<ListBox ItemsSource="{Binding MySource}">
  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Style.Triggers>
        <DataTrigger Binding="{Binding IsEditing}" Value="True">
          <Setter Property="IsEnabled" Value="True"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding IsEditing}" Value="False">
          <Setter Property="IsEnabled" Value="False"/>
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </ListBox.ItemContainerStyle>  
</ListBox>

我对XAML不是很熟悉。那么我如何在用户窗体上使用它来操作两个特定的列表框? - Doons

0
另一个值得考虑的选项是禁用 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控件模板。

这个问题与没有ListBox.SelectionMode =“None”,是否有其他方法在列表框中禁用选择?几乎相同,我的答案也是一样的。


0

使用http://www.codeproject.com/Tips/60619/Scrollable-Disabled-ListBox-in-WPF提供的完整答案

样式:

<Style TargetType="{x:Type local:CustomListBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomListBox}">
                <Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="1">
                    <ScrollViewer IsEnabled="True">
                        <ItemsPresenter IsEnabled="{Binding Path=IsEnabledWithScroll,  RelativeSource={RelativeSource TemplatedParent}}"  SnapsToDevicePixels="{TemplateBinding  UIElement.SnapsToDevicePixels}"/>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这是一个类

public class CustomListBox : ListBox
{
    public bool IsEnabledWithScroll
    {
        get { return (bool)GetValue(IsEnabledWithScrollProperty); }
        set { SetValue(IsEnabledWithScrollProperty, value); }
    }

    public static readonly DependencyProperty IsEnabledWithScrollProperty =
        DependencyProperty.Register("IsEnabledWithScroll", typeof(bool), typeof(CustomListBox), new UIPropertyMetadata(true));
}

然后,不要在ListBox上设置IsEnabled,而是使用IsEnabledWithScroll。如果ListBox启用或禁用,滚动将正常工作。


0
似乎有很多种方法可以解决这个问题。我发现通过在XAML中设置ItemsContainerStyle上的IsHitTestVisible,我得到了我所需要的东西:
<ListBox IsHitTestVisible="true" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsHitTestVisible" Value="False" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

0

嗯,我找到了一个不错的方法来实现这个功能。我的做法是,在listBox的DataTemplate中,我使用Page作为源,将父布局的enable属性与布尔标志绑定。

步骤1- 为页面提供x:Name属性。如果你使用的页面是扩展基础页面的,则确保基础页面不是抽象类,并且具有默认构造函数而没有任何参数。

<Page x:Class="OPMS.Views.Registration"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    x:Name="MainPage"
    d:DesignWidth="1024"
    Title="Registration"
>

步骤二 - 使用该页面作为数据模板父布局项的源,设置IsEnabled属性。

<ListBox Grid.Row="2"
    ItemsSource="{Binding TestGroups}"
    AlternationCount="2"
    Padding="0"
    Margin="10,5,10,10"
>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding Name}"
                IsChecked="{Binding IsSelected}"
                IsEnabled="{Binding Source={x:Reference MainPage}, Path=DataContext.BindingVariableHere}"
            />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

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