Set the focus on a textbox in xaml wpf - wpf

Despite some posts on this forum and others i cannot find something that tells me how to set the focus on a TextBox.
I have a userControl with many labels and textBoxes. When the form is loaded I want the a particular textBox to have the focus.
I have set the tabIndex but that didn't seem to work.
Any suggestions?

You can use the FocusManager.FocusedElement attached property for this purpose. Here's a piece of code that set the focus to TxtB by default.
<StackPanel Orientation="Vertical" FocusManager.FocusedElement="{Binding ElementName=TxtB}">
<TextBox x:Name="TxtA" Text="A" />
<TextBox x:Name="TxtB" Text="B" />
</StackPanel>
You can also use TxtB.Focus() in your code-behind if you don't want to do this in XAML.

You can apply this property directly on the TextBox :
<TextBox Text="{Binding MyText}" FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>

I am new to using WPF and reading through the above examples I had a similar experience trying set the focus to a textbox using the xaml code examples given, i.e. all the examples above didn't work.
What I found was I had to place the FocusManager.FocusElement in the page element. I assume this would probably work as well if you used a Window as the parent element. Anyway, here is the code that worked for me.
<Page x:Class="NameOfYourClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="Title"
Height="720"
Width="915"
Background="white"
Loaded="pgLoaded"
FocusManager.FocusedElement="{Binding ElementName=NameOfYourTextBox}">
<!-- Create child elements here. -->
</Page>

I have a TextBox inside a Grid inside a DataTemplate which I want to have keyboard focus when it becomes visible. I also found that
<DataTemplate x:Key="DistanceView" DataType="{x:Type vm:ROI}">
<Grid FocusManager.FocusedElement="{Binding ElementName=tbDistance}">
<TextBox x:Name="tbDistance" Grid.Column="1" Grid.Row="1" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
did not work for me.
However when I call Focus() in the parent ContentControl
private void ContentControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((sender as ContentControl).IsVisible)
{
(sender as ContentControl).Focus();
}
}
it starts to work and the caret is visible in the TextBox. I think the FocusScope has to be given focus for the FocusManager.FocusedElement property to have any effect.
Jerry

From experimenting around, the xaml solution
FocusManager.FocusedElement="{Binding ElementName=yourElement}"
seems to work best when you place it in the highest element in the window hierarchy (usually Window, or the Grid you place everything else in)

Usage:
local:FocusManager.FocusOnLoad="True"
public class FocusManager
{
public static readonly DependencyProperty FocusOnLoad = DependencyProperty.RegisterAttached(
"FocusOnLoad",
typeof(bool),
typeof(FocusManager),
new UIPropertyMetadata(false, new PropertyChangedCallback(OnValueChanged))
);
private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (!(sender is Control control))
return;
if ((bool) e.NewValue == false)
return;
control.Loaded += (s, e) => control.Focus();
}
public static bool GetFocusOnLoad(DependencyObject d) => (bool) d.GetValue(FocusOnLoad);
public static void SetFocusOnLoad(DependencyObject d, bool value) => d.SetValue(FocusOnLoad, value);
}

FocusManager was not in intellisense and this confused me a bit. I just typed the entire attribute and it worked.
FocusManager.FocusedElement="{Binding ElementName=MyTextBox}"
Microsoft Visual Studio Enterprise 2015 version 14.0.23107.0/C#/WPF

For completeness, there is also a way to handle this from code behind (e.g. in the case of controls that, for whatever reason, are created dynamically and don't exist in XAML). Attach a handler to the window's Loaded event and then use the ".Focus()" method of the control you want. Bare-bones example below.
public class MyWindow
{
private VisualCollection controls;
private TextBox textBox;
// constructor
public MyWindow()
{
controls = new VisualCollection(this);
textBox = new TextBox();
controls.Add(textBox);
Loaded += window_Loaded;
}
private void window_Loaded(object sender, RoutedEventArgs e)
{
textBox.Focus();
}
}

bind the element you want to point the focus in as
FocusManager.FocusedElement= "{Binding ElementName= Comobox1}"
in grid or groupbox etc

Further to my comment on Feb 04 '22, I solved it this way:
In the UserControl definitionin the XAML add a Loaded event handler. (pressing tab after Loaded= will automatically add an event handler to the code behind)
Then edit the event handler in the code behind:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
expressionTextBox.Focus();
}
I'm hoping that WPF is clever enough to handle th unhooking of the evnt at some point, allowing the class to be garbage collected and not give rise to memory leaks, but I don't know. I'd be interested in any comments on that.

Related

How can I change textbox contents based on which control the mouse is hovering over in WPF?

I have a textbox and several custom controls. Each of the custom controls has a "hint text" property that should appear in the textbox when that control is hovered over.
In winforms I was able to just give the custom control a textbox property and change its text property with events, however in WPF it instead makes a new textbox when I give it a property.
So how can I get the desired functionality?
Assuming your custom controls all inherit from a common base type (which I have called CustomControl as an example)...
XAML:
<TextBox x:Name=TextBox/>
<Button VerticalAlignment="Bottom" Click="ButtonBase_OnClick" MouseEnter="UIElement_OnMouseEnter" MouseLeave="UIElement_OnMouseLeave">test</Button>
Code behind:
private void UIElement_OnMouseEnter(object sender, MouseEventArgs e)
{
TextBox.Text = ((CustomControl) sender).HintText;
}
private void UIElement_OnMouseLeave(object sender, MouseEventArgs e)
{
TextBox.Text = string.Empty;
}

WPF ListView and ScrollViewer hide MouseLeftButtonDown

To demostrate the problem I have this Xaml:
<DockPanel MouseLeftButtonDown="DockPanel_MouseLeftButtonDown" MouseLeftButtonUp="DockPanel_MouseLeftButtonUp">
<ListView>
<ListViewItem>ListViewItem</ListViewItem>
</ListView>
<TextBlock>TextBlock</TextBlock>
</DockPanel>
and the event handlers are :
private void DockPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("DockPanel_MouseLeftButtonDown");
}
private void DockPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("DockPanel_MouseLeftButtonUp");
}
When you run the app and click on the words TextBlock you get MouseDown fired followed by MouseUp. So far so good. But when you click on the words ListViewItem only MouseUp is fired. Same problem for ScrollViewer (List view includes it so I am guessing it's the same problem).
Does anybody know why and if this can be fixed.
By fixed I mean get it to fire not try to use another event or another mechanism all together.
First the problem:
As suspected the problem is in ScrollViewer: http://referencesource.microsoft.com/#PresentationFramework/Framework/System/Windows/Controls/ScrollViewer.cs,488ab4a977a015eb
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (Focus())
e.Handled = true;
base.OnMouseLeftButtonDown(e);
}
As you can see it sets MouseButtonEventArgs.Handled to true which stops the bubbling of the event.
Now the solution - it is in the way you add the handler:
MyListView.AddHandler(
ListView.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(ListView_MouseLeftButtonDown),
true);
Note the last parameter (true) it causes the handler to be invoked even if the EventArgs.Hanlded was set to true.
Then you can reset it:
private void ListView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = false;
}
I had somewhat similar situation when ScrollViewer was blocking my MouseLeftButtonDown event. I had a content control wrapped into ScrollViewer:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ContentControl x:Name="Details" />
</ScrollViewer>
and this was inside of Popup which had a drag/drop behavior. So, because my behavior was not receiving this event, it did not work. When I added IsHitTestVisible="True" to ScrollViewer, my behavior started to work, but of course my ContentControl was not responding to any clicks. Then I saw this:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (Focus())
e.Handled = true;
base.OnMouseLeftButtonDown(e);
}
and tried to add Focusable="False" to exclude ScrollViewer from my click - it works. My behavior works and controls inside of ContentControl are getting all mouse events.
<ScrollViewer VerticalScrollBarVisibility="Auto" Focusable="False">
<ContentControl x:Name="Details" />
</ScrollViewer>
Hope it will help somebody.

Binding Button.IsEnabled to position of current in CollectionView

I am trying to bind the IsEnabled property of a button to properties of the window's CollectionViewSource. I am doing this to implement First/Previous/Next/Last buttons and want the First and Previous to be disabled when the view is on the first item etc.
I have the collection view source set up, UI controls binding to it correctly, with access to its view in code so the click event handlers work fine in navigating through the view.
<CollectionViewSource x:Key="cvMain" />
The DockPanel is the root element of the window
<DockPanel DataContext="{StaticResource cvMain}">
FoJobs is an observable collection, cvJobs is a CollectionView that I use in the button's click handler
private void Window_Loaded(object sender, RoutedEventArgs e) {
((CollectionViewSource)Resources["cvMain"]).Source = FoJobs;
cvJobs = (CollectionView)((CollectionViewSource)Resources["cvMain"]).View;
}
I have tried this but get a binding error "BindingExpression path error: '' property not found on 'object' ''ListCollectionView'"
<Button Name="cbFirst" Click="cbMove_Click" IsEnabled="{Binding Source={StaticResource cvMain}, Converter={StaticResource CurrPos2BoolConverter}}" />
I am trying to do with a converter first but figure a style with triggers would be more efficient, but cant get access to the collection view. Even though the underlying datacontext is set to a collection view source, the binding is passed to the converter as the view's source (if I dont explicity set the binding's Source, as above), which has no currency properties (CurrentPosition, Count etc).
Any help would be greatly appreciated.
Why don't you use a RoutedCommand for this(even if you don't use MVVM that is)?
say something like:
<Button x:Name="nextButton"
Command="{x:Static local:MainWindow.nextButtonCommand}"
Content="Next Button" />
and in your code-behind:
public static RoutedCommand nextButtonCommand = new RoutedCommand();
public MainWindow() {
InitializeComponent();
CommandBinding customCommandBinding = new CommandBinding(
nextButtonCommand, ExecuteNextButton, CanExecuteNextButton);
nextButton.CommandBindings.Add(customCommandBinding); // You can attach it to a top level element if you wish say the window itself
}
private void CanExecuteNextButton(object sender, CanExecuteRoutedEventArgs e) {
e.CanExecute = /* Set to true or false based on if you want button enabled or not */
}
private void ExecuteNextButton(object sender, ExecutedRoutedEventArgs e) {
/* Move code from your next button click handler in here */
}
You can also apply one of the suggestions from Explicitly raise CanExecuteChanged() to manually re-evaluate Button.isEnabled state.
This way your encapsulating logic relating to the button in one area.

focus visual not showing when navigating focus programically

Whenever I try to move focus programmatically the focus visual (the dotted rectangle) does not display.
What can be done to force this visual to display?
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="OnLoaded">
<StackPanel>
<TextBlock x:Name="a" Focusable="True">A</TextBlock>
<TextBlock Focusable="True">B</TextBlock>
<Button Focusable="False" Click="OnClick">Move Focus</Button>
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Keyboard.Focus(a);
}
private void OnClick(object sender, RoutedEventArgs e)
{
var request = new TraversalRequest(FocusNavigationDirection.Next);
var elementWithFocus = Keyboard.FocusedElement as UIElement;
if (elementWithFocus != null)
elementWithFocus.MoveFocus(request);
}
}
If you look (in reflector/ilspy) at the KeyboardNavigation's ShowFocusVisual you'll find that the framework will only show it if the last input was from the keyboard (or if an internal static property based on the KeyboardCues system parameter info is true). So I don't think there is a good way to do this short of using reflection to temporarily set that property or asynchronously focusing the element and forcing a keyboard action (maybe using the winforms SendKeys or keybd_event api) but I wouldn't recommend either.

How to databind Click= to a function on the object and not the page

I'm using Silverlight, but I'd be interested in a WPF answer as well
I have a list that is databound to an linked list of “Favorites”. Each favorite contains a name and a phone number.
The list is bound to a DataTemplate that describes the graphical aspects. In the this template is a button – Dial. When you click on that button I want the Dial() method of the Favorite to be called. Right now the Dial method of the page/window is called.
If this is not possible is there a way I can get the Favorite to somehow be attached to the Button? such that I know which Favorite was associated with the button press?
the below XAML does not work, Text="{Binding Name}" works great as it binds to the Name property on the Favorite, but Click="{Binding Dial}" does not call Dial() on the Favorite.
<DataTemplate x:Key="DataTemplate1">
<StackPanel d:DesignWidth="633" Orientation="Horizontal" Height="93">
<Button x:Name="DialButton" Content="Edit" Click="{Binding Dial}"/>
<TextBlock x:Name="TextBlock" TextWrapping="Wrap" Text="{Binding Name}" FontSize="64" Height="Auto" FontFamily="Segoe WP SemiLight"/>
</StackPanel>
</DataTemplate>
So it should go:
<Button CommandParameter="{Binding}" Command="{Binding Dial}"/>
Then you will receive the data object as the command parameter. In this scenario you must provide a Property that is called Dial and returns an ICommand-implementation. If the property is not available on your data-object but on the main class (code-behind), you must look for it within the binding, use for this the RelativeSource keyword.
Another way is to make a click handler. In the click handler you can cast the sender to a Button (or FrameworkElement) and then get the data object from the DataContext. I assume you tried to create such a solution.
private void Button_Click(object sender, RoutedEventArgs e) {
Button btn = (Button)sender;
MyObject obj = btn.DataContext as MyObject;
if(null != obj){
obj.Dial();
// or Dial(obj);
}
}
The markup must be as follows:
<Button x:Name="DialButton" Content="Edit" Click="Button_Click"/>
The main difference is, that I removed the binding from the Click-Event and registered an event-handler.
A third solution would be, to register a handler in the code behind for the Button.ClickEvent. The principle is similiar as in the second example.
I don't know silverlight very well. Perhaps there are the things a little bit other.
HappyClicker's first solution is the best one for most purposes, since it supports good design patterns such as MVVM.
There is another simple way to get the same result using an attached property, so you can write:
<Button Content="Edit" my:RouteToContext.Click="Edit" />
and the Edit() method will be called on the button's DataContext.
Here is how the RouteToContext class might be implemented:
public class RouteToContext : DependencyObject
{
public static string GetClick(FrameworkElement element) { return (string)element.GetValue(ClickProperty); }
public static void SetClick(FrameworkElement element, string value) { element.SetValue(ClickProperty, value); }
public static DependencyProperty ClickProperty = ConstructEventProperty("Click");
// Additional proprties can be defined here
private static DependencyProperty ConstructEventProperty(string propertyName)
{
return DependencyProperty.RegisterAttached(
propertyName, typeof(string), typeof(RouteToContext),
new PropertyMetadata
{
PropertyChangedCallback = (obj, propertyChangeArgs) =>
obj.GetType().GetEvent(propertyName)
.AddEventHandler(obj, new RoutedEventHandler((sender, eventArgs) =>
((FrameworkElement)sender).DataContext
.GetType().GetMethod((string)propertyChangeArgs.NewValue)
.Invoke(((FrameworkElement)sender).DataContext,
new object[] { sender, eventArgs }
)
))
}
);
}
}
How it works: When the RouteToContext.Click attached property is set, Type.GetEvent() is used to find the event named "Click", and an event handler is added to it. This event handler uses Type.GetMethod() to find the specified method on the DataContext, then invokes the method on the DataContext, passing the same sender and eventArgs it received.

Resources