WPF ComboBox doesn't initially select correct item - c#

I have a WPF ComboBox that I bind to a list of custom objects, each of which contains an IsSelected property. This property is bound to its corresponding ComboBoxItem's IsSelected property. When initially creating the list, I set a particular object's IsSelected property to true, with the expectation that this item will be selected when the ComboBox is initially shown. However, the ComboBox always shows the first item in the list as selected, regardless of which object in its bound list has its IsSelected property set to true.
I know that the binding is working properly, because when I click the ComboBox and it expands to show all the available options, the ComboBox updates to show the correct selected item.
Here's the XAML the defines my ComboBox. It's in a DataTemplate, and is dynamically added/removed from the page, if that is at all relevant:
<ComboBox ItemsSource="{Binding DataContext.YearList, RelativeSource={RelativeSource AncestorType=Window}}" IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Object}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Try removing IsSynchronizedWithCurrentItem="True". That setting is telling the control to use the CurrentItem on the bound collection's generated ICollectionView, which defaults to the first item in the list. If you want to see what its doing you can play with this view in code by using CollectionViewSource.GetDefaultView(YearList) and then looking at or changing the current item.

Try binding to SelectedItem property, ComboBox will not select first item by default but your Source of data binding:
<ComboBox ItemsSource="{Binding YearList, RelativeSource={RelativeSource AncestorType=Window}}" SelectedItem="{Binding <YourDefinedSelectedItemProperty>, RelativeSource={RelativeSource AncestorType=Window}}">
Hope this helps.

Related

Handle Multiselect in ListView (WPF + MVVM) [duplicate]

I have a ListBox that I populate dynamically via a binding (this is defined in a DataTemplate, which is why the binding is somewhat unusual):
<ListBox SelectionMode="Extended" ItemsSource="{Binding DataContext.ResultList, RelativeSource={RelativeSource AncestorType=Window}}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Object}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Each ListBoxItem's IsSelected property is bound to an IsSelected property on a custom object.
When I select individual ListBoxItems, the binding works properly - the custom object's IsSelected property is updated in my ViewModel. However, if I select all of the ListBoxItems with a Ctrl+A command, only the currently visible ListBoxItems (those that are currently in my scrolling viewport) update their ViewModel bindings. On the frontend, all the ListBoxItems appear to be selected, and the ListBox.SelectedItems.Count property on the container ListBox shows that all items are selected.
Furthermore, as I scroll through the ListBox after selecting all ListBoxItems with Ctrl+A, the bindings are successfully updated when each ListBoxItem is scrolled into view.
Why does this binding seem to be only partially working? Is there a better way to handle the binding of the IsSelected property when large numbers of ListBoxItems can be selected simultaneously?
Edit:
This behavior doesn't happen exclusively with the Ctrl+A command - I get the same results when selecting all the items using a shift+click.
I think the behavior you're seeing is to due to VirtualizingStackPanel.IsVirtualizing which is True by default when binding to ItemsSource of ListBox
if you for eg set your ListBox such as:
<ListBox VirtualizingStackPanel.IsVirtualizing="False" SelectionMode="Extended" ItemsSource="{Binding DataContext.ResultList, RelativeSource={RelativeSource AncestorType=Window}}">
or
<ListBox ...>
...
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
then you should see all your bound items have their IsSelected updated accordingly with Ctrl+A or Shift + ...
Properties such as Count of the collection even with virtualization would report the correct value to accommodate for things like computing the required ScrollBar.Height. Items which are outside the View-port do not get rendered hence no bindings are in effect on them until they actually get used.

Selecting multiple Checkboxes at once

I'm looking for a way to select multiple checkboxes at once in WPF. I'm using the MVVM pattern without any further augments like PRISM. I'm loading data from a MySQL database and binding it to a Data Grid. Then I want to select some of those tables and then add them to another Data Grid. The solution I came up with was creating checkboxes dynamically and binding them to a IsSelected property in my data grid.
<DataGridTemplateColumn
Header=""
Width="auto"
CanUserResize="False"
CanUserReorder="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="RadioButtonDatabase"
IsChecked="{Binding IsSelected,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The good thing: It works. The bad thing: Each entry in my Datagrid has to be manually clicked, so that IsSelected is updated for each object. I'd like to see a solution that makes multiselection possible (with shift), and possibly a key(space?). Other good ideas are also welcome. Research on the internet didn't yield a satisfying solution.
I'd prefer no answers using codebehind, I'm trying to stick as close to strict MVVM as possible.
You can define Command for your checkbox in your ViewModel(cause I hope you also want binding to reflect on your IsSelected property for each data item when checked):
<CheckBox Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}},Path=DataContext.CheckedCommand}" .../>
ViewModel:
{
CheckedCommand = new RelayCommand(() => this.CheckAllCheckboxes());
}
public RelayCommand CheckedCommand { get; set; }
public void CheckAllCheckboxes()
{
//set IsSelected true for all items here
}
Get Relay Command from here
Update:
Define a Row style for datagrid that will bind the IsSelected Property of DataGridRow to your Model's any property and then in command action check if row is selected:
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsSelected" Value="{Binding IsRowSelected}" />
</Style>
</DataGrid.RowStyle>
If also want to enable/disable on selection add this binding to checkbox:
IsEnabled="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},Path=IsSelected,Mode=OneWay}"

How to bind to a source inside a ListBox different from the ItemsSource already specified

I have a ListBox inside a HubSection, whose Items are bound to a class "players" added to my DefaulViewModel via code behind.
First I simply put a TextBox bound to the property "PlayerName" of my class "players".
Now I would like to add a ComboBox with some items that are NOT part of the class players.
Is it possible ? I thought that definind an ItemsSource in the ComboBox would sort of override the ItemsSource of the ListBox, but nothing displays.
The DataContext of the whole page is defined like so:
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
Then the HubSection is like so:
<HubSection x:Name="HubSec1">
<DataTemplate>
<ListBox x:Name="ListBox1" ItemsSource="{Binding players}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Path=PlayerName, Mode=TwoWay}"/>
<ComboBox ItemsSource="{Binding Path=ListOfElements}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</HubSection>
If I define the ComboBox in the same way but outside the ListBox, it will display the string elements of "ListOfElements" properly.
But in this ListBox, the ComboBox is empty. So my guess is that having defined an ItemsSource for the ListBox, it is not possible to override it.
I have tried to define a DataTemplate but was not successful doing so, but it might be the good solution (and I did not proceed properly)
What am I missing ?
Edit :
The ComboBox items is an ObservableCollection. It is not part of the "players" class.
Here is how I added these elements to the DefaultViewModel
DefaultViewModel.Add("players", players);
DefaultViewModel.Add("MyItemsList", ListOfElements);
You can walk up the visual tree and bind to an ancestors datacontext:
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
EX:
{Binding Path=ListOfItems, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}
that should give you the datacontext that the listbox has, so assuming your ListOfItems exists in that data context.
Or you can name your control, and then bind to its datacontext by element name:
{Binding ElementName=mySourceElement,Path=ListOfItems}
It can be a little bit tricky to create a good working binding in Windows Apps. A widely used work around is to use the Tag property.
<ListBox x:Name="ListBox1" ItemsSource="{Binding players}" Margin="0,184,0,0" Tag="{Binding Path=ListOfElements}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Path=PlayerName, Mode=TwoWay}"/>
<TextBox Text="{Binding Path=Tag, ElementName=ListBox1}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
A binding to an element wirh the specific name will work always. And the ListOfElements should be in the scope of the ListBox so you can use the Tag property as a proxy. If you need to bind more than one property, you can also use dummy XAML elements:
<Border Tag="{Binding ...}" Name="dummy1"/>

How to show button when the user clicks a listbox item?

I was checking this window. It has a listbox, and when you click an item, it shows three buttons.
I want to implement something similar in my program. How can I do that?
You can bind to the IsSelected of the item, either by directly binding the Visibility using a BooleanToVisibilityConverter or by employing a DataTrigger.
Direct Binding
<ListBox.Resources>
<BooleanToVisibilityConverter x:Key="b2v"/>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- ... -->
<Button Visibility="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem},
Converter={StaticResource b2v}}".../>
</DataTemplate>
</ListBox.ItemTemplate>
Style & DataTrigger
throw new NotImplementedException();

WPF ListBox in Popup setting null SelectedItem on PopupClose

I have a WPF Popup, which is structured as below (with some senstitive stuff removed)...
<Popup>
<Border>
<StackPanel>
<ListBox
ItemSource="{Binding X}"
SelectedItem="{Binding Y}"
IsSynchronizedWithCurrentItem="True"/>
<Separator/>
<MenuItem Command="{Binding Path=EditModeCommand}"/>
</StackPanel>
</Border>
</Popup>
The ListBox works as expected, the list populates from the binding, and the selected item feeds back to the collection correctly.
However when the MenuItem fires its command, the SelectedItem binding fires as well, setting the SelectedItem to null. Is there a way to preserve the SelectedItem when the listbox is not the focus of the click?
Try setting the SelectedItem property before the ItemSource property in the declaration.

Categories