WPF: How can I avoid the flickering of the checked checkboxes in a ListBox or a ListView?
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;
}
}
}
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;
}
}
}
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.
+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>
精彩评论