Bind Combobox with huge data in WPF - wpf

I am trying to bind combobox with custom object list. My object list have around 15K record and combobox takes long time to show the data after clicking on combobox.
Below are the code:
<ComboBox Height="23" Name="comboBox1" Width="120" DisplayMemberPath="EmpName" SelectedValue="EmpID" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"/>
code behind:
List<EmployeeBE> allEmployee = new List<EmployeeBE>();
allEmployee = EmployeeBO.GetEmployeeAll();
comboBox1.ItemsSource = allEmployee;
allEmployee have around 15K record.
Can any one Suggest how can I improve my combobox performance?

That's bad UI design: No user will read through 15K records.
You can improve the performance by allowing the user to enter some filter criteria before showing the results, for example, by using an AutoCompleteBox instead of a ComboBox.

You could try a VirtualizingStackPanel as described here - http://vbcity.com/blogs/xtab/archive/2009/12/15/wpf-using-a-virtualizingstackpanel-to-improve-combobox-performance.aspx
As others have said, you really want to re-imagine your UI, as a ComboBox isn't appropriate for 15k records.

Try using a VirtualizingStackPanel as ItemsPanel for the ComboBox.
<ItemsPanelTemplate x:Key="ComboBoxItemsPanelTemplate">
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
<ComboBox ItemsPanel="{StaticResource ItemsTemplate}"/>

Related

Which tool is the best option for doing search in Datagrid in wpf?

I have created search option using combobox, for example
In combobox1 items are m1,m2,m3,m4,m5 based on that, if m1 item selected then
another combobox2 displays with items a,b,c,d and if a item is selected another
combobox3 dispalys, based on last combobox it searches on the datagrid.
I think it is long process, use of many combobox makes it lenghty. Is any
other way is their to implement this. plz help
<ComboBox Grid.Column="1"
Grid.Row="1"
x:Name="cmbType"
VerticalAlignment="Top"
IsEnabled="{Binding IsOther}"
ItemsSource="{Binding Source={StaticResource enumTypeOfType}}"
SelectedItem="{Binding SearchType,Mode=TwoWay}"
SelectedIndex="{Binding CmdResIndex,Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
SelectionChanged="DataSource1"
Margin="0,0,1,0">
</ComboBox>
So if i get this right, you have a collection a, which goes to collection b,etc, and the second collection will change based on the selected item of the first? You have to remember, that since the data will change for each selection, hard coding the value is out of the question.
Knowing this, WPF provides you with a great mechanism for this. Using a stackpanel, with a list view will actually work.
<ItemsControl ItemsSource="{binding collections}" ItemTemplate="{binding TemplateForListViewItems}" ItemPanelTemplate="{binding itemPanelTemplate}"></ItemsControl>
Now, with the items control, one can simply set an ItemTemplate/DataTemplate, to set the styling of each control. Linking to the onclick event, or using interactions, you can simply do collections.Add to add your new list view with generated data for the selection, and done.

In focus row in Xceed datagrid doesn't get updated

I have a Xceed Datagrid whose ItemsSource is CollectionViewSource defined in XAML. Whenever grid is updated, only the row which is in focus doesn't show updated values (revert back to original values) but all of other rows are updated. If I directly bound the grid to a Collection in ViewModel then everything works fine. The problem is only when, CollectionViewSource comes into picture. Any help is appreciated.
Can you try using the DataGridCollectionViewSource instead of a CollectionViewSource. By using this, you will reap the benefits of the DataGrid such as built in filtering, sorting, grouping, etc. An example from their documentation:
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
<Grid.Resources>
<xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
Source="{Binding Source={x:Static Application.Current},
Path=Orders}"/>
</Grid.Resources>
<xcdg:DataGridControl x:Name="OrdersGrid"
ItemsSource="{Binding Source={StaticResource cvs_orders}}"/>
</Grid>
I got solution...
grid.CurrentItem = null

What standard WPF control should I use?

I am trying the master-detail presentation in my app: when an item in a listbox is selected, its details are displayed in an adjacent control.
This control will have a list of measurements such as height, width, weight, etc. It will also have some small graphics such as a green or red dot or a medium sized image. It will also have some rich text.
Which STANDARD WPF control should I use to contain all these elements. I am thinking of using a listbox but wonder if there are better controls to use.
My main consideration is ease of coding, then possibly efficiency of the code.
Thanks.
A listbox indicates a list of items that can be tailored using a DataTemplate for appearence. In this case you are showing the details of a selected item. I would actually use a container such as a Grid nested in your current UI and have a set of stackpanel including the details of the selected item.
<Grid>
<StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Detail1</TextBlock>
<TextBox></TextBox>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Detail2</TextBlock>
<TextBox></TextBox>
</StackPanel>
</StackPanel>
</Grid>
This is only one suggestion but the point is to use a container and use a set of controls in the containers - textblock,textbox,checkboxes(boolean details), etc... this will allow you to use any control type necessary to represent the specific data field of the selected item.
You don't want to use a listbox unless you have a collection of similar items, and you want one or more items to be 'selected' at some point. It sounds like that is not what you want for the details part.
You do have a collection, which is shown in your master list. You should bind the SelectedItem in your master list to a property in your viewmodel. Then you can bind that same property to the details section of your UI. When the selection in the master list changes, your details UI will automatically update to reflect the changes.
<ListBox x:Name="masterList" ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem, Mode=TwoWay}"></ListBox>
<UserControl x:Name="detailsControl" DataContext="{Binding MySelectedItem}"> </UserControl >

using wpf datagridcomboboxcolumn's IsSynchronizedWithCurrentItem

(see below for my own answer that I came up with after letting this percolate for days & days)
I am trying to achieve the following scenario in WPF.
I have a datagrid that is displaying rows of data for viewing and additional data entry. It is a new app but there is legacy data.
One particular field in the past has had data randomly entered into it. Now we want to limit that field's values to a particular list. So I'm using a DataGridComboBoxColumn. FWIW I have alternatively tried this with a DataGridTemplateColumn containing a ComboBox.
At runtime, if the existing value is not on the list, I want it to display anyway. I just cannot seem to get that to happen. While I have attempted a vast array of solutions (all failures) here is the one that is most logical as a starting point.
The list of values for the drop down are defined in a windows resource called "months".
<DataGridComboBoxColumn x:Name="frequencyCombo" MinWidth="100" Header="Frequency"
ItemsSource="{Binding Source={StaticResource months}}"
SelectedValueBinding="{Binding Path=Frequency,UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
What is happening is that if a value is not on the list then the display is blank. I have verified at runtime that the IsSynchronizedWithCurrentItem element is indeed False. It is just not doing what I am expecting.
Perhaps I am just going down the wrong path here. Maybe I need to use a textbox in combination with the combobox. Maybe I need to write some code, not just XAML. I have spent hours trying different things and would be really appreciative of a solution. I have had a few suggestions to use this class or that control but without explanation of how to use it.
Thanks a bunch!
I have finally solved this.
The trick is to get rid of the comboboxcolumn and use a template that has a textbox for display and a combobox for editing. However, I still spent hours with a new problem...when making a selection in the combobox, it would modify any other rows where I had also used the combobox in the grid. Guess what solved the problem! The IsSynchronizedWithCurrentItem property that I was trying to use before. :)
<DataGridTemplateColumn x:Name="frequencyCombo" Header="Frequency">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Frequency}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Source={StaticResource frequencyViewSource},
TargetNullValue=''}"
SelectedItem="{Binding Path=Frequency}" IsSynchronizedWithCurrentItem="False"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
No ugly hacks. No non-usable choices hanging around at the bottom of the dropdown. No code to add those extra values and then clean them up.
I am not going to take away the "Answer" on Mark's suggestion since it enabled me to get the app into my client's hands, but this is the solution I was looking for. I found it buried in a "connect" item after hours of web searching.
Thanks for everyones help!
Could you please clarify what's happening here? It's unclear what the "existing value" is at runtime - if this is the field where data is being randomly entered, by limiting it does this mean you're running some sort of validation logic although you still want it to display?
Also, I'm more on the Silverlight side of things...does WPF also default to one way binding?
Rather than mixing the static resource and the view model property as a source for items on the list, have you tried using an ObservableCollection or CollectionViewSource in the view model? Then you could insert and remove the non-standard items at will and make them selected (or not) whenever you want. So the "normal" list would have the normal months, but when an odd one comes along, add that to the list and make it selected. Seems like it would be easier to control exclusively in the view model.
Why not just do something like:
//create collection
PagedCollectionView view = new PagedCollectionView(e.Result);
view.SortDescriptions.Add(
new SortDescription("Months", ListSortDirection.Ascending));
gridProducts.ItemsSource = view;
//filter collection by category
PagedCollectionView view = new PagedCollectionView(e.Result);
view.Filter = delegate(object filterObject)
{
Product product = (Product)filterObject;
return (product.CategoryName == "Legacy");
};
gridProducts.ItemsSource = view;
//create categories through grouping
PagedCollectionView view = new PagedCollectionView(e.Result);
view.GroupDescriptions.Add(new PropertyGroupDescription("Legacy"));
view.GroupDescriptions.Add(new PropertyGroupDescription("etc..."));
gridProducts.ItemsSource = view;
Try this:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Months}"
Text="{Binding Value}"
IsEditable="True" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

WPF binding to individual 'rows' (not columns) of a datagrid possible?

I have a datagrid. A column of the datagrid is a simple <DataGridTemplateColumn> with its CellTemplate containing a <DataTemplate> which contains a <ComboBox> such as
<my:DataGrid Name="dataGridMain" AutoGenerateColumns="False">
<my:DataGrid.Columns>
<my:DataGridTemplateColumn Header="Food" >
<my:DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<ComboBox Name="comboDataTemplate"
Text="{Binding Path=Food,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource resFoodLookups}}"
DisplayMemberPath="FoodName"
SelectedValuePath="FoodID" IsEditable="True" />
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
</my:DataGrid.Columns>
</my:DataGrid>
All is working fine. Each combobox is bound to a static list due to the ItemsSource="{Binding Source={StaticResource resFoodLookups}}" statement.
But my requirement is that this list will change from row-to-row.
That is: each time a user types a new entry in the combobox list on one row, I want to have it available in the selection on the next row.
Basically, I want to create a new list for the user each time the user inserts a new word in the combobox on any of the rows. (The combobox is editable).
Now, I can wire up the "ItemsSource=..." at run-time, but I'm only able to do this once thus the <DataTemplate> propagates the 'same' list to 'all' the comboboxes on 'all' the rows.
My thoughts are that I need to change the ItemsSource=... property on an object-by-object basis on each combobox that is created in memory after the DataTemplate has created them - but I have no idea how to do this.
What you need to do is perform 2 way data binding to your the ItemsSource, this way when the ItemSource is updated in one of the combo boxes it will auto update your original collection and therefore your other combo boxes as well.
What I normally do is use the MVVM pattern. It is worth some research if you are not already using a particular pattern on your application.
Using it to solve your problem i would do the following:
Create a ViewModel (Lets call it MyViewModel) which has a collection of values called 'MyComboBoxItems' (It is important that you use ObservableCollection for the databinding to work)
When I create the Window/Control that contains your table, I also create an instance of MyViewModel and set its the Window.DataContext=myViewModelInstance
For your combobox binding use ItemsSource="{Binding Path=MyComboBoxItems, Mode=TwoWay}

Resources