[RESOLVED] WinForms BeginUpdate() equivalent in WPF?
Ok.. let's say you have the following code:
Code:
for (int i = 0; i < 91001; i++)
{
Listbox.Items.Add(i);
}
In WinForms I'd do:
Code:
Listbox.BeginUpdate();
for (int i = 0; i < 91001; i++)
{
Listbox.Items.Add(i);
}
ListBox.EndUpdate();
that would take perhaps, 3 seconds to execute... now in WPF the problem lies in the fact that I do not know how to stop the drawing until all items are added before drawing everything in one go.
The thing is, I need to load files (1~3 MB big, containing words/URLS on each line) into a Listbox, now when you just use the Listbox.Items.Add it would take centuries to load, let alone the resources it would consume.
So, if anyone could help me, I'd be grateful.
P.S: I am using the Bureau Black theme from http://wpf.codeplex.com/wikipage?title=WPF%20Themes
Re: WinForms BeginUpdate() equivalent in WPF?
I've never really used WPF and I don't know the answer to your question. What I did do is open the documentation for the WPF ListBox control and see that it has a BeginInit method, so my first guess would be to use that.
That said, I think your best bet would be to add all the data to an array or collection and then assign it to the ItemSource of the ListBox. That would be somewhat like using AddRange once instead of Add many times in WinForms.
Re: WinForms BeginUpdate() equivalent in WPF?
Yeah the preferred way to do it in WPF would be to add all of the items to a List/Array and then just assign that list to the ItemsSource property of the listbox. Give that a go and see what sort of performance you get.
Re: WinForms BeginUpdate() equivalent in WPF?
Quote:
Originally Posted by
jmcilhinney
I've never really used WPF and I don't know the answer to your question. What I did do is open the documentation for the WPF ListBox control and see that it has a BeginInit method, so my first guess would be to use that.
That said, I think your best bet would be to add all the data to an array or collection and then assign it to the ItemSource of the ListBox. That would be somewhat like using AddRange once instead of Add many times in WinForms.
Want to know something funny? The first thing I tried was the BeginInit method, even funnier, it was as slow as I would programmatically start a Loop to add everything to the listbox.
I went on and tried the ItemSource too, but I realized that it was definitely not the same as AddRange because it still took as much time as doing it via a Loop.
Quote:
Originally Posted by
chris128
Yeah the preferred way to do it in WPF would be to add all of the items to a List/Array and then just assign that list to the ItemsSource property of the listbox. Give that a go and see what sort of performance you get.
Done it, and the performance is as bad as using a Loop to add everything to the listbox.
My apologizes, I should've mentioned the methods I've used before posting >___>....
Anyway, the main problem I am continuously facing is the fact that when I time (using the System.Diagnostics.StopWatch Class) everything; it takes less than 100 milliseconds to do, but now comes the scary part, it needs to draw everything in the listbox and it takes like 5 seconds to draw 2001 items in the listbox... it doesn't sound that scary right? ... until I try adding 90 000 items to the listbox... well let's say it takes like 3 seconds in WinForms, whilst the app gets stuck when doing it in WPF.
Re: WinForms BeginUpdate() equivalent in WPF?
I just did some testing on the following code snippets. The first three are WinForms and the last three are WPF. I ran each one three times and the elapsed times are shown below.
vb.net Code:
Public Class Form1
Private letters As Char() = "abcdefghijklmnopqrstuvwxyz".ToCharArray()
Private rng As New Random
Private max As Integer = letters.Length
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim watch = Stopwatch.StartNew()
For count = 1 To 100000
Me.ListBox1.Items.Add(Me.letters(Me.rng.Next(Me.max)))
Next
Console.WriteLine(watch.Elapsed)
End Sub
End Class
Quote:
00:00:14.6206819
00:00:13.3435931
00:00:14.3321844
vb.net Code:
Public Class Form1
Private letters As Char() = "abcdefghijklmnopqrstuvwxyz".ToCharArray()
Private rng As New Random
Private max As Integer = letters.Length
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim watch = Stopwatch.StartNew()
Me.ListBox1.BeginUpdate()
For count = 1 To 100000
Me.ListBox1.Items.Add(Me.letters(Me.rng.Next(Me.max)))
Next
Me.ListBox1.EndUpdate()
Console.WriteLine(watch.Elapsed)
End Sub
End Class
Quote:
00:00:00.8258245
00:00:00.9750567
00:00:00.8716296
vb.net Code:
Public Class Form1
Private letters As Char() = "abcdefghijklmnopqrstuvwxyz".ToCharArray()
Private rng As New Random
Private max As Integer = letters.Length
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim watch = Stopwatch.StartNew()
Dim upperBound = 100000 - 1
Dim items(upperBound) As Object
For index = 0 To upperBound
items(index) = Me.letters(Me.rng.Next(Me.max))
Next
Me.ListBox1.Items.AddRange(items)
Console.WriteLine(watch.Elapsed)
End Sub
End Class
Quote:
00:00:00.9451726
00:00:00.8333862
00:00:00.8349755
vb.net Code:
Class MainWindow
Private letters As Char() = "abcdefghijklmnopqrstuvwxyz".ToCharArray()
Private rng As New Random
Private max As Integer = letters.Length
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim watch = Stopwatch.StartNew()
For count = 1 To 100000
Me.ListBox1.Items.Add(Me.letters(Me.rng.Next(Me.max)))
Next
Console.WriteLine(watch.Elapsed)
End Sub
End Class
Quote:
00:00:00.8518833
00:00:00.8643582
00:00:00.8179867
vb.net Code:
Class MainWindow
Private letters As Char() = "abcdefghijklmnopqrstuvwxyz".ToCharArray()
Private rng As New Random
Private max As Integer = letters.Length
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim watch = Stopwatch.StartNew()
Me.ListBox1.BeginInit()
For count = 1 To 100000
Me.ListBox1.Items.Add(Me.letters(Me.rng.Next(Me.max)))
Next
Me.ListBox1.EndInit()
Console.WriteLine(watch.Elapsed)
End Sub
End Class
Quote:
00:00:00.1255790
00:00:00.1265229
00:00:00.1444075
vb.net Code:
Class MainWindow
Private letters As Char() = "abcdefghijklmnopqrstuvwxyz".ToCharArray()
Private rng As New Random
Private max As Integer = letters.Length
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim watch = Stopwatch.StartNew()
Dim upperBound = 100000 - 1
Dim items(upperBound) As Object
For index = 0 To upperBound
items(index) = Me.letters(Me.rng.Next(Me.max))
Next
Me.ListBox1.ItemsSource = items
Console.WriteLine(watch.Elapsed)
End Sub
End Class
Quote:
00:00:00.0110180
00:00:00.0094633
00:00:00.0258997
I think that those results pretty much speak for themselves. Using BeginInit and EndInit resulted in about an 8-fold reduction in the time. Setting the ItemsSource resulted in about a 10-fold reduction in time over that. I can't comment on your results because I haven't seen your code, but it sounds like just drawing your items is the problem, not that they are being drawn multiple times.
Re: WinForms BeginUpdate() equivalent in WPF?
Ok, Wow, stupid me....
What you confirmed is entirely correct, what I forgot to do is to test the whole thing out in a new project in WPF without using a theme... And yeah, I just tried it... and yup, 100 000 items get added to the list in less than 3 seconds without a theme.
As I mentioned in my first post; I use the Expression Light theme from http://wpf.codeplex.com/wikipage?title=WPF%20Themes
Now, this means that the problem apparently lies in the theme....
OK, problem solved....
Apparently this was the wrong-doer in the Theme:
Code:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid>
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Background="{DynamicResource ControlBackgroundBrush}" />
<ScrollViewer Margin="1" Style="{DynamicResource NuclearScrollViewer}" Focusable="false" Background="{x:Null}">
<StackPanel Margin="1,1,1,1" IsItemsHost="true" />
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border" />
<Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
I removed that part of the XAML script and now everything works like a charm.
Thank you VERY MUCH, I freakin' love you =D. No, but seriously, thanks for the help and confirming everything, really appreciate the help you've given me here =).