WPF ListBox selected item not changed while using DataTemplate - c#

My listbox is binded to an items source and selecteditem property is also binded. Most of my work is being done in selecteditem property. Actually I have two listboxes, for each item in first list box there are some child items in the collection. Against all the items that are selected in first listbox, their child items are supposed to be added in the second list box.
Problem is Selecting the item(by checking the checkbox) does not raise the SelectedItem property changed
XAML for my listbox controls are
<ListBox SelectionMode="Multiple" ItemsSource="{Binding Charts,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedChart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding ChartName}" VerticalAlignment="Center" IsChecked="{Binding IsChartSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding Tracks, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding IsTrackSelected}"/>
<TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="{Binding TrackName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
My Chart Selection Changed property in view model is
public ChartSourceForMultipleSelection SelectedChart
{
get { return _selectedChart; }
set
{
_selectedChart = value;
ChartSelectionChanged();
NotifyPropertyChanged("SelectedChart");
}
}

This binding doesn't make any sense:
<CheckBox IsChecked="{Binding IsTrackSelected,
RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
This attempts to bind to a property IsTrackSelected on the outer ListBoxItem, but no such property exists. It doesn't look like you need a RelativeSource here, provided IsTrackSelected is a property of the underlying data item.
Also, I'm not sure why you're a ListBox for the tracks collection; it looks like your concepts of track selection is separate from the ListBox concept of a selected item. Why not use a simple ItemsControl instead?
As to your primary problem, the CheckBox in the item template is eating the mouse/focus events that would normally tell the ListBox to select the parent ListBoxItem when clicked. You can manually update the selection state of a ListBoxItem whenever its inner CheckBox receives keyboard focus.
Add GotKeyboardFocus="OnChartCheckBoxGotKeyboardFocus" to the CheckBox your chart list item template, give the chart list box a name, e.g., x:Name="ChartsList", and write the handler in the code behind:
private void OnChartCheckBoxGotKeyboardFocus(
object sender,
KeyboardFocusChangedEventArgs e)
{
var checkBox = sender as CheckBox;
if (checkBox == null)
return;
var listBoxItem = ItemsControl.ContainerFromElement(ChartsList, checkBox)
as ListBoxItem;
if (listBoxItem != null)
listBoxItem.IsSelected = true;
}

Related

Using ItemTemplate and DataTemplate creates a button within ListView. Why?

I have a situation with a ListView. Inside the item template there is a DataTemplate and inside the DataTemplate there is a ListViewItem and it's content is bound. When I click I want to select the item but when I hover my mouse over it, it seems like there is a button created with the ItemTemplate and DataTemplate . At the moment, I can select item by clicking somewhere else within the same row, but that doesn't work if I select the button. I use SelectedItem and if you click on the button within the item, the SelectedItem will not fired.
The ListView is linked to an ItemSource, and SelectedItem of the ListView is bound to a property
Further information: language is C#, ide is Visual Studio 2010.
I will leave my xaml code below, Thanks.
XAML
<ListView x:Name="myListView"
Grid.Column="0"
Grid.Row="2"
Height="Auto"
Margin="0,0,0,0"
SelectionChanged="SchemaListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem Content="{Binding Path=Value}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#
void ReadData(string id)
{
var data = ExampleData.GetData(id);
myListView.ItemsSource = data.Results;
}
I've tried binding without ItemTemplate and DataTemplate also tried to link source in xaml but that didn't work
You must not have a ListViewItem in the ItemTemplate of a ListView.
When the elements of the ItemsSource collection are not already ListViewItems (which they typically never are), the framework will automatically create a ListViewItem that becomes the "item container" element for each data item. This automatically generated ListViewItem uses the ItemTemplate as its ContentTemplate.
Besides that, use a ListBox instead of ListView when you do not set a ListView's View property.
So instead of a ListViewItem, use a ContentControl or perhaps a TextBlock:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If there is nothing more than a single content element, simply set the DisplayMemberPath property:
<ListBox ... DisplayMemberPath="Value"/>

How can I disable the selection event on this WPF ComboBox without disabling it?

I have the following ComboBox XAML:
<ComboBox Width="350" ItemsSource="{Binding RunSets}" SelectedItem="{Binding SelectedRunSet, Mode=OneWay}" VerticalAlignment="Center" Height="20" Margin="5,1,0,0" Background="DimGray" Foreground="White" >
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Width="320">
<CheckBox IsChecked="{Binding IsSelected}" Margin="2,2,10,0" Visibility="{Binding ExchangeMarketNamesVisibility}" PreviewMouseDown="RunsetComboBoxItem_PreviewMouseDown">
<CheckBox.Content>
<StackPanel PreviewMouseDown="RunsetComboBoxItem_PreviewMouseDown">
<StackPanel Orientation="Horizontal" PreviewMouseDown="RunsetComboBoxItem_PreviewMouseDown">
<TextBlock Text="{Binding DisplayName}" PreviewMouseDown="RunsetComboBoxItem_PreviewMouseDown"/>
</StackPanel>
<StackPanel PreviewMouseDown="RunsetComboBoxItem_PreviewMouseDown">
<TextBlock Text="{Binding ExchangeMarketNames, StringFormat=🢒 {0}}" Visibility="{Binding ExchangeMarketNamesVisibility}" Margin="2,2,0,2" Foreground="LightGray" PreviewMouseDown="RunsetComboBoxItem_PreviewMouseDown" />
</StackPanel>
</StackPanel>
</CheckBox.Content>
</CheckBox>
<TextBlock Text="Select Items..." Visibility="{Binding DefaultItemVisibility}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The goal I'm trying to achieve is to have the ComboBox present a set of selectable ComboBoxItems. It should always display "Select Items" when closed:
And when opened I would like it to display the following:
When the highlighted area is clicked it should check the checkbox without closing the combobox. I have managed to get most of the behaviour I want by hacking the Visibility property to hide the checkbox in the "Select Items..." ComboBoxItem. Together with capturing the PreviewMouseDown event:
private void RunsetComboBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var checkBox = sender as CheckBox;
if(checkBox != null)
{
checkBox.IsChecked = !checkBox.IsChecked;
e.Handled = true;
}
}
This is almost working perfectly, but there are two issues that I can't resolve:
If I click on the border between the ComboBoxItems in the dropdown it will select that ComboBoxItem. I want it to never select any of them. I have managed to work around for clicks inside the highlighted area by capturing the preview mouse down event on the Grid in the and marking it handled. Unfortunately in the border this doesn't get triggered and the SelectedItemChanged event gets triggered on the ComboBox. This causes the selected item to change (as mentioned, I want the ComboBox to always display the "Select Items..." ComboBoxItem.
The "Select Items..." is shown twice when the combo box is expaned. I would like it to show just once.
I have managed to get most of the behaviour I want by hacking the Visibility property to hide the checkbox in the "Select Items..." ComboBoxItem together with capturing the PreviewMouseDown event.
I have read through many stack overflow posts about this, and I have not found a way of doing this. I tried capturing the SelectionChange and marking it handled but it closed the combobox anyway, and there is no PreviewSelectionChange event. When an item is selected I'd like the combobox to stay open.
Is there any way I can achieve this? Please let me know if there's any more information I can add to clarify my problem. Thanks!

XAML binding to IsChecked for all ToggleButtons in an ItemsControl

I have seen many questions similar to mine but none that solves or addresses my problem.
What I have is a group of toggle buttons that exist inside of an ItemsControl, and these toggle buttons are created dynamically at run time based off of the number of people inside of an index that the ItemSource of the ItemsControl is bound to. When these toggle buttons are clicked in the UI, some filtering is done based on the person's name. Multiple buttons or even all the buttons can be selected if so desired. There is also a "clear" button which simply removes all filtering and should reset the toggle button IsClicked states.
My problem is that when I click the clear button, all of the toggle buttons that were clicked remain clicked. I am trying to have the clear button reset every ToggleButton's IsClicked value to false by binding the IsClicked value to a property that is managed in the code, but no matter what I try I cannot get this to work.
Code:
Xaml
<Button Content="Clear Filter" HorizontalAlignment="Center" Margin="0,0,0,1" VerticalAlignment="Bottom" Grid.Column="1" Click="ClearClick"/>
<ItemsControl ItemsSource="{Binding Source={StaticResource peopleSorted}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Name="toggleButton" Margin="0,0,5,0" Command="ApplicationCommands.Open"
CommandParameter="{Binding Name}" IsChecked="{Binding IsChecked, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
c# ViewModel:
private void ClearClick(object sender, RoutedEventArgs e)
{
this.m_viewModel.ClearFiltering();
}
C# model:
public bool IsChecked
{
get
{
return this.m_isChecked;
}
set
{
this.m_isChecked = value;
this.OnPropertyChanged("IsChecked");
}
}private bool m_isChecked;
public void ClearFiltering()
{
m_hashTable.Clear();
this.IsChecked = false;
}
I am pretty stuck. I feel like the reason the binding isn't working is that the toggle buttons are dynamically created inside of an ItemsControl, but I am not sure if that's what's wrong or how to fix it if it is.
UPDATE
I was able to work around this by doing a swap and swap-back on the data of observable collection that the ToggleButtons ItemsControl is bound to, which triggered the OnPropertyChanged for the entire collection of toggle buttons, thus resetting that part of the UI. It is a simple hack with almost no overhead and four lines of code, but doesn't really answer the original question.

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.

WPF ListBox selection does not work

I have a ListBox for a few items, and I need to be able to click them. Problem is, the SelectionChanged event doesn't get fired when I click on the item's text, only if I click on the blank part. I'm quite new to WPF, and I don't understand why this is happening.
XAML:
<ListBox Name="lBoxVouchers" BorderThickness="0" FontSize="15" SelectionChanged="lBoxVouchers_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Content="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Handler:
private void lBoxVouchers_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
MessageBox.Show("You just selected " + e.AddedItems[0]);
}
I'm binding the list of objects in code via the lBoxVouchers.ItemsSource property, and they show up. Each object has a Name property, of course.
I've tried setting IsEnabled on the ListBox and the items, both in code and XAML, but it doesn't help.
Any comments about better ways to do this in WPF are also welcome.
If you only want to show the Name property you could define your listbox like this:
<ListBox Name="lBoxVouchers" BorderThickness="0" FontSize="15" SelectionChanged="lBoxVouchers_SelectionChanged" DisplayMemberPath="Name" />
If you put your items on an ObservableCollection on code-behind, you can also pass the databinding to XAML:
<ListBox Name="lBoxVouchers" BorderThickness="0" FontSize="15" SelectionChanged="lBoxVouchers_SelectionChanged" DisplayMemberPath="Name" ItemsSource={Binding Path=Items}" />
And on your code behind you should have something like:
ObservableCollection<object> Items {get; set}
About the handler, I would also do something like this:
private void lBoxVouchers_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (((ListBox)sender).SelectedItem != null)
MessageBox.Show("You just selected " + (ListBox)sender).SelectedItem);
}
Set IsSynchronizedWithCurrentItem="true" on the listbox.
Here you can find a starting point to get more details about this property.
Setting this property to true makes the selection be in sync with the current item which holds the actual selected item. When you click the blank space probably the current item changes to null and you get your event handler called.
may be the content in the listbox item is not stretched. just write this style for the listbox item and try.
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>

Categories