MVVM combobox Selected item binding in listview - wpf

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.

Related

Binding to elements in WPF GridViewHeader doesn't work

I'm trying to create a custom header for a ListView / GridView where the user can easily filter items using Text inputs:
I've created DependencyProperties on the UserControl's code-behind and are binding the Xaml datacontext to itself (name=root). The binding i'm having trouble with is the TextBox inside the Header - it simply doesn't work. If i set a 'ModelVariantId' to "Whatever" it doesn't show and likewize i don't get any PropertyChanged events when writing in it..
I've found -some- similar sounding questions on the internet but nothing exactly matching.
<ListView ItemsSource="{Binding ElementName=root, Path=CombinedCarModels}">
<ListView.View>
<GridView>
<GridViewColumn Width="120" DisplayMemberBinding="{Binding ModelVariantId}">
<GridViewColumn.Header>
<GridViewColumnHeader HorizontalContentAlignment="Stretch">
<StackPanel>
<Label>ModelVariantId</Label>
<TextBox Text="{Binding ElementName=root,
Path=CarModelFilter.ModelVariantId, Mode=TwoWay}" />
</StackPanel>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
If you define a HeaderTemplate, this binding should work:
Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl},
Path=CarModelFilter.ModelVariantId, Mode=TwoWay}"
XAML:
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" HorizontalAlignment="Stretch">ModelVariantId</Label>
<TextBox Grid.Row="1" HorizontalAlignment="Stretch"
Text="{Binding ElementName=root, Path=CarModelFilter.ModelVariantId, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
There is no element named "root" in the same naming scope as the header, but you should be able to bind to a parent element in the visual tree using the RelativeSource property.
This assumes that the parent UserControl has a CarModelFilter property.

Why my autocompletebox does not display any data?

I have a silverlight autocompletebox, am adding the ItemsSource in the codebehind.cs , also set the valuemember path as well, it does not display any data.
here is the code,
<sdk:AutoCompleteBox Margin="105,2,40,0" ItemsSource="{Binding}" ValueMemberPath="Code" FilterMode="Contains" IsTextCompletionEnabled="True" x:Name="txtcode" Height="23" VerticalAlignment="Top" TabIndex="1" TabNavigation="Local" >
<sdk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</sdk:AutoCompleteBox.ItemTemplate>
</sdk:AutoCompleteBox>
txtcode.ItemsSource = collection;
Possibly you have missed this one inside the DataTemplate,
<TextBlock Text="{Binding Code}" />
You Should bind that textbox first
Sample Code:
<sdk:AutoCompleteBox Margin="105,2,40,0" ItemsSource="{Binding}" ValueMemberPath="Code" FilterMode="Contains" IsTextCompletionEnabled="True" x:Name="txtcode" Height="23" VerticalAlignment="Top" TabIndex="1" TabNavigation="Local" >
<sdk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Item}" />
</DataTemplate>
</sdk:AutoCompleteBox.ItemTemplate>
</sdk:AutoCompleteBox>
txtcode.ItemsSource = collection;

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 to the IsSelected property of the parent ListViewItem

I'm attempting to bind the Visibility property of a TextBlock that's held within the ItemTemplate for a ListView to the IsSelected property of the TextBlock's parent ListViewItem.
With this markup, the TextBlock is always visible.
<ListView x:Name="ItemListView" ItemsSource="{Binding Path=Accounts}" Margin="60,0,0,10" Grid.Row="1" Grid.Column="0">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100">
</ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Width="100" Height="100" Grid.Column="0"></Image>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Path=Account.Name}"
FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
</StackPanel>
<TextBlock Grid.Column="3" VerticalAlignment="Bottom"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=IsSelected, Converter={StaticResource boolConverter}, Mode=OneWay}">
Show More Details...
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Notes:
1. In case it makes any difference, this is WinRT; a Metro app written in C#.
2. boolConverter is a fairly standard converter appears to work correctly.
Use Mode=FindAncestor:
<TextBlock Grid.Column="3" VerticalAlignment="Bottom"
Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListViewItem}, Path=IsSelected, Converter={StaticResource boolConverter}, Mode=OneWay}">
I think that in this case you will have to use ElementName=ItemListView
#Murven 's answer was close. This is what worked for me:
<TextBlock Visibility="{Binding DataContext.IsSelected, ElementName=ItemListView Converter={StaticResource boolConverter}, Mode=OneWay}">
I had to use DataContext.IsSelected to access the context of the ItemListView.
Not sure if there is a better way.

Performance with WPF Combo Box inside a ListView

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,

Resources