WPF ListView with CollectionViewSource not getting SelectedItem in ViewModel - c#

I Have a ListView I am grouping using a CollectionViewSource, but I cant seem to get the selected item back into the ViewModel. What do I need to do to get the item the user selects?
SelectedItem="{Binding SelectedComparatorGroupItem}" does not appear to work when using a collectionviewSource, I tried IsSynchronizedWithCurrentItem="True" but that did not help.
<Grid>
<Grid.Resources>
<CollectionViewSource x:Key="NumberGroups"
Source="{Binding Path=ComparatorGroupItemList}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Group" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Grid.Resources>
<ListView x:Name="lvNumbers"
DataContext="{StaticResource NumberGroups}"
ItemsSource="{Binding IsAsync=True}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedComparatorGroupItem}"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True">           
     
<ListView.View>
<GridView>
<GridViewColumn Header="Number" DisplayMemberBinding="{Binding Number}"/>
<GridViewColumn Header="# Found" DisplayMemberBinding="{Binding NumberFound}"/>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
Margin="3"
FontSize="18" FontWeight="Bold"
/>
<TextBlock Text="{Binding ItemCount}"
Margin="3"
FontSize="18" FontWeight="Bold"
/>
</StackPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>

You set the DataContext of the ListView to your local CollectionViewSource. That means that binding SelectedItem to SelectedComparatorGroupItem is going to look for the SelectedComparatorGroupItem property on the CollectionViewSource, not your viewmodel. You should be seeing some kind of binding error in the output window from that.
Don't set the DataContext, just let it flow through naturally. All you should need to do is bind the ItemsSource to the CollectionViewSource.
I think this will do it, but I don't have VS open, or access to your ViewModel, to verify:
<ListView x:Name="lvNumbers"
ItemsSource="{Binding Source={StaticResource NumberGroups}, IsAsync=True}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedComparatorGroupItem}"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True">
As a general rule, if you are ever setting the DataContext of something, you are not doing the way WPF wants you to do it. Usually the only place I end up setting it is when I am being lazy with a UserControl.

Related

Cannot Bind Grouping property in ListView

I have an ObservableCollection of items that I want to use to group my ListView, but I just can't bind my Grouping to that item.
That's my little class:
public class Module
{
public string Name { get; set; }
public Module(string name)
{
Name = name;
}
}
ViewModel:
public ObservableCollection<Module> ServerModules
{
get => serverModules;
set
{
if (serverModules == value) { return; }
serverModules = value;
OnPropertyChanged();
}
}
Here my Xaml Code:
<StackPanel Orientation="Vertical">
<ListView ItemsSource="{Binding ServerModules}">
<ListView.View>
<GridView>
<GridViewColumn Header="Property" Width="200"/>
<GridViewColumn Header="Value" Width="200"/>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontSize="20" FontWeight="Bold" Foreground="DodgerBlue" Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</StackPanel>
The Name Property cannot be resolved.
I also tried using a CollectionViewSource but it's also not working for me:
<CollectionViewSource x:Key="GroupModules" Source="{Binding ServerModules}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<ListView ItemsSource="{Binding Source={StaticResource GroupModules}}">
Any ideas and help are really appreciated.
Update Solution:
In my Resources section I defined a CollectionViewSource to set a GroupDescription property and a Style for my GroupItem.
<UserControl.Resources>
<CollectionViewSource x:Key="GroupedModules" Source="{Binding ServiceModuleSetViewModel.AllModuleProperties}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="ModuleName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<!-- ListView GroupStyle.ContainerStyle -->
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock Text="{Binding Name}" FontSize="13" Foreground="DodgerBlue" FontWeight="Bold" />
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
ListView:
<ListView ItemsSource="{Binding Source={StaticResource GroupedModules}}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle/>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="local:ModuleProperty">
<TextBlock FontSize="13" Text="{Binding Path=Name}" TextAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Value" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="local:ModuleProperty">
<TextBox FontSize="13" Text="{Binding Path=Value}" TextAlignment="Center"
Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The ListView control has the CollectionViewSource Resource as ItemsSource.
Each GridViewColumn has a CellTemplate to display my data in the columns.
You need to define style for GroupItem or specify GroupStyle.ContainerStyle
Also for GridViewColumn you need to specify DisplayMemberBinding.
<UserControl.Resources>
<!-- ListView GroupStyle.ContainerStyle -->
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock Text="{Binding Name}" Foreground="Gray" FontWeight="Bold" />
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<ListView ItemsSource="{Binding ServerModules}">
<ListView.GroupStyle>
<GroupStyle/>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Property" DisplayMemberBinding="{Binding Property}" Width="200"/>
<GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}" Width="200"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>

How to add ItemsSource to Combobox nested in ListBox Template from codebehind?

I have some problem with ComboBox nested in ListBox. I want to add the same ItemsSource(gained from database, adding from codebehind) to each of comboboxes created in ListBox, but don't know how. Any ideas how to do this?
</Window.Resources>
<Style x:Key="lbxKey" TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type Type1}">
<StackPanel Orientation="Horizontal" Width="Auto" HorizontalAlignment="Stretch">
<TextBlock TextTrimming="CharacterEllipsis" Width="200" Text="{Binding NAMETYPE1}" HorizontalAlignment="Left"></TextBlock>
<ComboBox HorizontalAlignment="Stretch" Tag="{Binding IDTYPE1}">
<ComboBox.ItemsSource>
<!-- no idea what should be here or even if this is needed -->
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type Type2}">
<StackPanel Orientation="Horizontal" Width="100">
<TextBlock Text="{Binding NAMETYPE2}" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
In codebehind:
lbxListbox.ItemsSource = observableCollectionFromDatabase;
//here should be sth to set ItemsSource for comboboxes in ListBox
There should be a collection type property in your item class (anything that implements IEnumerable will do). Then you would bind the ComboBox's ItemSource like this:
<ComboBox Tag="{Binding IDTYPE1}" ItemsSource="{Binding ITEMS}" ...>

Setting tag of a ListViewItem when used with Data Template

I have a List View
<ListView ItemsSource="{Binding Apparatus.AcidBaseApparatus}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Tag="{Binding AppratusName}">
<TextBlock Text="{Binding AppratusName}" Background="Azure">
</TextBlock>
<Image HorizontalAlignment="Center" MaxHeight="50" Source="{Binding ImageSource}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<i:Interaction.Behaviors>
<behave:ApparatusDragBehavior></behave:ApparatusDragBehavior>
</i:Interaction.Behaviors>
</ListView>
I want to Bind the tag of each ListViewItem to ApparatusName as I have done with the StackPanel in the Data Template. I couldn't find any option fiddling with template on my own. Is it possible to do so?
You need to set the Binding in ItemContainerStyle (targeting ListViewItem):
<ListView ItemsSource="{Binding Apparatus.AcidBaseApparatus}">
<!-- ... -->
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Tag" Value="{Binding AppratusName}"/>
</Style>
</ListView.ItemContainerStyle>
<!-- ... -->
</ListView>

c# WPF ListView Displays either Grouping of Data, but not both

I have a bound ListView on a Window and I've setup XAML to do grouping, but when I try to apply it I either get only data or grouping with no data. The XAML is as follows:
<ListView x:Name="lvNav" HorizontalAlignment="Left" Height="100" Margin="331,41,0,0" VerticalAlignment="Top" Width="166">
<ListView.View>
<GridView>
<GridViewColumn Header="Customer" DisplayMemberBinding="{Binding serviceID}" />
<GridViewColumn Header="S/N" DisplayMemberBinding="{Binding machineID}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
</StackPanel>
</Expander.Header>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
The code that loads the data is:
System.Data.Entity.DbSet<srsr> srsrs = _fa.srsrs;
srsrs.Load();
lvNav.ItemsSource = srsrs.Local;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvNav.ItemsSource);
PropertyGroupDescription pgd = new PropertyGroupDescription("stateID");
view.GroupDescriptions.Add(pgd);
I'm assuming I'm missing something relatively simple here, so hopefully someone can point me in the right direction.
If you would use the MVVM approach i.e. without code behind, you could use this:
<CollectionViewSource Source="{Binding PropertyOnYourVM}" x:Key="NameOfTheGrouping">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="PropertyToGroupOn"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
you can then use it like this on your ListView:
<ListView ItemsSource="{Binding Source={StaticResource NameOfTheGrouping}}">
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource YourStyleNameHere}"/>
</ListView.GroupStyle>
But because you are NOT using MVVM you will have to do it in code behind.
BTW: you forgot <ItemsPresenter /> in your xaml, which goes between </Expander.Header> and </Expander>

Bind property to each ListBox.ItemTemplate created

I'm totally new to wpf and silverlight, and I have A LOT to learn...
I've got a listbox which contain a template
<ListBox ItemsSource="{Binding itemList}" x:Name="list">
<ListBox.ItemTemplate>
<DataTemplate x:Name="datatemplate" >
<Grid Name="{Binding Id}">
<TextBlock Text="{Binding Txt}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i need to assign a AutomationProperties.AutomationId to each list item, like in
<ListBoxItem x:Name="lb_a" AutomationProperties.AutomationId="lb_1">
<Grid Name="grid_a">
<TextBlock Text="aa"></TextBlock>
</Grid>
</ListBoxItem>
<ListBoxItem x:Name="lb_b" AutomationProperties.AutomationId="lb_2">
<Grid Name="grid_b">
<TextBlock Text="bb"></TextBlock>
</Grid>
</ListBoxItem>
...
how can i do?
is that even possible?
You can set your attached property in ItemContainerStyle:
<ListBox ItemsSource="{Binding itemList}" x:Name="list">
<ListBox.ItemTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="AutomationProperties.AutomationId" Value="{Binding MyAutomationIdProperty}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Categories