XAML binding to IsChecked for all ToggleButtons in an ItemsControl - c#

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.

Related

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!

How do I prevent a ToolBar in a nested UserControl from being focused when switching tabs?

I wrote an example application that highlights the issue, which might have to do with focus-scope because it's whenever a ToolBar is involved and a UserControl is unloaded when switching tabs.
The main window contains a Menu with a File->New command and a TabControl that displays a collection of "Box" instances:
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Command="New"/>
</MenuItem>
</Menu>
<TabControl ItemsSource="{Binding Boxes}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Width="40"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<local:BoxView/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</DockPanel>
Each "Box" instance has a Name property displayed in the tab header, and the rest of it is displayed in a "BoxView" UserControl. The "BoxView" has a ToolBar, the first button of which becomes the focus hog in the mentioned use case. The "BoxView" also displays a collection of "Compartments" that are inside each "Box" instance:
<StackPanel>
<ToolBarTray IsLocked="True">
<ToolBar>
<Button Content="Focus hog"/>
</ToolBar>
</ToolBarTray>
<ItemsControl ItemsSource="{Binding Compartments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CompartmentView/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Each "Compartment" instance has a TotalValue property simply displayed in a TextBox in a "CompartmentView" UserControl:
<StackPanel>
<TextBox Text="{Binding TotalValue}"/>
</StackPanel>
And the problem is...
If I click on the TextBox of any one of the CompartmentView's, and then click a different tab of the main window, the first button of the ToolBar of the BoxView is focused (the focus hog one), disabling commands working in the main window.
It can be observed commands are disabled when opening the File menu. But after closing the menu and reopening, commands are enabled again.
How do I prevent the ToolBar in the nested UserControl from being focused when switching tabs?
Or perhaps I'm going at this wrong... how do I prevent focus from being set when switching tabs?
Update
The following link is a video on youtube where I explain the problem with the application.
https://youtu.be/0T5LK3CYxgw
Solution update
Thanks to themightylc for the answer, the issue was clearly a keyboard focus problem when the focused visual control (any selected "CompartmentView" or "BoxView" control) was unloaded while changing the selected tab in the TabControl. The solution was to focus on the TabControl itself in the main window whenever the tab selection changed:
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
(sender as TabControl).Focus();
}
Thanks also to Christoph Nahr's response for the solution on this msdn thread: https://social.msdn.microsoft.com/Forums/vstudio/en-US/f5de6ffc-fa03-4f08-87e9-77bbad752033/a-focusscope-nightmare-bug-commands-are-disabled?forum=wpf
<Button Content="Focus hog" Focusable="False" />
This solves the problem you're having. Focusable is a DependencyProperty If you still want to access the ToolBar via Keyboard-Focus, you can bind it or set it from code-behind when the Tab-Switch is complete.
You can of course set it via Style so you don't have to set it on every element of the ToolBar.

set SelectionChanged in ListView Clickmode = "pressed" in uwp

I have a ListView and i want ListView to be selected when Mouse pressed. (Like ClickMode property of Buttons). is there any solution ?
I tried to put button in ListView template and change its ClickMode property
The SelectionChanged is triggered when mouse released which is by default. It seems like we cannot change it. So we can put button in ListView item template and change its click mode as what you thought about, or register a PointerPressed event for the container inside item template which will be triggered when mouse pressed not released.
but i couldn't access object that is clicked in the ListView
To access which item is clicked you still can use property like SelectedItem of ListView to get the selected item. For example:
XAML Code
<ListView Name="CategoryLIstView" Grid.Row="1" ItemsSource="{x:Bind categories}" HorizontalAlignment="Center" Margin="10" VerticalAlignment="Top" IsItemClickEnabled="True" SelectionChanged="CategoryLIstView_SelectionChanged" ItemClick="CategoryLIstView_ItemClick" PointerPressed="CategoryLIstView_PointerPressed">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Category">
<StackPanel Margin="0" PointerPressed="StackPanel_PointerPressed">
<StackPanel Orientation="Vertical" >
<TextBlock Text="{x:Bind Name}" Foreground="Blue" FontWeight="Bold" FontFamily="Yu Gothic" FontSize="17"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code behind:
private async void StackPanel_PointerPressed(object sender, PointerRoutedEventArgs e)
{
await new Windows.UI.Popups.MessageDialog("point press").ShowAsync();
System.Diagnostics.Debug.WriteLine(CategoryLIstView.SelectedIndex);
}

ListView SelectItem Binding not working as expected

I have a fairly straight forward listview control for my Windows 8 XAML/C# application. I am binding the listview to a PersonList and that works correctly. However, what I'd like to do and haven't been able to find the answer for is I want the to click an item in the listview and be able to display the PersonSingle object to the other textboxes on the screen.
I've read some posts that indicate that the listview may not be the right control for this operation. Am I missing something in my listview that would allow me to do this operation or should I use a different control?
<ListView
x:Name="itemListView"
Visibility="Visible"
Width="auto"
Height="auto"
ItemsSource="{Binding PersonList, Mode=TwoWay}"
SelectedItem = "{Binding PersonSingle, Mode=OneWay}"
ItemTemplate="{StaticResource person80Template}"
SelectionMode="Single"
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Stretch">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="7,7,0,0">
<Button
AutomationProperties.Name="PersonValue"
Content="{Binding PersonName}"
Style="{StaticResource TextButtonStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
UPDATE:
So I figured out how to do it in a non-MVVM way (or at least not in a true MVVM way)
I added an ItemClick event to my ListView:
ItemClick="itemListView_ItemClick"
And then in the code behind I added the following:
private void itemListView_ItemClick(object sender, ItemClickEventArgs e)
{
VM.PersonSingle = ((Person)e.ClickedItem);
}
That works, but like I said, it doesn't feel very MVVM'ish. If you have any suggestions on how to make it work without having to manually set the PersonSingle object please answer below.
Your ItemClick solution is the right solution. You could create an attached behavior if you are opposed to handling events, but that's your choice.

How to determine listboxitem a button was pressed from?

Basically I'm dynamically adding items to a listbox. Inside of each listbox item I have also added a button control that will perform a specific action on the item from which the button was clicked.
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title"/>
<Button Click="MyBtn_Click"/>
</StackPanel>
</DataTemplate>
Does anyone know how to determine which item the button was clicked from? I know each listbox item contains an index. I think if you could access the parent of the button you could then deterimine which button was clicked?
You could use the CommmandParameter of the button to hold a value -
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
<Button Click="MyBtn_Click" CommandParameter={Binding Title}/>
</StackPanel>
</DataTemplate>
public void MyBtn_Click(object sender, args)
{
string MyVal = (sender as Button).CommandParameter.ToString();
}
Convention is to use the Command event instead of the click event for this type of thing.

Categories