WPF: 如何避免在 ListBox 或 ListView 中选中复选框时出现闪烁?

7

如何避免WPF ListBox或ListView中选中的复选框闪烁?可以通过下面的代码点击“刷新”按钮或滚动列表框来重现。如果IsChecked为false,则不会闪烁。

Window1.xaml:

<Window x:Class="WpfApplication6.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox Name="listBox">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="True"
                                  VerticalAlignment="Center"/>
                        <Label Padding="3"
                               Content="{Binding}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Content="Refresh"
                Grid.Column="1"
                VerticalAlignment="Top"
                Click="Button_Click"/>
    </Grid>
</Window>

Window1.xaml.cs:

using System.Windows;

namespace WpfApplication6
{
    partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            Button_Click(null, null);
        }

        void Button_Click(object sender, RoutedEventArgs e)
        {
            var items = new int[10000];
            for (int i = 0; i < items.Length; i++)
                items[i] = i + 1;
            listBox.ItemsSource = items;
        }
    }
}
3个回答

6
由于您正在丢弃旧的ItemsSource并创建一个新的,因此它会闪烁。这需要重新执行所有绑定,并重新创建显示每个项目的模板。为避免重新创建整个列表的性能开销,请仅修改现有ItemsSource中的各个元素。然后,与更改的属性和/或项绑定的DataTemplate部分将自动更新,而无需重新创建整个列表视图。这样做将消除您看到的“闪烁”。
尝试以下代码:
public partial class MainWindow : Window
{
    private ObservableCollection<object> _items;

    public MainWindow()
    {
        InitializeComponent();

        _items = new ObservableCollection<object>();
        for (int i = 0; i < 10000; i++)
            _items.Add(i + 1);
        listBox.ItemsSource = _items;

    }

    void Button_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < _items.Count;i++)
        {
            if (!(_items[i] is int)) continue;
            _items[i] = (int)_items[i] + 1;
        }
    }
}

2

你的意思是复选框被勾选吗?我认为你需要在勾选/设置复选框被选中时更改动画。

这在 Windows XP 上没有发生(这就是为什么我认为它是动画),我还没有测试 Vista :)

祝你好运。


1

给回答正确的蛇+1分。

补充一下:

CheckBox 控件具有一个控件模板和故事版动画,当选中状态发生变化时,会对选中图标进行开/关动画。选中状态会因为绑定到 ObservableCollection 并重新创建 ItemsSource 而发生改变,这会导致新的复选框被创建(带有 IsChecked=false)并绑定到您的 ViewModel(可能导致 IsChecked=True)。

要禁用这个“特性”,您可以修改如何填充 ObservableCollection,或者如果不可能,您可以更改 Checkbox 的模板/样式。

只需反向工程 Checkbox 的 ControlTemplate(使用 blend 或使用其中一个 WPF 主题),找到这些行。 您需要将两个动画的持续时间设置为零。

<!-- Inside the CheckBoxTemplate ControlTemplate -->
<Storyboard x:Key="CheckedTrue">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="CheckIcon" 
                            Storyboard.TargetProperty="(UIElement.Opacity)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
    </DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="CheckedFalse">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="CheckIcon" 
                            Storyboard.TargetProperty="(UIElement.Opacity)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

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