Selecting null item in a ListBox - c#

I have a ListBox with an ItemsSource that can contain null values.
I can't select those null values in the ListBox with the mouse, but I can with the keyboard.
Is there any way to make null items selectable by mouse?
Example xaml:
<ListBox>
<ListBox.Items>
<x:Null />
<system:String>Hello</system:String>
<x:Null />
</ListBox.Items>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text">
<Setter.Value>
<Binding />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=.}" Value="{x:Null}">
<Setter Property="Text" Value="Null value!" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Which results in:
however, I can't select any of the Null value! entries using the mouse, which is what I want to do.
If it can be done with another pure-xaml solution, that'd be fine. I'd prefer to not have to use any converters, if possible.

I'm afraid you will have to go for converters (don't really know why you don't want some ?)
From this SO question :
The null "item" is not being selected by the keyboard at all - rather
the previous item is being unselected and no subsequent item is (able
to be) selected.
In short, you can neither select nor deselect a null item in a
ComboBox. When you think you are doing so, you are rather deselecting
or selecting the previous or a new item.
This can perhaps best be seen by adding a background to the items in
the ComboBox. You will notice the colored background in the ComboBox
when you select "Hello", but when you deselect it via the keyboard,
the background color disappears. We know this is not the null item,
because the null item actually has the background color when we drop
the list down via the mouse!
The following XAML, modified from that in the original question, will
put a LightBlue background behind the items so you can see this
behavior.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox x:Name="bars" Height="21" SelectedItem="{Binding Bar}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Background="LightBlue" Width="200" Height="20">
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel> </Window> ```
If you want further validation, you can handle the SelectionChanged event on the ComboBox and see that
"selecting the null item" actually gives an empty array of AddedItems
in its SelectionChangedEventArgs, and "deselecting the null item by
selecting 'Hello' with the mouse" gives an empty array of
RemovedItems.

Related

Hide checkbox, but show its content

Is it possible to hide a checkbox, but leave its content visible?
<ListBox
ItemsSource ="{Binding MyItemCollection}"
SelectionMode="Single"
Width="300"
Height="320">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}">
<CheckBox.Content>
<TextBlock Text="{Binding Item.Code}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<CheckBox Content="Edit Mode"
IsChecked="{Binding Path=EditModeSelected, Mode=TwoWay}">
</CheckBox>
</StackPanel>
I would like to hide the checkboxes in the list box when I turn Edit Mode off (so it should be binded to EditModeSelected), but the text should be left visible.
In order to do so You can keep two TextBlocks. In edit mode visible CheckBox and hide TextBlock and in reader mode vice versa. I hope this may help. As DataTemplate can have only one child here's the fix
Create a Window Resource like below. Two Data Templates were created one for edit mode and another for Reader Mode.
<Window.Resources>
<DataTemplate x:Key="EditModeTemplate">
<CheckBox IsChecked="{Binding IsChecked}">
<CheckBox.Content>
<TextBlock Text="{Binding Item.Code}"/>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="ReaderModeTemplate">
<TextBlock Text="{Binding Item.Code}"/>
</DataTemplate>
</Window.Resources>
Now in .cs file assign the Date Template as per requirements.
if (EditMode)
{
DemoCollection.ItemTemplate = this.Resources["EditModeTemplate"] as DataTemplate;
}
else
{
DemoCollection.ItemTemplate = this.Resources["ReaderModeTemplate"] as DataTemplate;
}
3 possible solutions come in my mind - two of them more or less "hacks" and one more or less clean solution:
A checkbox and textblock for every item - you can get problems with margins etc
A checkbox with no content (which is only visible when in edit mode), and a textblock which is always visible
Take the default controltemplate for checkbox (Default ControlTemplate for CheckBox) and bind the visibility of the checkbox
Here is a Xaml only solution pulled from a project I am working on. In this case "ShowCheck" is a field in the current binding context saying whether or not to show the check.
<CheckBox Content="{Binding Name}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowCheck}" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<ContentControl Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Basically if the checkbox should be invisible, then I use a style and a trigger to change the checkbox's template to something without the checkbox. My implementation the content is just a string, so this works. If you were putting more complicated objects into the checkbox, you might need to shuttle the ContentTemplate, ContentTemplateSelector, and related fields into the ContentControl that is used to replace the checkbox

ListView.SelectedItems does not change when clicking a TextBox in the ItemTemplate

I have a ListView bound with an ObservableCollection. The Itemtemplate of the ListView is the following:
<ListView ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" PreviewMouseLeftButtonUp="ListViewClicked" PreviewKeyDown="NameBox_KeyDown">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="./Resources/DeleteIcon.png"/>
<TextBox Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way I give the user the choice to either change the name of the element, or delete it from the list. I handle this two operations in code behind through the two event handlers attached to the ListView.
The problem is that when I click on the TextBox, the SelectedItems property of the ListView doesn't change and it points the last selected item. When I click on the image or on the free space around the selection change.
Now I have two questions:
Is there an easy way to fix this behavior?
Is it possible to get a reference to the collection item whose property is exposed by the TextBox?
One way to deal with this problem is to set Trigger to set IsSelected when keyboard focus is within ListViewItem
<ListView ... SelectionMode="Single">
<!-- .... -->
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>

Window with ListBox is not displaying error on the ListBox

I have a dialog in my WPF application which contains a ListBox. The ListBox uses the following DataTemplate to display its contents:
<DataTemplate x:Key="AlarmClassTemplate">
<CheckBox Content="{Binding Path=Value}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
I've also configured the following template and style to display when there is an error in the ListBox's contents:
<ControlTemplate x:Key="InputErrorTemplateA">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Right"
Height="30"
Margin="5"
Source="{StaticResource ErrorImage}"
ToolTip="Contains invalid data"
VerticalAlignment="Center"
Width="30" />
<Border BorderBrush="Red"
BorderThickness="5"
Margin="5">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplateA}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
And here's the XAML for the ListBox itself:
<ListBox FontSize="20"
FontWeight="Bold"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="1"
Height="158"
ItemsSource="{Binding Path=IDs, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
ItemTemplate="{StaticResource AlarmClassTemplate}"
Margin="5,0,110,0"
Name="AlarmClassListBox"
ToolTip="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"
Visibility="{Binding Converter={StaticResource BoolToVisibility}, Path=DataTypeIsAlarms}" />
The validation logic for the data in the ListBox is that at least one item has to be checked off. If none of them are, the ListBox should display an error and the OK button on the dialog should be disabled.
The good news is that the OK button on the dialog is indeed disabled when nothing in the ListBox is checked. The bad news is that the Style doesn't seem to be working, in that no red border is displayed around the ListBox and the error image (a red circle with a white exclamation point inside) does not show.
I'm using the same exact ControlTempate and a similar Style on other controls on the same dialog and they work fine. What am I doing wrong? Is it the ListBox? Does ListBox validation work differently?
Indeed the problem is you weren't raising PropertyChanged event for your validation to gets fired.
But i can see one more issue in your code. You have set local value for tooltip on ListBox here:
ToolTip="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"
But you want different tooltip in case validation returns some error which you define in style triggers.
But, local value has higher precedence order than style triggers. So, your tooltip will never be set. So, you should move the tooltip to style setters to work:
<Setter Property="ToolTip"
Value="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"/>
MSDN link - Dependency property value precedence.
I found the answer to my problem in this post. It turns out that I have to raise the PropertyChanged event when the checkboxes change in order for the Validation logic to fire. Since the items in the ListBox implement INotifyPropertyChanged, it was easy to add an event listener for each item as it's added to the ListBox which raises the necessary event.
Thanks anyway.

wpf combobox with custom itemtemplate text

I have ComboBox with custom ItemTemplate.
<ComboBox Height="20" Width="200"
SelectedItem="{Binding Path=SelectedDesign}"
ItemsSource="{Binding Path=Designs}" HorizontalAlignment="Left"
ScrollViewer.CanContentScroll="False">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type formdesign:FormDesignContainer}">
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding Path=ImageThumb}" Stretch="Uniform" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This works well. However WPF tries to draw rectangle as Combobox Text. How can I set "text" for this template. By "text" I mean string or control which represent selected item and write into combobox when item is selected
In other words I'd like to do this:
But now I got this
Try setting SelectionBoxItemTemplate with a TextBlock.
Appears that SelectionBoxItemTemplate is read-only. So another approach is to override ItemContainerStyle.Template. Example
I found this solution by Ray Burns a good approach. You can define two DataTemplate one for Items in the drop down list and the other for the selected item which should be shown in the Combobox. The using a trigger and checking the visual tree it decide which one to use.
<Window.Resources>
<DataTemplate x:Key="NormalItemTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
...
<ComboBox
ItemTemplate="{StaticResource CombinedTemplate}"
ItemsSource="..."/>
Add Textblock to the datatemplate and bind it
or add Contentpersenter on the rectangle
Edit:
it seems like i didn't got what you were tring to accomplish ,

Set focus in WPF Checkbox list

I have a WPF checkbox list containing car models. When I tab into the list the follwoing happens:
1) On first tab key press, the list item is selected
2) On second tab key press, the actual checkbox is selected allowing me to set the IsChecked state using the spacebar key.
Is it possible to automatically move the cursor to the checkbox when the list item is selected? (trying to avoid hitting tab twice)
XAML:
<ListBox ItemsSource="{Binding Cars}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Make}" IsChecked="{Binding IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Try adding this to your ListBox:
<ListBox.ItemContainerStyle>
<Style TargetType="Control">
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.ItemContainerStyle>
Tried Dan's solution, it works but it moves the focus out of the ListBox control after selecting the first checkbox, I was looking for something which prevents the focus from being set to the selected listbox item while still allowing to tab through all the checkboxes in the list.
The following link helped me - Keyboard focus to list box items in WPF
I've made a few changes to have create border as well as the verticall scrollbar, complete XAML below:
<Border BorderThickness="0.8" BorderBrush="Gray">
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="1">
<ItemsControl ItemsSource="{Binding Cars}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Make}" IsChecked="{Binding IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>

Categories

Resources