How to create a combobox supporting data virtualization in wpf? - c#

I'm looking for a simple way to add data-virtualization support to WPF ComboBox?
How can I achieve that?
I tried to plug different collections to the ItemsSource property:
- https://www.codeproject.com/Articles/34405/WPF-Data-Virtualization
- https://alphachitech.wordpress.com/2015/01/31/virtualizing-observable-collection
But seems my ComboBox is displaying nothing, am I doing something wrong?
Are they supposed to work with any controls supporting ItemsSource property?

You should set the ItemsPanel of the property to be a VirtualizingStackPanel.
<ComboBox VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.ScrollUnit="Pixel"
VirtualizingPanel.VirtualizationMode="Recycling">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>

Related

Unable to bind collection to ComboBox in silverlight

I've a ObservableCollection, when I want to display this ObservableCollection with CheckBox I am simply binding the contents to CheckBox but when I want to display the same collection by using ComboBox I am unable to do that. Any suggestions?
XAML: Display collection using CheckBox --WORKS
<ItemsControl ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Display items in CheckBox-->
<CheckBox Content="{Binding Display}" Margin="10,0,0,0" /> // Display is the collection.
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Output:
XAML: Display collection using ComboBox --NOTHING OVER HERE
<ItemsControl ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Display items in ComboBox.-->
Approach - 1
<ComboBox>
<ComboBoxItem Content="{Binding Display}"/>
</ComboBox>
Approach - 2
<ComboBox ItemsSource="{Binding Path=Synonyms}" DisplayMemberPath="Display"/>
Approach - 3
<ComboBox >
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Green" BorderThickness="1" Padding="5">
<TextBlock Text="{Binding Path=Display,StringFormat='Display: {0}'}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Output:
I want to display items(one,two,three, etc.) inside oneComboBox with Select All option. I've tried several approaches but nothing. What am I missing here?
The checkbox is not designed to hold multiple items unlike the Combobox so implementing a system designed for the checkbox is not relevant for the combobox.
To solve your issue, remove the combobox from the ItemsControl and have it stand on its own:
<ComboBox ItemsSource="{Binding Synonyms}" DisplayMemberPath="Display"/>
Which tells the combobox to bind its ItemsSource to the data context which is unspecified, which is fine, so it then gets it's parent's data context. That process works its way up to each parent until it finds a bound data context (most likely the page's datacontext to a VM instance).
Assuming that the data context is valid at some point in the visual tree, it will bind to that instance and look for a property named Synonyms. From the Synonyms property it will use that as a list to display items.
To show (display) text in the combobox (instead defaulting to the item's ToString()) the combobox will show the string from the item's property Display.
Giving a list of items in one drop down.
The short answer, is you should use a ComboBox as the root element, not ItemsControl. CompboBox is just a specialized version of ItemsControl.
<ComboBox ItemsSource="{Binding Synonyms}" DisplayMemberPath="Display"/>
The longer answer.
ComboBox derives from ItemsControl, so you get all the features the base class, plus additional features.
ItemsControl (and its derived classes) provides a way of repeating a set of data in the UI. The DataTemplate is where you specify what UI you want for each "row" of data in the Synonyms source.
What you are doing is asking Silverlight to create a separate ComboBox for each underlying data row.
You can still use a DataTemplate within the ComboBox. Like this.
<ComboBox ItemsSource="{Binding Synonyms}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Display items in CheckBox-->
<TextBox Text="{Binding Display}"
Margin="10,0,0,0"
FontWeight="Bold" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

How to add more items to ItemsControl after binding

In my C# windows phone app, I create a binding to bind a list of string to ItemsControl.
// MyCollections is a List<string>
<ItemsControl x:Name="ContentRoot" ItemsSource="{Binding MyCollections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding }" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It works. But My question is how can I add my own item (e.g. 'Click to add more') to this ItemsControl after it is binded?
There are two answers to this question:
Use an ObservableCollection instead of a List, as it will notify the UI when items are added/removed from it. Then you just add your new item to the list in the view model.
Use a CompositeCollection so you can have the "additional" item without modifying the actual collection.
Normally you would do 1, but since you want a "Click to add more" type of option, CompositeCollection is probably the way to go.
Since you metioned windows phone (but tagged WPF) you may want to look at this post for how to write your own CompositeCollection object: how to do a CompositeCollection in WP8?
Use CompositeCollection to add additional items in your XAML. This should work:
<StackPanel x:Name="stackPanel">
<StackPanel.Resources>
<CompositeCollection x:Key="myCollection">
<CollectionContainer Collection="{Binding DataContext.MyCollections,
Source={x:Reference stackPanel}}"/>
<ContentControl Content="Click to add more"/>
</CompositeCollection>
</StackPanel.Resources>
<ItemsControl x:Name="ContentRoot"
ItemsSource="{StaticResource myCollection}"/>
</StackPanel>

How to access an item in a DataTemplate?

I defined a ListView like this:
<ListView x:Name="libraryBooksListView"
AutomationProperties.AutomationId="VideoListView"
AutomationProperties.Name="Videos"
TabIndex="1"
Padding="0,0,4,0"
ItemsSource="{Binding}"
IsSwipeEnabled="False"
ItemTemplate="{StaticResource LibraryBooksItemTemplate}"
ItemContainerStyle="{StaticResource LibraryListViewItemStyle}"
Grid.Column="1"
SelectionMode="None">
</ListView>
and I defined the LibraryBooksItemTemplate like this:
<DataTemplate x:Key="LibraryBooksItemTemplate">
<Grid Margin="0">
<GridView x:Name="booksGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
ItemsSource="{Binding Items}"
ItemTemplateSelector="{StaticResource textbookTemplateSelector}"
SelectionMode="Multiple"
IsItemClickEnabled="True"
ItemClick="booksGridView_ItemClick"
SelectionChanged="booksGridView_SelectionChanged"
IsSwipeEnabled="false"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</Grid>
</DataTemplate>
The GridView, booksGridView, has multiple items (books).
How to modify/access the "ItemTemplateSelector" and SelectionMode etc. of the GridView?
Is there a way to access each of the items in the booksGridView?
Thx
There are many ways to do that.
The easiest one is to wrap your DataTemplate contents into a UserControl.
Another one is to use something like ContainerFromIndex().
Then you can also use VisualTreeHelper class to walk the visual tree.
Then again you can subclass your ItemsControl and override GetContainerForItemOverride() or
PrepareContainerForItemOverride() or
use the ItemContainerGenerator property
The imporant thing to note is that your ItemsSource provides items to the control while the overrides or the generator provide containers to display the items using the ItemTemplates.
*Edit As for your additional questions:
How to modify/access the "ItemTemplateSelector" and SelectionMode etc. of the GridView?
You have defined your selector resource and gave it a key of "textbookTemplateSelector", so you can just get it with this.Resources["textbookTemplateSelector"]. SelectionMode you can bind to the same source DataContext you bound your ItemsSource to and change or read it through a binding.
Is there a way to access each of the items in the booksGridView?
Yes. Since your DataContext is set as the ItemsSource of your ListView - you can access all the items through that DataContext. Each of these items seems to have an Items property that is bound to your GridView, so you can access each of these through the Items property you have defined yourself.
You can access it by using ItemsSource:
foreach(var book in this.booksGridView.ItemsSource)
{
}

wpf ComboBox and how to get values out of Items

I have a ComboBox in editable mode in my wpf application and I would like to know what would be the best way to get its Items string values for use. I tried moviecomboBox.Items.CurrentItem.ToString(), but it doesn't work.
Edit: Here is a part of my xaml code:
<ComboBox DisplayMemberPath="Movie" Grid.Column="1" Height="23" HorizontalAlignment="Left" ItemsSource="{Binding}" Margin="9,4,0,4" Name="movieComboBox" VerticalAlignment="Center" Width="120" IsEditable="True">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
And I would like to be able to use the Items values in xaml.cs file.
Try using the SelectedItem property:
moviecomboBox.SelectedItem != null ? moviecomboBox.SelectedItem.ToString() : null;
Trying using SelectedItem instead of CurrentItem.
moviecomboBox.Items.SelectedItem.ToString()
Thanks for the answers the solution was in the end as simple as movieComboBox.Text.

Binding ObservableCollection items to UserControl in WrapPanel?

I may just be missing something obvious here, so I apologize if this is a really dumb question. I have a WrapPanel in a view that I need to bind to an ObservableCollection on the ViewModel. This ObservableCollection contains a different type of ViewModel that needs to be bound to another type of view when displayed in the WrapPanel. The goal is to create a wrappable list of items, each of which displays via an instance of a smaller view which should be added to the WrapPanel.
I am using MVVM, and the ViewModel does not have direct access to the View. I would rather not create a binding between the ViewModel and the View if at all possible, so manually adding items to the WrapPanel.Children collection is not a viable option. I am at a loss as to how I can bind a collection of child ViewModel objects to the WrapPanel in such a way that it will create instances of another view and add them to itself. Am I simply approaching the problem incorrectly? I figure there is probably a DataTemplate involved, but it doesn't appear that a WrapPanel has a DataTemplate, nor is it bindable.
Thanks for any insight.
What you need is a ListView that uses a WrapPanel to host all of the items.
<ListView ItemsSource={...}>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<!-- Fill in how you want each item to look here -->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Use an ItemsControl, and set its ItemsPanel to a WrapPanel:
<ItemsControl ItemsSource="{Binding Something}" ItemTemplate="{StaticResource YourDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

Categories