Reputation: 961
In my application I have a Usercontrol which represents some configuration for an object. Inside this is a Texblock which contains the name of said object. I wanted to be able to click on this Textblock and turn it into a Textbox so you were able to edit the name.
I want it to have the following 3 behaviours
and I am currently stuck on 3. How can I get my textbox to satisfy requirement 3 - Clicking off the textbox sets the value I have implemented some attachment behaviours to make the other 2 work but can't get the mouse one to work.
I have tried the following
The code for my textbox is as follows.
<TextBox x:Name="text" VerticalAlignment="Center"
Margin="5,2,0,0"
Text="{Binding Name, Mode=TwoWay}"
Visibility="{Binding IsEditingName, Converter={StaticResource VisConverter}, FallbackValue=Collapsed}"
b:FocusBehaviours.ShouldFocusWhenVisible="True"
b:InputBehaviours.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>
I've looked at countless SO posts and none of them seems to be able to help me with this, they all recommend adding it to the window/usercontrol which I can't do.
Upvotes: 1
Views: 1966
Reputation: 1008
I was doing this in a WPF Window for a built-in TextBox
in order to do something whenever user clicks outsides. The method may be easily applied to WPF/UWP Window
or Page
.
// In your `XxxWindow.xaml.cs`(or corresponding class in VB):
public partial class XxxWindow : Window {
...
// The event handler delegate for handling click outsides a TextBox.
private readonly MouseButtonEventHandler windowWideMouseButtonEventHandler;
// Add something in the constructor or any initialization methods.
public MainWindow() {
...
// Initialize the handler.
windowWideMouseButtonEventHandler = new MouseButtonEventHandler(OnWindowWideMouseEvent);//+
}
...
...
// Whenever the text box got focus, begin listening to window-wide clicks.
// This method shall be bounded in xaml or elsewhere.
// Added method
private void SearchBox_GotFocus(object sender, RoutedEventArgs e) {
AddHandler(Window.PreviewMouseDownEvent, windowWideMouseButtonEventHandler);
}
// The handler method
// Added method
private void OnWindowWideMouseEvent(object sender, MouseButtonEventArgs e) {
// Exclude the text box itself.
if (!FocusManager.GetFocusedElement(this).IsMouseOver) {
// Unregister the already-used handler.
RemoveHandler(Window.PreviewMouseDownEvent, windowWideMouseButtonEventHandler);
// Do something, save text or cancel focus.
}
}
...
}
And in XxxWindow.xaml
set GotFocus
property for the TextBox
.
<TextBox ... GotFocus="SearchBox_GotFocus">
...
</TextBox>
Done.
About this:
Added a PreviewMouseDownOutsideCapturedElement - but can never get this to fire.
I only got it fired if a MessageBox was shown whenever CaptureMouse
was called, which was not supposed to happen in the real world. Maybe that's designed for popups.
Upvotes: 1
Reputation: 961
I managed to find a solution.
Basically I bound an event in my code behind for OnGotFocus of the textbox. When it hits this method it adds an event for MouseDown and MouseLeave to the usercontrol. So when you click anywhere in the usercontrol it updates the contents of the textbox.
If you leave the bounds of the usercontrol then you want to shift the mouse down event to the window itself. Similarly when you move back into the usercontrol you want to remove the mouse down event on the window or else when you click back into the textbox after leaving, it sets the value.
I have added my code here, hopefully it will be clear to whoever may have a similar problem.
I am sure there must be a better way of doing it than using the code behind but I don't think I am breaking MVVM principles here so I think I am safe.
private void View_OnMouseDown(object sender, MouseButtonEventArgs e)
{
updateTextbox();
}
private void View_OnMouseLeave(object sender, MouseEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
if (parentWindow != null)
{
Mouse.AddPreviewMouseDownHandler(parentWindow, ParentWindow_OnMouseDown);
}
}
private void View_OnMouseEnter(object sender, MouseEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
if (parentWindow != null)
{
Mouse.RemovePreviewMouseDownHandler(parentWindow, ParentWindow_OnMouseDown);
}
}
private void ParentWindow_OnMouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
Window parentWindow = Window.GetWindow(this);
if (parentWindow != null)
{
Mouse.RemovePreviewMouseDownHandler(parentWindow, ParentWindow_OnMouseDown);
}
updateTextbox();
}
private void updateTextbox()
{
Keyboard.Focus(this);
}
private void Text_OnGotFocus(object sender, RoutedEventArgs e)
{
MouseDown += View_OnMouseDown;
MouseLeave += View_OnMouseLeave;
MouseEnter += View_OnMouseEnter;
}
private void Text_OnLostFocus(object sender, RoutedEventArgs e)
{
BindingOperations.GetBindingExpression(text, TextBox.TextProperty).UpdateSource();
MouseDown -= View_OnMouseDown;
MouseLeave -= View_OnMouseLeave;
MouseEnter -= View_OnMouseEnter;
}
Upvotes: 2