Setting focus on a ListBox item breaks keyboard navigation - wpf

After selecting ListBox item programmatically it is needed to press down\up key two times to move the selection. Any suggestions?
View:
<ListBox Name="lbActions" Canvas.Left="10" Canvas.Top="10"
Width="260" Height="180">
<ListBoxItem Name="Open" IsSelected="true" Content="Open"></ListBoxItem>
<ListBoxItem Name="Enter" Content="Enter"></ListBoxItem>
<ListBoxItem Name="Print" Content="Print"></ListBoxItem>
</ListBox>
Code:
public View()
{
lbActions.Focus();
lbActions.SelectedIndex = 0; //not helps
((ListBoxItem) lbActions.SelectedItem).Focus(); //not helps either
}

Don't set the focus to the ListBox... set the focus to the selected ListBoxItem. This will solve the "two keyboard strokes required" problem:
if (lbActions.SelectedItem != null)
((ListBoxItem)lbActions.SelectedItem).Focus();
else
lbActions.Focus();
If your ListBox contains something else than ListBoxItems, you can use lbActions.ItemContainerGenerator.ContainerFromIndex(lbActions.SelectedIndex) to get the automatically generated ListBoxItem.
If you want this to happen during window initialization, you need to put the code in the Loaded event rather than into the constructor. Example (XAML):
<Window ... Loaded="Window_Loaded">
...
</Window>
Code (based on the example in your question):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lbActions.Focus();
lbActions.SelectedIndex = 0;
((ListBoxItem)lbActions.SelectedItem).Focus();
}

You can do this easily in XAML too. Please note that this will set logical focus only.
For example:
<Grid FocusManager.FocusedElement="{Binding ElementName=itemlist, Path=SelectedItem}">
<ListBox x:Name="itemlist" SelectedIndex="1">
<ListBox.Items>
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
<ListBoxItem>Three</ListBoxItem>
<ListBoxItem>Four</ListBoxItem>
<ListBoxItem>Five</ListBoxItem>
<ListBoxItem>Six</ListBoxItem>
</ListBox.Items>
</ListBox>
</Grid>

Seems that there are two levels of Focus for ListBox control: ListBox itself and ListBoxItem. Like Heinzi said, directly set Focus for the ListBoxItem will avoid the case that you have to click twice on direction key in order to go through all ListBoxItems.
I found out this after several hours work, now it works perfect on my APP.

Related

How can I make unselectable TextBox with enable Scroll?

I want to make unselectable TextBox with enabled ScrollBar in WPF.
If I set IsReadOnly=True, it can scroll but selection of text is possible.
And if I set IsEnabled=True, it is unselectable but scrolling gets disabled.
IsHitTestVisible=True is also unselectable, disabled scroll.
How can I make unselectable, enabled scroll TextBox ?
'IsHitTestVisible' leads textbox to inactive and you can't modify the content. I Hope below code will meet your requirement
WPF:
<ScrollViewer Width="120" Height="50">
<TextBox x:Name="txBox" SelectionBrush="Transparent" ContextMenu="{x:Null}" TextWrapping="Wrap" Text="How can I make unselectable TextBox with enable Scroll?"/>
</ScrollViewer>
if you want to restrict copying text from textbox use below code additionally.
Code behind:
private void TxBox_KeyDown(object sender, KeyEventArgs e)
{
if (txBox.SelectedText.Length > 0)
{
txBox.SelectionLength = 0;
}
}
Good Day :)
You can set IsHitTestVisible=false and wrap the TextBox with a ScrollViewer
<ScrollViewer Width="100" Height="50">
<TextBox IsHitTestVisible="False" TextWrapping="Wrap">asjdla jksad lkjasd jd kla sljas kdj ksald jksad ksalj dlasj lkajs ljka sajksd</TextBox>
</ScrollViewer>

WPF - How to restyle ComboBox to remove the textbox/editbox and replace with static text

I want to restyle a WPF ComboBox which is formatted to be a drop-list type, BUT remove the selected TextBox which gets populated with the selected contents and just replace it with some static text and an image which remains constant, simulating a button like look.
So in effect it becomes a button-drop-list, so when I select an item from the drop list, I can populate another control via command bindings with its selected value and the button style remains.
Basically something like this crude picture I've hacked together.
I've seen examples of button with context menus, but I don't like the idea, and a ComboBox fits my needs perfectly in terms of function and easy command and data binding.
I know it can be done, but I lost faith in my ablity after reading overly confusing examples based on other controls. I couldn't find an example detailing my needs to learn from.
Cheers
DIGGIDY
After much playing around, I decided the better option was to go for a button with bound context menu, this worked out to be the better solution in the end.
Thanks for your help Marc.
I had got the same problem and actually, it's simple.
Just put a read-only ComboBox with a SelectionChanged event.
You put in index 0 your static text.
Now, when the user is selecting something, get the selected item and then, set the SelectedIndex to 0. So you got the item the user selected but the displayed text is the same.
See:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox combo = (ComboBox)sender;
if (combo.SelectedIndex > 0)
{
// Do your stuff here...
// Then
combo.SelectedIndex = 0;
}
}
[EDIT] According to me, I prefer my previous answer. So make sure you, reader, that my previous answer doesn't match your expectations. [/EDIT]
Another answer is to put your object above the ComboBox and then catch the MouseDown event from this object and dropped down the ComboBox. I used a read-only TextBox in my example.
See:
<Grid>
<ComboBox x:Name="Combo" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem Content="TEST" />
<ComboBoxItem Content="TEST1" />
<ComboBoxItem Content="TEST2" />
<ComboBoxItem Content="TEST3" />
</ComboBox>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" Text="TextBox" VerticalAlignment="Top" Width="120" IsReadOnly="True" PreviewMouseDown="TextBox_PreviewMouseDown"/>
</Grid>
And then the code behind:
private void TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true; // Prevents the event.
Combo.IsDropDownOpen = true; // Drops down the ComboBox.
}
It works fine for me.

WPF Expander, get the Header name out of a child element

I have a WPF Expander, such as this:
<Expander Canvas.Left="251" Canvas.Top="425" Header="expander1" Height="100" Name="expander1">
<Grid>
<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Option 1" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 2" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 3" Checked="chk_DoThis" />
</StackPanel>
</Grid>
</Expander>
When a checkbox is clicked, I fire off a 'Checked' event.
Is there some way to pull out a string that contains the 'Header' of the Expander? In this example, I want to pull out 'expander1' and assign that to a string.
I tried a few ways of doing this and couldn't get it to work. I have done this same concept using TreeViewItems and using a Header.Parent.ToString() to get what I wanted. No luck here. This is what I'm referring to:
string child = ((TreeViewItem)((TreeViewItem)((TreeView)sender).SelectedItem)).Header.ToString();
Does anyone know of a way I could do this for my Expander example. Googling and searching this site has yielded no return. It's probably something easy and I'm just overlooking it.
Thanks to anyone that has some ideas.
You can easily get the Expander from the CheckBox. Just iterate to the VisualTree and get the Top most parent's Expander. I just simply did only one parent in the below example.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Expander expander = (Expander)checkBox.Parent;
if (expander != null)
{
string str = expander.Header.ToString();
Debug.WriteLine(str);
}
}
I hope it will help you.

How to find the index location of dropped item into Silverlight ListBoxDragDropTarget

I have a silverlight ListBox that is contained in a ListBoxDragDropTarget. I am listening to the Drop event of the DDT, but I don't know how to find the index of the drop action. i.e. I want to know at which index spot the user dropped the items into my ListBox. On the UI when I'm dragging over the ListBox, I can see a line indicating the spot that I'm hovering over, but after dropping, I don't know how to get the drop location information from the drop event.
Given the following Xaml:
<Grid x:Name="ListBoxDragDropTarget"
Background="Gold"
AllowDrop="True"
Drop="ListBoxDragDropTarget_Drop">
<ListBox x:Name="MyListBox" Margin="50">
<ListBoxItem Content="Item 1" />
<ListBoxItem Content="Item 2" />
<ListBoxItem Content="Item 3" />
</ListBox>
</Grid>
If you want to know the ListBoxItem on which the user dropped the items you can use e.GetPosition to get the position of the mouse and VisualTreeHelper.FindElementsInHostCoordinates for hit testing:
private void ListBoxDragDropTarget_Drop(object sender, DragEventArgs e)
{
Point position = e.GetPosition(this.ListBoxDragDropTarget);
var hits = VisualTreeHelper.FindElementsInHostCoordinates(position, this.ListBoxDragDropTarget);
ListBoxItem dropElement = hits.FirstOrDefault(i => i is ListBoxItem) as ListBoxItem;
if (dropElement != null)
{
// Do something with the dropElement... or if you want the index use ItemContainerGenerator
int index = this.MyListBox.ItemContainerGenerator.IndexFromContainer(dropElement);
}
}

Setting CommandTarget to selected control in a TabControl

I have a WPF window with a few buttons and a tabcontrol having a tab for each 'document' the user is working on. The tabcontrol uses a DataTemplate to render the data in ItemSource of the tabcontrol.
The question: If one of the buttons is clicked, the command should be executed on the control rendering the document in the active tab, but I've no idea what I should set CommandTarget to. I tried {Binding ElementName=nameOfControlInDataTemplate} but that obviously doesn't work.
I tried to make my problem a bit more abstract with the following code (no ItemSource and Document objects, but the idea is still the same).
<Button Command="ApplicationCommands.Save" CommandTarget="{Binding ElementName=nestedControl}">Save</Button>
<TabControl x:Name="tabControl">
<TabControl.Items>
<TabItem Header="Header1">Item 1</TabItem>
<TabItem Header="Header2">Item 2</TabItem>
<TabItem Header="Header3">Item 3</TabItem>
</TabControl.Items>
<TabControl.ContentTemplate>
<DataTemplate>
<CommandTest:NestedControl Name="nestedControl"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I tested the code by replacing the complete tabcontrol with only one single NestedControl, and then the command button just works.
To be complete, here is the code of NestedControl:
<UserControl x:Class="CommandTest.NestedControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Label x:Name="label" Content="Not saved"/>
</Grid>
</UserControl>
And code behind:
public partial class NestedControl : UserControl {
public NestedControl() {
CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, CommandBinding_Executed));
InitializeComponent();
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) {
label.Content = "Saved";
}
}
I don't know exactly how CommandTarget works, but binding to the active tab in a TabControl is done with something like this:
"{Binding ElementName=tabControl,Path=SelectedItem}"
(SelectedItem is the current active tab)
EDIT:
More information about CommandTarget can be found here: Setting Command Target in XAML
EDIT 2:
Deleted my initial answer since it was not an answer to the question.

Resources