WPF - Binding large amount of data in Listbox - wpf

I am creating a search page for books. There is lots of data in the database. If the data size is more than 2000 the application hangs. ItemsSource of the listbox having the data but something wrong is happening behind.
Code
<ListBox Grid.Column="1"
x:Name="lbResult"
ItemsSource="{Binding}"
SelectionChanged="lbResult_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="320">
<TextBlock Margin="10">
<InlineUIContainer>
<TextBlock Foreground="DarkKhaki" Text="{Binding Title}"/>
</InlineUIContainer>
<Run Text=" "/><LineBreak/>
<InlineUIContainer>
<TextBlock Text=" By "/>
</InlineUIContainer>
<Run Text=" "/>
<InlineUIContainer>
<TextBlock Text="{Binding Author}"/>
</InlineUIContainer>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

The application is apparently "hanging" because the data load is happening on the UI thread.
You should consider a different model that enables you to load the data in another thread and update the UI periodically or as and when new data arrives.
You can use an ObservableCollection for this.
The background loading thread updates the collection and this fires an event out to the UI thread indicating that an update is required.
There's an example of how to do this on GALA Soft
You create a property (read only in this case) for the collection:
private ObservableCollection<MyDataItem> dataItems;
public ObservableCollection<MyDataItem> DataItems
{
get { return dataItems; }
}
Then in your XAML:
<ListBox ItemsSource="{Binding ElementName=mainWindow, Path=DataItems}"
...>
</ListBox>

One problem that you may have is that you are using a non-virtualizing type of panel (WrapPanel) in your ItemsPanelTemplate. What this means is that all 2000 data items will be loaded even if only a fraction of those are visible. By default ListBox uses a VirtualizingStackPanel as its panel which, as the name indicates, provides virtualisation, so it will only load the visible data set elements.
So an easy fix in terms of performance would be to dispense with the WrapPanel and us a virtualizing panel instead, however this would obviously change the appearance.
If you particularly want a WrapPanel then there is no virtualised equivalent provided by WPF, but there are implementations out there, such as http://virtualwrappanel.codeplex.com/.

Try using a ListView instead, I had the same problem. Now I can load over 7000 items in an instant.
Like this:
<StackPanel Grid.Row="1" Grid.Column="0">
<ListView
Height="100"
Name="lstPlayerList">
<ListView.View>
<GridView>
<GridViewColumn
Width="100"
Header="LastName"
DisplayMemberBinding="{Binding LastName}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>

Related

Multiselect ComboBox, Force it to always display the same text

I have a multiselect combobox that works fine. Except for the text. I want it to always have the same text ("Commodity Filter") regardless of what the user has selected.
If I set iseditable to true and the text to CommodityFilter it looks fine until the user makes a selection, then it is garbage (displays object type name). How can I hard code some text there? (Actually ideally i would databind it so it can change depending on whether anything is selected, but that would be a bonus at this point)
<ComboBox IsEditable="True" Text ="Commodity Filter" ItemsSource="{Binding Path=ActiveCommodities}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding Commodity}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I ended up creating a custom object for populating the ComboBox (which had the IsSelected property and implemented INotifyPropertyChanged) because I was creating several comboboxes to control filtering. Once I did this is was trivial to override the tostring on the customobject and pass in the appropriate text. So the xaml did not change much.
I would have preferred to overlay with a text box but that seemed to be beyond my abilities to get a polished look in a reasonable time.
<ComboBox ItemsSource="{Binding Path=ActiveFuturesMonths}"
IsEditable="True"
IsReadOnly="True"
Text="Futures Month Filter" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding Text}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Actually the crux is in setting -
IsEditable="True"
IsReadOnly="True"
Text="Futures Month Filter"
rather than creating custom object. thanks a lot it helped.

silverlight 4 treeview slow on expanding when many items

In XAML I have:
<sdk:TreeView x:Name="navigationTreeView" Grid.Column="0" Grid.Row="1" SelectedItemChanged="TreeView_SelectedItemChanged">
<sdk:TreeView.ItemContainerStyle>
<Style TargetType="sdk:TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</sdk:TreeView.ItemContainerStyle>
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<StackPanel Orientation="Horizontal">
<!--<Image Source="{Binding Path=ImageUri}" />-->
<TextBlock Text="{Binding Path=Title}" ToolTipService.ToolTip="{Binding Path=Title}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
In code behind:
this.navigationTreeView.ItemsSource = nodes;
navigationTreeView.ExpandAll();
There are 1000 items as children of one node. If I'm not expanding elements everything is fine. But when I expand that node it's pretty slow (10 sec maybe). What could I do to speed it up?
Silverlight 4, 2010 april toolkit.
Unlike WPF, Silverlight (until Silverlight 4, not sure about 5) does NOT support UI virtualization for hierarchical data, and this is why when you expand the node, the 1000 items which are inside the HierarchicalDataTemplate are not virtualized and take more than 10 seconds to load.
I believe Telerik's RadTreeView has its built-in UI virtualization, but the control is not free.
The best solution I have found so far is from this site. Please note it is still a ListBox solution, however, because it doesn't use HierarchicalDataTemplate, it is fully virtualized and looks exactly like a TreeView. Also by looking at the source code, it is quite easy to implement.
I know it is not the perfect answer for this question but at least give you some alternatives. :)
This is how the TreeView works. It's virtualized by default: it won't create TreeViewItems until they're needed. However, you're expanding the entire tree which forces all items to be created. That's just inherently slow. If you really need this sort of behavior (where all choices are expanded), I'd suggest something else like a ListBox. Anything that is scrolled off the screen won't be created until it's needed (but see this caveat.)
My bad stack panel is not in the right place...
Ok try this instead:
<sdk:TreeView x:Name="navigationTreeView" Grid.Column="0" Grid.Row="1" SelectedItemChanged="TreeView_SelectedItemChanged">
<sdk:TreeView.ItemContainerStyle>
<Style TargetType="sdk:TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</sdk:TreeView.ItemContainerStyle>
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<StackPanel Orientation="Horizontal">
<!--<Image Source="{Binding Path=ImageUri}" />-->
<TextBlock Text="{Binding Path=Title}" ToolTipService.ToolTip="{Binding Path=Title}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
<sdk:TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</sdk:TreeView.ItemsPanel>
</sdk:TreeView>
Adding:
<sdk:TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</sdk:TreeView.ItemsPanel>
Let me know the results :D
try this:
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<VirtualizingStackPanel Orientation="Horizontal">
<!--<Image Source="{Binding Path=ImageUri}" />-->
<TextBlock Text="{Binding Path=Title}" ToolTipService.ToolTip="{Binding Path=Title}"/>
</VirtualizingStackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
Replace the normal StackPanel with one VirtualizingStackPanel...

Silverlight 4 - simple control template

<DataTemplate x:Key="dirtSimple">
<TextBlock Margin="10,0,0,0" Text="{Binding Path=CurrentBook.Published, StringFormat=d}"></TextBlock>
</DataTemplate>
<ControlTemplate x:Key="lbWrapPanelTemplate">
<StackPanel Orientation="Horizontal" Margin="2" Background="Aqua">
<ItemsPresenter></ItemsPresenter>
</StackPanel>
</ControlTemplate>
...
<ListBox Template="{StaticResource lbWrapPanelTemplate}" x:Name="bookListBox" Grid.Row="0" ItemsSource="{Binding Path=BookSource}" ItemTemplate="{StaticResource dirtSimple}" >
</ListBox>
The list box is displaying correctly, with a beautiful "Aqua" background, and each item is boringly displayed with just a date. For some reason though the items are not flowing horizontally. I originally tried it with the Silverlight Toolkit's WrapPanel, with the same problem, but I can't even get it to work with a built-in StackPanel, so I suspect I'm missing something.
Are you trying to get selection-based behavior that a ListBox provides? If not, use an ItemsControl (and supply an ItemsPanel as below).
The reason it's not going horizontal is the ItemsPresenter ultimately has its own panel it lays out items in. It's not inserting each item separately into your StackPanel (or WrapPanel), it's putting them in its own panel
What you want to do is specify a value for ItemsPanel like so:
<ListBox ItemTemplate="{StaticResource dirtSimple}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Telerik WPF RibbonBar populated via DataBinding

I have and object model, a UserProfile, that contains many ServiceProfile, each containing many CommandProfile.
I have bound this model with Telerik WPF OutlookBar:
<telerikNavigation:RadOutlookBar
ItemsSource="{Binding ServiceProfiles}"
Background="{Binding Color}">
<telerikNavigation:RadOutlookBar.TitleTemplate>
<DataTemplate>
<Label Content="{Binding Description}"/>
</DataTemplate>
</telerikNavigation:RadOutlookBar.TitleTemplate>
<telerikNavigation:RadOutlookBar.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Description}"/>
</DataTemplate>
</telerikNavigation:RadOutlookBar.ItemTemplate>
<telerikNavigation:RadOutlookBar.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding CommandProfiles}" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Description}"
Command="{Binding ExecuteCommand}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</telerikNavigation:RadOutlookBar.ContentTemplate>
</telerikNavigation:RadOutlookBar>
This XAML code creates a OutlookbarItem for each ServiceProfile. Each OutlookbarItem presents a list of buttons as a content.
I'm not able to do the analogous job with ribbonBar: inside a single tab (referring to my UserProfile), I want to create a RibbonGroup for each ServiceProfile. Inside each Group (Service profile) there are many Ribbonbuttons, one for each CommandProfile.
But I'm not able.
I arrive to this code:
<telerikRibbonBar:RadRibbonTab
x:Name="theTab"
Header="{Binding Description}"
Background="{Binding Color}"
ItemsSource="{Binding ServiceProfiles}">
</telerikRibbonBar:RadRibbonTab>
which creates the ribbongroups, but I'm not able to control anything (title of the group, fill (via Binding) the content.
Any idea?
Thanks
Marco Parenzan
Bit late, but sorry to say but it doesn't look supported yet:
http://www.telerik.com/community/forums/silverlight/ribbonbar/headertemplate-of-radribbontab.aspx
Hello Alex Fan,
Unfortunately the RibbonBar doesn't support databinding for now. However, you can put your vote for this feature in our PITS thus increasing its priority.
In your case the best will be if you add the ribbon items programmatically.
Let us know if you need more info.
All the best,
Tina Stancheva
the Telerik team

How can I make an "Accordion Widget" in WPF?

The goal:
I'm trying to achieve something like this in WPF:
(source: wordpress.org)
An initial solution:
At the moment, I'm trying to use an ItemsControl with an ItemTemplate composed of an Expander.
I want a consistent look for the Header portion of the Expander, but I want the Content portion of the Expander to be completely flexible. So, it's basically a set of "portlets" stacked vertically, where each portlet has a consistent title bar but different content.
The code so far:
This is what I have at the moment:
<ItemsControl
Grid.Row="2"
Grid.Column="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander>
<Expander.HeaderTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal">
<TextBlock
FontSize="14"
FontWeight="Bold"
Text="Title_Of_Expander_Goes_Here" />
<TextBlock
Margin="10,0,0,0"
FontWeight="Bold"
FontSize="18"
Text="*" />
</StackPanel>
</DataTemplate>
</Expander.HeaderTemplate>
<Expander.Template>
<ControlTemplate
TargetType="Expander">
<Border
BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
</Expander.Template>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<StackPanel>
<TextBlock
FontSize="14"
FontWeight="Bold"
Text="Users:" />
<wt:DataGrid
Margin="0,1,0,0"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserDeleteRows="True"
ItemsSource="{Binding Source={StaticResource Main_SystemUsers}, XPath=//Users/*}">
<wt:DataGrid.Columns>
<wt:DataGridTextColumn
Header="User Name"
Binding="{Binding XPath=#UserName}" />
<wt:DataGridComboBoxColumn
Header="Role"
ItemsSource="{Binding Source={StaticResource Main_UserRoles}, XPath=//Roles/*}"
SelectedValueBinding="{Binding XPath=#Role}" />
</wt:DataGrid.Columns>
</wt:DataGrid>
<StackPanel
Margin="0,10,0,0"
Orientation="Horizontal">
<Button
Content="Add New User..." />
<Button
Margin="10,0,0,0"
Content="Delete User..." />
</StackPanel>
</StackPanel>
</ItemsControl.Items>
</ItemsControl>
Discussion:
The only thing that shows up when I run this is the DataGrid of users and the buttons ("Add New User" and "Delete User") below it. There is no Expander or title bar. Also, even if I did see one, I'm not sure how to set up a Binding for the title that appears on the title bar. I know how to do bindings if I use ItemsSource, but I wanted to set my items declaratively.
The question:
How should I go about this? I'm looking for either a fix for what I have now or a clean-sheet solution.
Edit:
What I ended up doing was replacing the ItemsControl with a StackPanel and just writing a style for my expanders. This proved to be much simpler, and there really was no benefit to the ItemsControl since I needed to declare custom content for each item anyway. The one issue remaining was how to achieve a custom title for each expander. That's where #Thomas Levesque's suggestion to use TemplateBinding came in. All I had to do was replace Text="Title_Of_Expander_Goes_Here" in my header's template (see code above) with Text="{TemplateBinding Content}".
You're not seeing the Expander because you redefined its template. This one should work better :
...
<Expander.Template>
<ControlTemplate
TargetType="Expander">
<Border
BorderThickness="1">
<Expander Content="{TemplateBinding Content}" Header="{TemplateBinding Header}"/>
</Border>
</ControlTemplate>
</Expander.Template>
...
Personally I think a TreeView control would give you a much better base to work from, especially if you're using Expression Blend as a basis to create new/blank Templates from for items. Seeing the default Templates is extremely enlightening and gives you much more fine-grained control and better understanding and insight into how things work by default. Then you can go to town on them. It also looks like you're working with Hierchical Data and TreeViews inherently lend themselves well to working with such data.

Resources