How to thru char into TextBox? - wpf

I have some TextBox on focus ( cursor is blinking on him ).
Now, from other usercontrol ( that contain buttons ) i want to send event that will insert char into the TextBox when pressing any of the buttons that are on the usercontrol.
This need to be without lose the focus from the TextBox ... ( blinking cursor on the TextBox )
How can i do it ?
( i try to raise key down event - but its does not work )

Make your buttons to be not focusable (Focusable = false).

Do you want to use this virtual keyboard with other applications, or is it something that's only going on in your application? Beyond that, if it is only your application, do you only ever want to insert characters into one particular TextBox, or potentially any TextBox?
If it's a virtual keyboard intended to work with any application, then you'll want to use a Win32 API method like SendKeys. WinForms has an extremely easy interface for using SendKeys.
If it only ever needs to add characters to the one TextBox, then it's much more easy to modify the TextBox's Text property rather than trying to raise events on it to get the desired behavior. There's a CaretIndex property that will tell you where to insert the character. Beyond that, it's simple string concatenation.

<StackPanel>
<TextBox Name="MainTextBox" />
<Button Content="A"
Focusable="False"
Click="Button_Click" />
<Button Content="B"
Focusable="False"
Click="Button_Click" />
<Button Content="C"
Focusable="False"
Click="Button_Click" />
<Button Content="D"
Focusable="False"
Click="Button_Click" />
<Button Content="E"
Focusable="False"
Click="Button_Click" />
</StackPanel>
Code:
private void Button_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Text += (sender as Button).Content.ToString();
}

var text = button.Content as string;
textbox.SelectedText = text;
textbox.SelectionLength = 0; // removing selection from inserted text
textbox.SelectionStart += text.Length;
This will insert button content at cursor position (and replace selected text) - the same as user inputted it from keyboard.
PS. if textbox is unknown, it may be found with
var textbox = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this)) as TextBox;
Instead of FocusManager.GetFocusScope(this) you may put window.
If you need it not only for textboxes - WinAPI functions should help. See http://www.pinvoke.net/default.aspx/user32.sendinput

Related

WPF: How do I get X Y position of last character within a TextBox?

I want to have the x, y position of the last character in a TextBox. I found GetCharacterIndexFromPoint method which exactly is a reverse approach. => here
However, I couldn't find the appropriate approach for getting actual position of last character within a TextBox.
Does anyone know how I can get such information?
This question is pretty old now, but I want to give it a try.
My solution for getting the actual position of last character within a TextBox:
MainWindow.xaml:
<StackPanel>
<Label Margin="10" HorizontalAlignment="Center" Content="Enter First Text: " FontSize="20"/>
<TextBox Name="TextBox" Margin="10" Width="200" LostFocus="TextBox_OnLostFocus"/>
<Button Content="GetPositionFromLastCharacter" Width="200" Click="GetLastCharacterFromTextBox"/>
<Label Margin="10" HorizontalAlignment="Center" Content="Enter second Text: " FontSize="20"/>
<TextBox Margin="10" Width="200"/>
</StackPanel>
Method "GetLastCharacterFromTextBox" in MainWindow.xaml.cs
private void GetLastCharacterFromTextBox(object sender, RoutedEventArgs e)
{
int lastCharacterPosition = TextBox.Text.LastIndexOf(TextBox.SelectedText, StringComparison.CurrentCulture);
MessageBox.Show($"Last character from the Text is at position: {lastCharacterPosition}");
}
Output:
WPF Application running
As you can see, I used the "LastIndexOf" Method with the selected text in the TextBox.
Suggestion for showing caret without focusing:
Use the "LostFocus" property on a TextBox and implement the following code:
private void TextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
e.Handled = true;
}
The result is the following:
Showing caret without focus
Even though I have no focus on the first TextBox anymore, the caret is still visible.

StringUpDown in WPF?

I have a question regarding the following question about NumericUpDown:
Good NumericUpDown equivalent in WPF?
How I can do this for 12 months of a year? I am planning to use a vertical ScrollBar with a textbox. I want to link the vertical ScrollBar up and down clicks to increment and decrement the months in the textbox using C#.
<ScrollBar x:Name="scbm"
HorizontalAlignment="Left" Height="26" Margin="230,195,0,0"
VerticalAlignment="Top" RenderTransformOrigin="0.542,0.83"/>
<TextBox x:Name="txtm"
HorizontalAlignment="Left" Height="23" Margin="139,195,0,0"
TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="66"/>
Can anyone tell me how I can accomplish this?
You can have a look This article that does something similar with a datepicker.
Basically you'll want to have a property for the month, and depending on your approach (code behind / mvvm), handle the click events on the up/down buttons, or the keyboard keydown event to handle them logic (so in your case, add the up/down buttons, and wire them to the events, naming them appropriately).
For example, having this on your xaml:
<DatePicker ... PreviewKeyDown="PreviewKeyDown_EventHandler" ... />
And something like this in your code behind:
private void PreviewKeyDown_EventHandler(object sender, System.Windows.Input.KeyEventArgs e)
{
// Avoid them nasty exceptions is the user hits "up" or "down" with no date selected:
if (sender == null || ((DatePicker)sender).SelectedDate == null)
return;
// Do this on up
if (e.Key == Key.Up)
{
((DatePicker)sender).SelectedDate =
((DatePicker)sender).SelectedDate.GetValueOrDefault().AddMonths(1);
}
// And this on down
if (e.Key == Key.Down)
{
((DatePicker)sender).SelectedDate =
((DatePicker)sender).SelectedDate.GetValueOrDefault().AddMonths(-1);
}
}
That example uses a datetime as the property, but you can do similar things with an int if that's what you're after.
(again, have a look at the link for more options, examples, and code example if you'd like)
Edit:
This doesn't have theh benefits of the up/down keys like the article's example, but this works:
<ScrollBar x:Name="scbm"
SmallChange="1" Maximum="12" Minimum="1"
Value="{Binding MonthScrollBar}"
HorizontalAlignment="Left" Height="26" Margin="230,195,0,0"
VerticalAlignment="Top" RenderTransformOrigin="0.542,0.83" />
<TextBox x:Name="txtm"
Text="{Binding MonthScrollBar}"
HorizontalAlignment="Left" Height="23" Margin="139,195,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="66" />
On my ViewModel (or your codebehind, if you want, do something similar)
public int MonthScrollBar
{
get { return _monthScrollBar; }
set { _monthScrollBar = value;
RaisePropertyChanged("MonthScrollBar");}
}
private int _monthScrollBar;
And setting the property value to whatever you want on the constructor.
Note that I'm using MVVM, binding to a property with notification change (so changes are propagating to the View), and initializing it.
Both the ScrollBar and TextBox bind to the same MonthScrollBar property)
If you're doing code behind, you can access it directly from the code behind.

WebBrowser control keyboard and focus behavior

Apparently, there are some serious keyboard and focus issues with WPF WebBrowser control. I've put together a trivial WPF app, just a WebBrowser and two buttons. The app loads a very basic editable HTML markup (<body contentEditable='true'>some text</body>) and demonstrates the following:
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisualStyle="{x:Null}" does not help).
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true.
I'd say, such behaviour is almost too dysfunctional to be true, but that's how it works. I could probably come up with some hacks to fix it and bring it in row with native WPF controls like TextBox. Still I hope, maybe I'm missing something obscure yet simple here. Has anyone dealt with a similar problem? Any suggestions on how to fix this would be greatly appreciated.
At this point, I'm inclined to develop an in-house WPF wrapper for WebBrowser ActiveX Control, based upon HwndHost. We are also considering other alternatives to WebBrowser, such as Chromium Embedded Framework (CEF).
The VS2012 project can be downloaded from here in case someone wants to play with it.
This is XAML:
<Window x:Class="WpfWebBrowserTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="640" Height="480" Background="LightGray">
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="300"/>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
</Window>
This is C# code, it has a bunch of diagnostic traces to show how focus/keyboard events are routed and where the focus is:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
namespace WpfWebBrowserTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// watch these events for diagnostics
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.PreviewKeyDownEvent, new KeyEventHandler(MainWindow_PreviewKeyDown));
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(MainWindow_GotKeyboardFocus));
EventManager.RegisterClassHandler(typeof(UIElement), FocusManager.GotFocusEvent, new RoutedEventHandler(MainWindow_GotFocus));
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
// load the browser
this.webBrowser.NavigateToString("<body contentEditable='true' onload='focus()'>Line 1<br>Line 3<br>Line 3<br></body>");
this.btnLoad.IsChecked = true;
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
// close the form
if (MessageBox.Show("Close it?", this.Title, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
this.Close();
}
// Diagnostic events
void MainWindow_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_GotFocus(object sender, RoutedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
Debug.Print("{0}, key: {1}, source: {2}, {3}", FormatMethodName(), e.Key.ToString(), FormatType(e.Source), FormatFocused());
}
// Debug output formatting helpers
string FormatFocused()
{
// show current focus and keyboard focus
return String.Format("Focus: {0}, Keyboard focus: {1}, webBrowser.HasFocusWithin: {2}",
FormatType(FocusManager.GetFocusedElement(this)),
FormatType(Keyboard.FocusedElement),
((System.Windows.Interop.IKeyboardInputSink)this.webBrowser).HasFocusWithin());
}
string FormatType(object p)
{
string result = p != null ? String.Concat('*', p.GetType().Name, '*') : "null";
if (p == this.webBrowser )
result += "!!";
return result;
}
static string FormatMethodName()
{
return new StackTrace(true).GetFrame(1).GetMethod().Name;
}
}
}
[UPDATE] The situation doesn't get better if I host WinForms WebBrowser (in place of, or side-by-side with WPF WebBrowser):
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<WindowsFormsHost Name="wfHost" Focusable="True" Height="150" Margin="10,10,10,10">
<wf:WebBrowser x:Name="wfWebBrowser" />
</WindowsFormsHost>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
The only improvement is that I do see focus events on WindowsFormsHost.
[UPDATE] An extreme case: two WebBrowser controls with two carets showing at the same time:
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<WebBrowser Name="webBrowser2" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
this.webBrowser.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text</textarea></body>");
this.webBrowser2.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text2</textarea></body>");
This also illustrates that the focus handling issue is not specific to contentEditable=true content.
For anyone else stumbling upon this post and needing to set keyboard focus to the browser control (not a particular element within the control, necessarily), this bit of code worked for me.
First, add a project reference (under Extensions in VS) for Microsoft.mshtml.
Next, whenever you'd like to focus the browser control (say for example, when the Window loads), simply "focus" the HTML document:
// Constructor
public MyWindow()
{
Loaded += (_, __) =>
{
((HTMLDocument) Browser.Document).focus();
};
}
This will place keyboard focus inside the web browser control, and inside the "invisible" ActiveX window, allowing keys like PgUp / PgDown to work on the HTML page.
If you want to, you might be able to use DOM selection to find a particular element on the page, and try to focus() that particular element. I have not tried this myself.
The reason it behaves this way is related to the fact that it's an ActiveX control which itself is a fully windows class (it handles mouse and keyboard interaction). In fact much of the time you see the component used you'll find it is the main component taking up a full window because of this. It doesn't have to be done that way but it presents issues.
Here's a forum discussing the exact same issue and it's causes can be clarified by reading the last commentators article links:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/1b50fec6-6596-4c0a-9191-32cd059f18f7/focus-issues-with-systemwindowscontrolswebbrowser
To outline the issues you're having
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
that's because the browser control itself is a window which can be tabbed to. It doesn't "forward" the tab to it's child elements immediately.
One way to change this would be to handle the WM message for the component itself but keep in mind that doing so gets tricky when you want the "child" document inside of it to be able to handle messages.
See: Prevent WebBrowser control from stealing focus? specifically the "answer". Although their answer doesn't account that you can control whether the component interacts through dialogs with the user by setting the Silent property (may or may not exist in the WPF control... not sure)
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
This is because the control itself has received the focus. Another consideration is to add code to handle the GotFocus event and to then "change" where the focus goes. Tricky part is figuring out if this was "from" the document -> browser control or your app -> browser control. I can think of a few hacky ways to do this (variable reference based on losing focus event checked on gotfocus for example) but nothing that screams elegant.
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisualStyle="{x:Null}" does not help).
I wonder if changing Focusable would help or hinder. Never tried it but I'm going to venture a guess that if it did work it would stop it from being keyboard navigable at all.
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true.
People have hit issues with where 2 browser controls both show the focus(well... the caret) or even had a hidden control take the focus.
All in all it's pretty awesome what you can do with the component but it's just the right mix of letting you control/change the behavior along with predefined sets of behavior to be maddening.
My suggestion would be to try to subclass the messages so you can direct the focus control directly through code and bypass it's window from trying to do so.

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.

"Modal Dialog" in WPF - make overlay block key events

I'm creating a WPF application containing a "Main-Content" - Layer containing a TabControl and a "Dialog" - Layer containing an ItemsControl.
The XAML looks like this:
<Grid>
<TabControl>
..Some Tabs
</TabControl>
<ItemsControl>
<ContentControl Content={Binding Dialog1Property} />
<ContentControl Content={Binding Dialog2Property} />
</ItemsControl>
</Grid>
Usually "Dialog1Property" and "Dialog2Property" are null which means the ItemsControl is invisible. Whenever I assign a Control to one of them, it is shown in front of the TabControl which is exactly what I want. If I assign a gray Rectangle with an opacity of 0.7 to one of the Dialog - Properties it creates a Gray overlay.
If I click on the Tab, which is slightly visible through the overlay, nothing happens - the Rectangle blocks Mouse Events. It is, however, still possible to focus the TabControl behind the overlay using the Tab-Key and therefore it is also possible to switch tabs even though a Dialog is shown.
Is there an easy way to tell the rectangle to somehow block key events as it allready does with Mouseclicks?
Regards
BBRain
Yes, on your Rectangle, subscribe to the event PreviewKeyDown.
<Rectangle Opacity="0.7" Fill="Green" PreviewKeyDown="Rectangle_PreviewKeyDown" />
In its handler, simply set e.Handled = true;
private void Rectangle_PreviewKeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
Since routed events prefixed with "Preview..." are tunneling, the elements under your rectangle won't recieve the input.

Resources