WPF:如何避免 ListBox 或 ListView 中选中的复选框闪烁?

发布于 2024-08-16 17:34:10 字数 1812 浏览 1 评论 0原文

如何避免 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;
        }
    }
}

How can I avoid the flickering of the checked checkboxes in a WPF ListBox or ListView ? It can be reproduced with the code below by clicking on the Refresh button or by scrolling the listbox. If IsChecked is false, it does not flicker.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

唠甜嗑 2024-08-23 17:34:10

它闪烁是因为您要丢弃旧的 ItemsSource 并创建一个新的 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;
        }
    }
}

It is flickering because you are throwing out the old ItemsSource and creating a new one. This requires all of the binding to be redone, and the template displaying each item needs to be recreated. To avoid the performance overhead of recreating an entire list, just modify the individual elements in the existing ItemsSource. Then the part of the DataTemplate that is bound to the changed properties and/or items will automatically update without needing to recreate the whole list view. Doing this will eliminate the "flicker" you are seeing.

Try this for the codebehind:

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;
        }
    }
}
瑕疵 2024-08-23 17:34:10

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

它不会出现在 Windows XP 上(这就是为什么我认为它是一个动画),我还没有测试过 Vista :)

祝你好运。

Do you mean checkbox being checked? I think you need to change the animation when you check / set the checkbox checked.

It does not occur on Windows XP (that's why I think it's an animation), I haven't tested Vista :)

Good luck.

葬花如无物 2024-08-23 17:34:10

+1给Snake,他在这里有正确的答案。

添加到此:

CheckBox 控件有一个带有情节提要动画的控件模板,当选中状态更改时,该动画会以动画方式打开/关闭选中的图标。当您绑定到 ObservableCollection 并重新创建 ItemsSource 时,Checked 状态会发生变化,这会导致创建新的复选框(IsChecked=false)并绑定到您的 ViewModel(这可能会导致 IsChecked=True)。

要禁用此“功能”,您可以修改填充 ObservableCollection 的方式,或者如果不可能,您可以更改复选框的模板/样式。

只需对 Checkbox 的 ControlTemplate 进行逆向工程(使用混合或使用 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>

+1 to Snake who has the right answer here.

To add to this:

The CheckBox control has a control template with storyboard animation which animates the checked icon on/off when the checked state changes. The Checked state changes as you are binding to ObservableCollection and recreating the ItemsSource which is causing new Checkboxes to be created (with IsChecked=false) and bound to your ViewModel (which probably results in IsChecked=True).

To disable this 'feature' you can either modify how you fill an ObservableCollection, or if that's not possible, you can change the template / style of the Checkbox.

Just reverse engineer the ControlTemplate of Checkbox (using blend, or by using one of the WPF Themes) and find these lines. you need to set the duration of the two animations to zero

<!-- 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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文