WPF ListBoxItem selection problem - wpf

I have a listbox where the items contain checkboxes:
<ListBox Style="{StaticResource CheckBoxListStyle}" Name="EditListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Click="Checkbox_Click" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="{Binding Path=DisplayText}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem I'm having is that when I click on the checkbox or its content, the parent ListBoxItem does not get selected. If I click on the white space next to the checkbox, the ListBoxItem does get selected.
The behavior that I'm trying to get is to be able to select one or many items in the list and use the spacebar to toggle the checkboxes on and off.
Some more info:
private void Checkbox_Click(object sender, RoutedEventArgs e)
{
CheckBox chkBox = e.OriginalSource as CheckBox;
}
In the code above when I click on a checkbox, e.Handled is false and chkBox.Parent is null.
Kent's answer put me down the right path, here's what I ended up with:
<ListBox Style="{StaticResource CheckBoxListStyle}" Name="EditListBox" PreviewKeyDown="ListBox_PreviewKeyDown">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" />
<TextBlock Text="{Binding DisplayText}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I had to use PreviewKeyDown because by default when you hit the spacebar in a list box, it deselects everything except for the most recently selected item.

To begin with, put the content outside the CheckBox:
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"/>
<TextBlock Text="{Binding DisplayText}"/>
</StackPanel>
After that, you will need to ensure that pressing space on a ListBoxItem results in the CheckBox being checked. There are a number of ways of doing this, including a simple event handler on the ListBoxItem. Or you could specify a handler for UIElement.KeyUp or whatever in your DataTemplate:
<CheckBox IsChecked="{Binding IsChecked}" UIElement.KeyUp="..."/>

You can also bind the IsChecked property of the CheckBox and IsSelected property of the ListBoxItem:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding DisplayText}" IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

In your use case it would be way simpler to use a ItemsControl instead of a list box. A ItemsControl is similar to a Listbox except that it doesn't contain the automatic selection behaviour. Which means that using it to host a list of what are essentially checkboxes is very simple and you don't have to workaround the ListBox's selection behaviour.
Simply switching to ItemsControl will give you exactly what you need:
<ItemsControl Style="{StaticResource CheckBoxListStyle}" Name="EditListBox">
<ItemsControl .ItemTemplate>
<DataTemplate>
<CheckBox Click="Checkbox_Click" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="{Binding Path=DisplayText}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can click on text to check checkboxes (default behavior) and you can use the keyboard too without having to wire up any event handlers.

Related

Single-click-editable bound CheckBox in Xceed DataGridControl

I've spent the past hour searching for solutions to what should be a simple problem: how to create a single-click-editable bound CheckBox in Xceed's community DataGridControl.
To be clear: I want a CheckBox column where the user may click any CheckBox, regardless of what row is selected, and have the view model's IsSelected property changing accordingly.
Below is the latest permutation I've tried. This code reads the value from the model, but clicking the CheckBox does not call the IsSelected setter for some reason.
<xcdg:DataGridControl x:Name="DictionariesDataGridControl" ItemsSource="{Binding Mode=OneWay, Source={StaticResource DictionariesViewSource}}" AutoCreateColumns="False" AutoRemoveColumnsAndDetailConfigurations="False" SelectionMode="Extended" NavigationBehavior="RowOnly">
<xcdg:DataGridControl.View>
<xcdg:TableView UseDefaultHeadersFooters="False" ShowRowSelectorPane="False" VerticalGridLineThickness="0">
<xcdg:TableView.FixedHeaders>
<DataTemplate>
<xcdg:ColumnManagerRow BorderThickness="0"/>
</DataTemplate>
</xcdg:TableView.FixedHeaders>
</xcdg:TableView>
</xcdg:DataGridControl.View>
<xcdg:DataGridControl.Columns>
<xcdg:Column FieldName="IsSelected" MinWidth="20" MaxWidth="20" CellEditorDisplayConditions="RowIsCurrent">
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding ., Mode=OneWay}" IsHitTestVisible="False"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding ., Mode=TwoWay}"/>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
</xcdg:DataGridControl.Columns>
EDIT 1
I'm trying this, which does exactly what I need:
<xcdg:Column FieldName="IsSelected" MinWidth="20" MaxWidth="20" CellEditorDisplayConditions="Always"/>
except that, for some reason, the CheckBox is styled with a blue background!
I've selected the element in the visual tree that has a Background property defined as SolidColorBrush with #FF0000FF as the color:
EDIT 2
I decompiled the DataGridCheckBox class that Xceed uses to render the CheckBox and found this override:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.ChildCheckBox.Background = (Brush) new SolidColorBrush(Colors.Blue);
}
What a bizarre decision by Xceed to arbitrarily set the background color to blue.
EDIT 3
Using #JBrooks' answer, I tried the following:
<xcdg:Column FieldName="IsSelected" MinWidth="20" MaxWidth="20" CellEditorDisplayConditions="Always">
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding ., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
Unfortunately, for some reason the setter on the IsSelected property is never called when I check the box. The getter is called several times, though, and the CheckBoxes are appearing correctly on initial bind.
You have both a CellContentTemplate and a CellEditor, so the first click does the "go into edit mode" logic. Just have a single one like below. This is for a normal WPF DataGrid, but maybe you can try something similar for your grid.
<DataGridTemplateColumn Header="Active" SortMemberPath="IsActive" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding IsEnabled}" Style="{StaticResource rightsCB}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
For this DataGrid I also have these properties set:
SelectedItem="{Binding SelectedUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionUnit="FullRow" SelectionMode="Single"
So this DataGrid does act like what you want - I click the checkbox in row 4 and the IsChecked changes and it also makes row 4 the current row which sets the SelectedUser to the user bound to row 4.

stop Text block (not Text box) from tabbing sequence

How to exclude TextBlock from tabbing sequence in SILVERLIGHT Grid XAML. I know for TextBox, we use IsTabStop false OR TabIndex -1, but same property is not avaiable for TextBlock
I have 4 controls, 1 and 4 are TextBox (editable) and 2 and 3 are TextBlock (non editable). When I tab, all the 4 are included in the tabbing sequence.
I want to exclude 2,3 (Textblocks) from tabbing. Means, If I tab from TextBox 1, focus should move directly to TextBox 4. please help.
Loaded="UserControl_Loaded">
<DataTemplate x:Key="CellEditClientAllocations" >
<TextBox Text="{Binding ClientAllocations, Mode=TwoWay}"
Style="{StaticResource GridCellTextBoxStyle}"
x:Name="tbxClientAllocations"
Loaded="TextBox_Loaded"
TextChanged="tbxClientAllocations_TextChanged"
KeyDown="tbxClientAllocations_KeyDown"
LostFocus="tbxClientAllocations_LostFocus"
GotFocus="tbxClientAllocations_GotFocus"/>
</DataTemplate>
<DataTemplate x:Key="CellAccountId">
<TextBlock Text="{Binding AccountId, Converter={StaticResource anc}}" Style="{StaticResource GridCellTextBlockStyle}" /> </DataTemplate>
<DataTemplate x:Key="CellEditAccountId">
<TextBox Text="{Binding AccountId, Converter={StaticResource anc}, Mode=TwoWay}" x:Name="tbxAccountId" LostFocus="TbxAccountIdLostFocus" TextChanged="TbxAccountIdTextChanged" GotFocus="tbxAccountId_GotFocus"/>
</DataTemplate><DataTemplate x:Key="CellAccountName"> <StackPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding AccountName, Mode=TwoWay}" Foreground="{Binding IsAccountValid, Converter={StaticResource cc}}" kStyle="{StaticResource GridCellTextBlockStyle}" Name="Account" MouseRightButtonUp="" > </TextBlock> </StackPanel> </DataTemplate>
<DataTemplate x:Key="CellLotInstructions"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LotInstructions}" Style="{StaticResource GridCellTextBlockStyle}"/>
<HyperlinkButton Content="Edit" Style="{StaticResource HyperlinkButtonStyleUnderline}" IsEnabled="{Binding LotInstructionsEnabled}" Name="Lotinstructons" HorizontalContentAlignment="Center" MouseLeftButtonDown="LotinstructonsMouseLeftButtonDown" VerticalContentAlignment="Center" Click="ViewSpecifyLots_Click" Visibility="{Binding LotInstructionsEdit}" /> </StackPanel> </DataTemplate>
Try setting attached property KeyboardNavigation.TabNavigation to None.
<TextBlock KeyboardNavigation.TabNavigation="None"/>
Set Focusable="False" for the textblock
I think you may need to work on the DataGrid Column rather than the Cell content (your TextBlock) it is the Cell that is Focussed not the TextBlock.
You could assign an event handler to the CellEnter Event (accessible from the definition of the original column) and then set the DataGrids selected cell selected property to false. Not the neatest solution but it should work.
Alternatively you could create a behaviour to do this....
Hope this helps!

Handling click event inside LongListSelector

I have the following XAML:
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" >
<TextBlock Text="{Binding Path=Name, Mode=TwoWay}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" />
<TextBlock Text="{Binding Path=State.Summary, Mode=TwoWay}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<toolkit:ToggleSwitch x:Name="stateToggle" x:Uid="{Binding Path=Id, Mode=TwoWay}" IsChecked="{Binding Path=State.Current, Mode=TwoWay}" Click="stateToggle_Click_1" ></toolkit:ToggleSwitch>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
I am trying to achieve the following:
1) Take an action on toggle switch click (handle an event)
2) Take an action when an item from the list is tapped i.e. outside the borders of the toggle switch.
I have tried SelectionChanged and ToggleSwitch Click but that way I still invoke both events when the toggle switch is clicked.
Any ideas?
If SelectionChanged works for use case 2, then you are basically there.
You should set the MouseEventArgs Handled property to true in your ToggleSwitch Click handler. That way the selection should not be changed (because the listitem has not been clicked) and only the click event you want should be raised.

Multiselect ComboBox, Force it to always display the same text

I have a multiselect combobox that works fine. Except for the text. I want it to always have the same text ("Commodity Filter") regardless of what the user has selected.
If I set iseditable to true and the text to CommodityFilter it looks fine until the user makes a selection, then it is garbage (displays object type name). How can I hard code some text there? (Actually ideally i would databind it so it can change depending on whether anything is selected, but that would be a bonus at this point)
<ComboBox IsEditable="True" Text ="Commodity Filter" ItemsSource="{Binding Path=ActiveCommodities}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding Commodity}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I ended up creating a custom object for populating the ComboBox (which had the IsSelected property and implemented INotifyPropertyChanged) because I was creating several comboboxes to control filtering. Once I did this is was trivial to override the tostring on the customobject and pass in the appropriate text. So the xaml did not change much.
I would have preferred to overlay with a text box but that seemed to be beyond my abilities to get a polished look in a reasonable time.
<ComboBox ItemsSource="{Binding Path=ActiveFuturesMonths}"
IsEditable="True"
IsReadOnly="True"
Text="Futures Month Filter" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding Text}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Actually the crux is in setting -
IsEditable="True"
IsReadOnly="True"
Text="Futures Month Filter"
rather than creating custom object. thanks a lot it helped.

Strange behavior with CheckBox in DataGridTemplateColumn

I have a SL4 DataGrid with a column containing a checkbox:
<data:DataGridTemplateColumn Header="Check" CanUserSort="False" >
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="CheckRead" Click="CheckRead_Click" IsChecked="{Binding Acknowledged, Mode=TwoWay}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Everything works fine as long as the user clicks directly on the checkbox. If they click anywhere else in the cell, the cell gets selected. But on subsequent clicks on the checkbox - the checkbox changes state but that change is not reflected in the bound object.
If the user clicks on any other cell and then goes back to click the checkbox, it works again.
Why does having the cell selected disable the binding of the checkbox????
I tried the solution proposed of setting horizontal/vertical alignment to stretch but that does not change anything.
Okay, the answer is...
<data:DataGridTemplateColumn Header="Check" CanUserSort="False" >
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Center">
<CheckBox x:Name="CheckRead" Click="CheckRead_Click" IsChecked="{Binding Acknowledged, Mode=TwoWay}" />
</ContentControl>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Wrapping the checkbox in a contentcontrol like this causes the problem to go away!

Resources