Performance with WPF Combo Box inside a ListView - wpf

I was wondering if I was missing something obvious.
I have a simple window with a ListView with 3 columns in it.
One displays text and the other two have combo boxes in them.
The ListView has approx. 500 records and the Comboboxes both pull from the same contact list which has approx. 8,000 records in it.
I am using MVVM.
This window takes for ever to open and once it does open it is practically frozen solid (it moves so slow)
the queries to the database take under ten seconds (I log when the VM is fully loaded) then it takes two or three minutes to open the window.
I made sure to store both lists in List<T> in my VM to make sure its not reprocessing the data or anything like that.
As you can see below. I've tried explicitly using Virtualizing Stack Panel but that did not help much.
Thank you for any help
<DataTemplate x:Key="ComboboxItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.RowSpan="3" Source="{Binding ImageURL, IsAsync=True}" Width="50" />
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding Email}" />
<TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding CampusName}" />
</Grid>
</DataTemplate>
<ListView ItemsSource="{Binding MainList}" IsSynchronizedWithCurrentItem="True" Grid.RowSpan="2">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.View>
<GridView>
<GridViewColumn Width="200" Header="Internal">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding MName}" />
<TextBlock Text="{Binding CampusName}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200" Header="Contact1">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource VM}, Path=ContactList, IsAsync=True}" SelectedValue="{Binding HisContactID}" SelectedValuePath="id" ItemTemplate="{StaticResource ComboboxItemTemplate}" Background="{Binding HisColor}" Margin="0,82,0,115" Grid.Row="1" Grid.Column="1">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200" Header="Contact2">
...
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

I had the same issue and finally figured it out...
It happens that the ListView was nested inside an Infragistic TabControl and each time something was bound inside the ListView (ie: ComboBoxes), the "SelectionChange" of the TabControl was firing, causing the delay...
I've also tested with a native Microsft TabControl and I got fairly the same behavior, but somewhat a bit more performant.
I solved the issue by validating the SelectionChangedEventArgs... making sure the e.AddedItems contains only "TabItem" (and not ComboBoxes) before processing.
Hope it helps,

Related

WPF TreeView ItemTemplate alighments

i have create my own tree-view with multi column Headers using standard wpf controls
Dock Panel
DataGrid , Just for creating the columns and having sorting and resizing capabilities. The height of datagrid is only 25 , we only need to show columns not data here.
The TreeView Control with hierarchy
i have add the image just to understand the problem and the XAML code
The Account Type should be always align at the left no matter how many levels are expanded in the "first column"
somewhere in the xaml of the TreeViewItemTemplate i lost the idea.. Can anyone help me to fix the alignments
<DockPanel DataContext="{StaticResource cust}">
<Button Command="{Binding rld}" Content="reload" DockPanel.Dock="Top"/>
<!--Unbound DataGrid just to display the headers-->
<DataGrid Height="25" DockPanel.Dock="Top" ItemsSource="{Binding Customers}" AutoGenerateColumns="False" Margin="0,0,0,0">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" x:Name="col0"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding AccountType}" Header="Account Type" x:Name="col1"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<!--Actual Binding with Tree View and item Template to display the properties-->
<TreeView ItemsSource="{Binding Customers}" DockPanel.Dock="Top">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding rel}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=col0,Path=ActualWidth}"></ColumnDefinition>
<ColumnDefinition Width="{Binding ElementName=col1,Path=ActualWidth}" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="{Binding AccountType}" VerticalAlignment="Center" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</DockPanel>
The TreeView naturally indents child items, so its normal what you see.
A possible fix could be to set a negative left margin in the AccountType TextBlock with the same value of the indent, therefore neutralizing the gap.
You need to apply it only if its parent is expanded. This can easily be done modifying a bit your viewmodels.
<TextBlock Grid.Column="1" Margin="{Binding marginPropertyInVM}" Text="{Binding AccountType}" VerticalAlignment="Center"/>
You can limit the size of your first column by changing your template grids column definition to:
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="{Binding ElementName=col1,Path=ActualWidth}" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="{Binding AccountType}" VerticalAlignment="Center" />
</Grid>
This way the first column will only be allowed the remaining width once the 2nd column and indent have been allocated their space.

MVVM combobox Selected item binding in listview

I am trying to bind combo box inside of a list view.
Here is the Xaml:
<UserControl
<UserControl.Resources>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="true" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<DockPanel MinWidth="724" Height="387" DataContext="{Binding DeviceDiagnosticsMainVeiwModel}">
<globals:SaveNotificationPopup IsOpen="{Binding IsSaveNotificationPopupOpen}" SaveCommand="{Binding SaveCommand}" CancelCommand="{Binding CloseSaveNotificationPopup}" Placement="Relative" VerticalOffset="300" HorizontalOffset="200" />
<StackPanel>
<Grid Width="250" HorizontalAlignment="Left" Margin="10,10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="23" />
<RowDefinition Height="Auto" MinHeight="23" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="{x:Static ml:MultiLang._2001}" x:Name="ML_0209" />
<Label Grid.Row="1" Content="{x:Static ml:MultiLang._2009}" x:Name="ML_0211" />
<ComboBox Grid.Column="1" Grid.Row="0" ItemsSource="{Binding Manufacturers}" DisplayMemberPath="WorkingPlace" SelectedValuePath="PersonCode" SelectedValue="{Binding Path=ManufacturerID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Devices}" DisplayMemberPath="Model" SelectedValue="{Binding Path=SelectedDevice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<ListView DockPanel.Dock="Top" Margin="10,25,10,10" Width="460" ScrollViewer.HorizontalScrollBarVisibility="Hidden" HorizontalAlignment="Left" ItemsSource="{Binding CurrentDeviceDiagnostics}" SelectedItem="{Binding SelectedDeviceDiagnostics}" BorderThickness="1">
<ListView.View>
<GridView>
<GridViewColumn Header="Command" Width="180" x:Name="ML_0007">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DiagnosticsCommand, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" FontSize="12" Margin="0 5 0 0" Width="165" TextAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" Width="180" x:Name="ML_0010">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DiagnosticsName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" FontSize="12" Margin="0 5 0 0" Width="165" TextAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Units" Width="100" x:Name="ML_0013">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Units}" DisplayMemberPath="UnitName" SelectedValuePath="UnitCode" SelectedValue="{Binding DiagnosticsUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="85" Margin="0,1,0,0" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<ContentControl DockPanel.Dock="Top" Content="{Binding AddNewCAView}" />
<!-- No Diagnodtics message-->
<globals:NoItemsMessage DockPanel.Dock="Top" Visibility="{Binding NoDiagnosticsVisibility}" HorizontalAlignment="Left" Margin="10" Width="360" Height="40" Message="{x:Static ml:MultiLang._742}" x:Name="ML_0017" />
<!-- delete popup -->
<globals:DeletePopup IsOpen="{Binding IsConfirmDeletePopupOpen}" Message1="{x:Static ml:MultiLang._746}" DeleteCommand="{Binding DeleteCommand}" CancelCommand="{Binding CancelDeleteCommand}" Placement="Center" VerticalOffset="-200" HorizontalOffset="200" x:Name="ML_0018" />
</StackPanel>
</DockPanel>
</UserControl>
The Dock Panel binds to : DataContext="{Binding DeviceDiagnosticsMainVeiwModel}"
The list view binds are :
ItemsSource="{Binding CurrentDeviceDiagnostics}" SelectedItem="{Binding SelectedDeviceDiagnostics}"
and both CurrentDeviceDiagnostics and SelectedDeviceDiagnostics are members of DeviceDiagnosticsMainVeiwModel.
One text box (for example) in the list view bind like this :
Text="{Binding DiagnosticsCommand, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
CurrentDeviceDiagnostics is an observble collection of DeviceDiagnosticsVeiwModel which contains DiagnosticsCommand.
And works just fine (both of the textboxes).
The checkbox binds like this:
ItemsSource="{Binding Units}" DisplayMemberPath="UnitName" SelectedValuePath="UnitCode" SelectedValue="{Binding DiagnosticsUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
And Units and DiagnosticsUnit are also members of DeviceDiagnosticsVeiwModel.
The Item source works fine and populated the way i need.
The problem is with the selected item... its initialized as empty although DiagnosticsUnit has an integer value.
Funny thing is that if I choose something from the combobox, the code goes to the DiagnosticsUnit property to the set function, so I'm not sure abou t this problem...
Even if I will change the combobox to a textbox using DiagnosticsUnit , it will work !
I used a lots of mvvm comboboxes and didnt have any binding problems but its the first time i have a combocox inside of a list view...
Please help me.
Thank you all !!!
Let's break this down... you said:
both CurrentDeviceDiagnostics and SelectedDeviceDiagnostics are members of DeviceDiagnosticsMainViewModel.
That's as it should be. Then you said:
CurrentDeviceDiagnostics is an ObservableCollection<DeviceDiagnosticsViewModel>
Therefore the data bindings from inside the CellTemplates should point to properties from the DeviceDiagnosticsViewModel class as you correctly pointed out.
Therefore, looking at your ComboBox code below, I can only think of two possible reasons why this wouldn't work:
<ComboBox ItemsSource="{Binding Units}" DisplayMemberPath="UnitName"
SelectedValuePath="UnitCode" SelectedValue="{Binding DiagnosticsUnit, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="85" Margin="0,1,0,0" />
One suggestion would be to use the ComboBox.SelectedItem property instead and to data bind to an instance of the same type as your Units collection... however, if that is just a collection of ints, then this step wouldn't help. The only other thing would be to ensure that you have correctly implemented the INotifyPropertyChanged interface in the DeviceDiagnosticsViewModel class.
O.K
I finally found the problem !
It wasn't a binding problem.
this is very silly.
i had this code
public void GetItems()
{
_devicesDiagnostics = new ObservableCollection<DeviceDiagnosticsVeiwModel>(
(from dd in _dal.GetItems()
orderby dd.DeviceID
select new DeviceDiagnosticsVeiwModel(dd)
{
ObjectStatus = Status.NoChange,
Units = _manager.UnitRepository.Units
}
) .ToObservableCollection<DeviceDiagnosticsVeiwModel>()
);
}
The Units is a member in the view model (DeviceDiagnosticsVeiwModel) and it's actually the comboboxs item source.
Inside of DeviceDiagnosticsVeiwModel there's an int property which is the selected item. It didnt load it right.
The correct code will be
public void GetItems()
{
_devicesDiagnostics = new ObservableCollection<DeviceDiagnosticsVeiwModel>(
(from dd in _dal.GetItems()
orderby dd.DeviceID
select new DeviceDiagnosticsVeiwModel(dd)
{
ObjectStatus = Status.NoChange,
Units = new ObservableCollection<UnitViewModel>(this.Units),
}
)
.ToObservableCollection<DeviceDiagnosticsVeiwModel>()
);
}
I had to give every object a new list. It worked.

WP8 - XAML Grid column expanding when text is too long

I have a grid with column definitions.
Whenever the text inside a column cell exceeds the designated width, it shrinks the left hand column down.
Here's an example
And this is my xaml markup
<ItemsControl Name="rfbItems" ItemsSource="{Binding}"
Style="{StaticResource contentItemsControl}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Width="Auto" HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Title}" Style="{StaticResource App_Content_Grid}" TextAlignment="Left" />
<TextBlock Grid.Column="1" Text="{Binding Description}" Style="{StaticResource App_Content_Grid_Subtle}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How should I restrict this behavior without losing responsiveness in the layout?
Also, if there is a better way to achieve what I'm trying to do here (inside the datatemplate), please feel free to share :)
Try setting margin and horizontal alignment to text boxes as below:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="5" HorizontalAlignment="Stretch" Text="{Binding Title}" Style="{StaticResource App_Content_Grid}" TextAlignment="Left" />
<TextBlock Grid.Column="1" Margin="5" HorizontalAlignment="Stretch" Text="{Binding Description}" Style="{StaticResource App_Content_Grid_Subtle}" />
</Grid>
The Grid doesn't have a limited width unless you give your StackPanel a fixed width. Setting Width="123" should work, HorizontalAlignment="Stretch" might do it as well (untested).
I'm sorry for all your efforts, but I found a solution.
None of the answers worked for me, so feel free to find a fitting solution and I'll accept your answer.
<ItemsControl Name="rfbItems" ItemsSource="{Binding}"
Style="{StaticResource contentItemsControl}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Width="Auto" HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="125" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Title}" Style="{StaticResource App_Content_Grid}" TextAlignment="Left" />
<TextBlock Grid.Column="1" Text="{Binding Description}" Style="{StaticResource App_Content_Grid_Subtle}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The first column has a fixed width, that's why the second column can't expand anymore.

Binding for nested ListView not working

I have a nested ListView, but the ItemSource binding is never firing. Am I missing something in here? Here you can see my parent list view, which is binding just fine. But the nested one does not.
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<CheckBox IsChecked="True" Margin="0,0,5,0"></CheckBox>
<TextBlock Text="{Binding Name}" Margin="0,0,15,0"/>
<TextBlock Text="Task Set Loop: "/>
<TextBox Text="{Binding Scenarios}"/>
</StackPanel>
<ListView Grid.Row="1" ItemsSource="{Binding ChildItems, Converter={StaticResource DebugBindingConverter}}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<CheckBox IsChecked="True" Margin="0,0,5,0"></CheckBox>
<TextBlock Text="{Binding Name}" Margin="0,0,15,0"/>
<ComboBox SelectedItem="{Binding DependentTaskName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
My binding for the ChildItems was at the wrong level. Changed that line to:
<ListView Grid.Row="1" ItemsSource="{Binding Path=DataContext.ChildItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
For UWP we can get it like this
<GridView x:Name="abc" ItemsSource="{Binding Path=DataContext.Companies,RelativeSource={RelativeSource Mode=TemplatedParent}}"></GridView>

Binding nested control using MVVM pattern

I have a problem with binding nested control with my MVVM pattern. This is my XAML code:
<ItemsControl Grid.Column="1" ItemsSource="{Binding NotificationContacts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:Expander>
<toolkit:Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ContactName}" Grid.Column="0" VerticalAlignment="Center"></TextBlock>
<Image Source="Images/answer_ok.png" Grid.Column="1" Margin="15,0,15,0" Width="27" Height="27"></Image>
</Grid>
</toolkit:Expander.Header>
<toolkit:Expander.Content>
<ListBox Margin="30,10,0,10" ItemsSource="{Binding NotificationContacts.Messages">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MessageName}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</toolkit:Expander.Content>
</toolkit:Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is, that the listbox control located in ExpanderControl Data Template is not being data bound.
Listbox control is populated by EntityCollection named 'Messages' which is contained in parent object 'NotificationContacts' that ItemsControl is databound with...
Does anyone know how to resolve this issue ?
Thanks in advance !!!
Did you try this:
<ItemsControl Grid.Column="1" ItemsSource="{Binding NotificationContacts}">
......
<ListBox Margin="30,10,0,10" ItemsSource="{Binding Messages}">
.....
<TextBlock Text="{Binding MessageName}"></TextBlock>
If I remember it right, when you are "inside" ItemContol, binding context is set to NotificationContacts. So use just "{Binding Messages}" could be fine.
And by the way, you are missing curly bracket on the line:
<ListBox Margin="30,10,0,10" ItemsSource="{Binding NotificationContacts.Messages">
Call ItemsControl f.i. "ic" and use next binding in ListBox
<ItemsControl x:Name="ic" Grid.Column="1" ItemsSource="{Binding NotificationContacts}">
...
<ListBox Margin="30,10,0,10" ItemsSource="{Binding ElementName=ic, Path=DataContext.Messages}">

Resources