ListBox keep selection after losing focus - c#

I have a ListBox that when in focus, and when I have an item selected returns a valid SelectedIndex. If I have a valid SelectedIndex and I click on a TextBox on the same Forum, the SelectedIndex now becomes -1. However I want it to keep its SelectedIndex from changing. How would I go about doing this?

ListBox will keep it's SelectedIndex regardless of focus.
I tested it on a blank project with one ListBox, one TextBox and one Label used to display the ListBox's SelectedIndex. Under both the ListBox's SelectedIndexChanged and the TextBox's TextChanged events I updated the Label with the ListBox's SelectedIndex
There must be something else going on to cause the Selected Index to change to -1.

I had the same issue as original poster.
I couldn't figure it out totally but it seems like when you have the listbox bound to an observable collection and the collection gets changed that the selected item loses the focus.
I hacked around the issue by saving the selected index in a variable and resetting it if the selected index was -1 (and it was valid to restore it)

This is an old question, but in case someone else experiences the same problem check your ListBoxItem style especially if you are using one of styles from WPF Themes.
The problem with WPF Themes specifically is the inclusion of the
section outside of the Control Template:
<Style d:IsControlPart="True" TargetType="{x:Type ListBoxItem}">
....
<Style.Triggers>
<Trigger Property="Selector.IsSelected" Value="True">
<Setter Property="Foreground">
<Setter.Value>
<SolidColorBrush po:Freeze="True" Color="{DynamicResource BlackColor}" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="true" />
</Trigger>
</Style.Triggers>
</Style>
Delete the Style.Triggers and the problem should go away

Handle the SelectedIndexChanged event and save the selected value so that you can restore it when your control regains focus.

I haven't verified this in my apps but if the SelectedIndex property changes when the LB loses focus you probably have to handle that case yourself by caching the last selected index and resetting it when the control regains focus. You can do this in the containing form or you can do it in a class derived from ListBox.
You could even try setting the selected index as soon as you see it becomes -1. Not sure what would happen but I'd be curious to find out....
Edit: just tested it and like the other poster I can't reproduce it either. Must be something slightly different about your LB

Are these controls in different dialogs, or maybe different tabs on a tabbed container? That's the only way I can think of that you would lose your SelectedIndex when changing focus. Otherwise, how would anybody e.g. click a button to take action on an item? You'd lose the selection when focus went to the button you're clicking...

Related

WPF Treeview is not Collapsing Rows Even with Visibility Collapsed [duplicate]

I've been trying to just hide items from a TreeView. I'm using a custom data type as source (called SettingsMenuItem) which inherits from FrameworkElement (currently FrameworkContentElement, because otherwise the TreeView renders them wrong).
My goal is by setting the VisibilityProperty of these FrameworkElements to either Collapsed or Visible that I'm able to hide certain items (including their children). I know that this can be done by deleting items from the source collection. But that's not what I want. It would mean that I have to mirror each collection in order to keep track of it's actual items, bind to each one in order to be notified about Visibility-changes and create a new collection each time one changes. A lot of overhead for this.
Right now I have no clue how I could accomplish that. I figure it's related to the ItemsGenerator, but I haven't seen any possibility to override it's behaviour. I thought TreeView would be able to detect Visibility, but obviously it doesn't. As alternative I thought of a custom TreeViewItem (maybe even TreeView if necessary) - but at this point the abstraction of this whole system overwhelms me. I don't know where to start and what is actually necessary to solve the problem.
Tips what I have to change or implement by myself would be more than enough. A complete solution would be nice.
You can do this using a data trigger bound to a property (e.g. "IsVisible") in you tree data nodes:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
While this technically answers your question I would be wary of actually doing it. User3690202's comment is correct, it's the sort of thing you would normally do via filtering in your view model.
For alternate solution using code behind xaml.cs:
To Remove a specific TreeViewItem from a TreeView which is created from a code behind.
TreeViewItem treeViewItem1 = new TreeViewItem
{
Visibility = Visibility.Collapsed,
};
use the code with TreeViewItem you want to hide in a if condition to hide specific TreeViewItem Header let say "Cars" and you want to hide it and use the code with if condition to hide "Cars" TreeViewItem.

Combobox doesn't get focus

I have a non-editable combobox (SupplierDropdown), and I would like when the user chooses the last value on the list, the combobox should become editable, with no value and automatically focused ready for the user to type their value. In other words, an editable, blank box with a blinking cursor ready for input.
Here's the code I have:
private void SupplierDropdown_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (SupplierDropdown.SelectedIndex == SupplierDropdown.Items.Count - 1)
{
SupplierDropdown.IsEditable = true;
SupplierDropdown.Text = "";
SupplierDropdown.Focus();
}
}
However, although the combobox indeed becomes editable, the rest of the code doesn't seem to work: (a) the combobox doesn't clear the value the user chose, and (b) the combobox doesn't get the focus, the user needs to hit tab once to see the blinking cursor in the combobox.
How can I make this work?
This seems like a major pain, because the control just is not intended to be used that way. Even if you get the initial logic to work, it is difficult to turn the editable flag back off in a sound way. When the user enters text that partially matches a given option it will select said option. How do you know whether that selection was caused by accident or intentionally and thus whether the editable flag should be set to false again?
Would recommend the additional TextBox scheme, where you just show a TextBox for any custom values, can be done in XAML only (if you know what value to trigger on) but that's not necessary.
<ComboBox Name="cb" SelectedValuePath="Content">
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>Other</ComboBoxItem>
</ComboBox>
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue, ElementName=cb}" Value="Other">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
(PS: This is an example of using the item/value instead of the index, even if someone decides that the "other" option should be at the top instead, the code will still work as intended, yours would not.)

How to correctly use a button in a listbox itemtemplate / datatemplate?

I have a Silverlight application that displays a list of items in a ListBox. Each item represents a different 'page' of my application so I have a style that is applied to the ItemContainerStyle property that looks like this:
<Style x:Key="navigationItemContainerStyle" TargetType="ListBoxItem">
<Setter Property="Margin" Value="5,3"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Cursor="Hand">
<VisualStateManager.VisualStateGroups>
<!-- code omitted --!>
</VisualStateManager.VisualStateGroups>
<Border x:Name="contentBorder"
Background="{StaticResource navigationHighlightBrush}"
CornerRadius="3"
Opacity="0"/>
<ContentControl x:Name="content"
Margin="10,5"
Content="{Binding}"
Foreground="DarkGray"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style is very simple. It simply displays the Border when the ListBoxItem's visual state is equal to 'Selected'. Notice that the content is hosted by a ContentControl as I want to be able to change the Foreground property when the item is in the 'Selected' state.
This works brilliantly as can be seen by the screenshot below:
Now I want the selected item to invoke navigation so the idea I had was to create a DataTemplate that sets the content of each item to a HyperLinkButton:
<DataTemplate x:Key="navigationListBoxItemTemplate">
<HyperlinkButton Content="{Binding}"
Background="Transparent"/>
</DataTemplate>
Now this doesn't work as the ItemTemplate hosts it's content in a ContentControl rather than a ContentPresenter so I have had to update the ListBoxItem template to use a ContentPresenter instead.
<ContentPresenter x:Name="content" Margin="10,5"/>
I now get the following result:
When I click on the HyperLinkButton the ListBoxItem is now no longer selected (I can click just outside the HyperLinkButton and the ListBoxItem becomes selected). What I really want is for the ListBoxItem to become selected when the HyperLinkButton is clicked. The HyperLinkButton has no concept of selection so I cannot bind to the ListBoxItem's IsSelected property.
Note: The actual navigation works perfectly, the problem is purely with the appearance of the ListBoxItems.
So my questions are:
Can I make it so that when the HyperLinkButton is clicked, the ListBoxItem becomes selected like the first image?
I will also need some way of changing the foreground of the HyperLinkButton when it is selected as this is no longer done in the items template due to me swapping the ContentControl fro a ContentPresenter.
Note: I could probably solve this problem by binding the SelectedItem property of the ListBox to my viewModel and handling the navigation there negating the requirement to have a HyperLinkButton hosted by each ListBoxItem but I am interested in knowing if it is possible using styles and templates to achieve my desired result.
Update
I have tried a couple of things to try and resolve this but so far have been unsuccessful. The first thing I tried was applying a new style to the HyperLinkButton control in my DataTemplate which essentially removes all of the default look and feel from the control but my application still behaves in the way described above.
The second thing I tried was setting the IsHitTestVisible property to false. This allows me to click 'through' the HyperLinkbutton and select the ListBoxItem but this means that the navigation is now no longer invoked.
The ListBoxItem is not selected because the Button (whatever type it is) marks the MouseLeftButtonDown event as Handled. Therefore the required event does not bubble up to the parent ListBoxItem.
Evidently, your ListBoxItem is getting focus from the click (I assume that is the border in your final image), so this must happen regardless.
Under the covers, the ListBoxItem will have a standard LeftMouseButtonDown event handler set up to deal with Selection, and it must have a call to AddHandler to deal with setting focus.
You can achieve something similar by adding your own handler for the event, like so:
listboxitem.AddHandler(UIElement.MouseLeftButtonDownEvent, new System.Windows.Input.MouseButtonEventHandler(MyMouseLeftButtonDownEventHandler), true);
The final parameter instructs the handler to handle handled events...
Attaching this handler I leave to you, but using a behavior is probably the most straight forward. You could event derive a type from Button and have it walk up the Visual Tree to find and select the ListBoxItem... The possibilities are endless.

WPF ListView programmatically deselect item

I have used the following approach to bind IsSelected of my items to a Property: WPF ListView Programmatically Select Item
<ListView.ItemContainerStyle>
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</ListView.ItemContainerStyle>
Now I am able to select my items in code behind by simple setting the IsSelected property to true. However I am not able to deselect items by setting the IsSelected property of my items to false.
Setting the items property IsSelected to true will trigger the ListViewSelectionChanged event. However setting the property IsSelected of an already selected item to false does not trigger the event. The property will be changed to false but the item remains selected within the ListView. I have also tried using Mode=TwoWay without any success.
I would appreciate any sort of help!
Thank you very much in advance,
Thomas
For OP or others looking to "programmatically" deselect a ListView.
If your ListView rigged up as Single, Extended or Multiple you can always just:
YourlistView.Selecteditem = null;
Or you can use this as well:
YourlistView.UnselectAll();
Looks like you are just missing the TargetType for the style. Add the target type of ListViewItem as per Kent's original code below.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsGroovy}"/>
</Style>
</ListView.ItemContainerStyle>

WPF DataGrid RowDataBound?

Ok this is driving me mad, I feel like a total Newbie.
I'm using WPF's DataGrid control from WPF Toolkit with .NET 3.5.
Link on Codeplex here
I want an equivalent to the classic GridView's RowDataBound event, and I can't find any. I tried working with LoadingRow, but it fires every time I scroll.
I'm trying to change the background color of certain cells in my grid based on a database values.
I'm new to WPF. Should I be using the XAML binding?
The apt way of doing that in WPF is through Datatrigger
<DataTrigger Binding="{Binding Path=State}" Value="WA">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
that comprehends to
UPDATE DataGrid
SET Foreground = 'Red'
WHERE State = 'WA';
I ended up disabling row virtualization on the DataGrid using EnableRowVirtualization="False". That way, the LoadingRow event would fire only once for all items.

Categories