WPF close window when property in ViewModel changes - wpf

I was wondering if there was a way to close a window when a property in the view model changes. In my situation I have a login window with an Ok button bound to a LoginCommand so that the function Login executes when Ok is clicked. If the login is successful, I want the window to close.
Now I know I could do this by adding an event handler on my button, which calls a function like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
DatabaseCredentialsViewModel vm = (this.DataContext as DatabaseCredentialsViewModel);
vm.Login();
if (vm.LoginSuccessful)
{
this.Close();
}
}
But I was wondering if there was a way to close the window when LoginSuccessful property changes without having an event handler on my button (I like working only with command binding and not having event handlers on Click event).
Thank you

Here's a similar question, which filled my need.
Basically, you use an attached property for your window, which binds to a bool? property on your VM. When the VM property is set to something non-null, the attached property sets the Window's DialogResult, which will automatically close the window.

If you want you can try this different approach.
You can do this by associating the OK button with a command. Create an event such as LoginSuccess and when then add a window.Close() to the list of event callback. Then you have only to raise the LoginSuccess event to close the windows.
In my opinion, this respect the MVVM pattern defining an event that can be used for other trigger and not only for closing windows.

You could do this fairly easily by creating an attached property or Behavior (from Blend SDK) that hooked into your Window.
I posted a sample behavior to the Expression Code Gallery which does something similar (though definitely different) - it prevents a window from being closed via a property on the VM. You could very easily adapt the code (included in the download) to just close the window on a property change.

Related

UpdateSource on active field before deactivating window

I have a program with a main window in which a user may alter data. The program also has a to-do-dialog, in which the user may see a list of stuff to do - and this dialog has it's own datacontext (connection to database) but needs to show let updated data.
I did not set UpdateSourceTrigger in all my fields in the main window to PropertyChanged (this wouldn't be wanted in all cases anyway because of functionality in the main window).
But I need the main window to do a updatesource on the "active" field in case the user activates the to-do-dialog so that the data may be saved to the database and re-read in the to-do-dialog.
If there were a "Deactivating" event on the window, I could use that to simply set focus to something else (forcing whatever currently focussed control to update it source). But no Deactivating event exists, only Deactivated which happens after the fact. And setting focus in the deactivated main window messes up up the activation of the to-do-dialog. Also, I cannot do this in the to-do-dialogs Actived event. A Activating event would have helped - but that does not exist.
Any good ideas how to force updatesource on my main window before the to-do-dialog gets activated (either by tabbing to it or by clicking it with the mouse)?
This kind of goes off of #AkselK's answer, but I find the keyboard focused control using the Keyboard.FocusedElement property and update the right bindings based on whether its a TextBox, Selector, or ToggleButton. I expected the Keyboard.FocusedElement to tell me the window I was swapping to is what has focus, but it actually does give me the control I was in inside the deactivating window.
void MyWindow_Deactivated(object sender, EventArgs e)
{
if(Keyboard.FocusedElement is TextBox)
{
(Keyboard.FocusedElement as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
else if(Keyboard.FocusedElement is Selector)
{
(Keyboard.FocusedElement as Selector).GetBindingExpression(Selector.SelectedItemProperty).UpdateSource();
//todo: should this also update SelectedValueProperty?
}
else if(Keyboard.FocusedElement is ToggleButton)
{
(Keyboard.FocusedElement as ToggleButton).GetBindingExpression(ToggleButton.IsCheckedProperty).UpdateSource();
}
}
I am unsure if the Selector portion should also update the binding for SelectedValue.
You do not need to set Focus to a different element to update the binding. Instead, you can use the GetBindingExpression method:
valueTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
This might work in the Deactivating event.

WPF Default Button command not triggering binding in textbox

I have a search screen with some textboxes and a Search button as the default. If I type in a textbox and I CLICK the button, everything's great. But if I press enter within a text box, the button command fires but the binding on whatever text box I was in does NOT fire and so my criteria doesn't make it to the view model to get filtered on.
I know one fix is to set the bindings on the text boxes to PropertyChanged, but this seems like way overkill. I might have logic in the viewmodel doing stuff and I don't want that to trigger on every single keystroke.
What I really want is a way for the button itself to either trigger a focus change or somehow trigger binding. Or to have the textbox trigger binding if focus is lost OR I press enter OR a command is executed from anywhere
One way to do this is with a BindingGroup.
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.bindinggroup.aspx
If your TextBox(es) and Button are both contained within a Grid (for example), you would add a BindingGroup like this:
<Grid>
<Grid.BindingGroup>
<BindingGroup Name="bindingGroup1"/>
</Grid.BindingGroup>
Then you could add a Click event handler to your button and call CommitEdit() on the BindingGroup (which the Button and TextBox inherit from the Grid):
private void button1_Click(object sender, RoutedEventArgs e)
{
(sender as FrameworkElement).BindingGroup.CommitEdit();
}
The Button.Click event fires before the CommandBinding, so any databound TextBox or any other databound controls within that BindingGroup should be updated before your view model command gets executed.
I've had the exact scenario you just mentioned. The trick I use is an attached behavior that sits on a control and listens for the PreviewKeyDown event. It checks if enter is being pressed. If so it forces the control to lose focus, thus causing the binding to fire before the command executes.
A simpler approach (rather than using a binding group) is to use the default button's click event to set the focus to itself. As this happens before the command is executed it means the ViewModel is updated in time.
private void button1_Click(object sender, RoutedEventArgs e)
{
(sender as Button).Focus()
}
And if you really hate code behind, you could always write an attached property...

using routed events within Silverlight user controls

within my current project file I have a user control that has a storyboard animation applied to the control. When a button is clicked in the page the storyboard starts and basically visually presents the control to the user. The storyboard resides in the current page as a resource
<navigation:Page.Resources>
<Storyboard x:Name="PreferncesOpen">....</Storyboard x:Name="PreferncesOpen">
</navigation:Page.Resources>
Within the page I have button that I have a click event on that starts the storyboard
private void btnOpenPreferences_Click(object sender, RoutedEventArgs e)
{
preferencesPanel.Visibility = System.Windows.Visibility.Visible;
PreferncesOpen.Begin();
}
Within the userControl (preferencesPanel) I have a button that when clicked needs to close/collapse the user control. I plan to do this using Visibility.collapsed. I assume that I need to use routed commands since the button is within the user control but the actions need to be called within the page that contains the control? I'm still new to routed commands and I assume this is the correct approach. I'm just unsure how to click on a button within the user control and have it modify or execute commands that would impact how the page (in which this control resides) may change or for that part affect other elements within the page? For example when the button is clicked within the user control I would like the visibility of the user control to be set to collapsed. I also would like to have the width of one of the grid columns within the main page re-size. I have done this in the past using the code behind for the page but I am trying to separate some of this and I thought routed commands would be the way to go?
I'd greatly appreciate any tips.
Thank you in advance
The title is a bit misleading, you're asking about commands rather then routed events if I understand you correctly.
Here's an example of using a DelegateCommand<T> from the Prism library; It happens to be my personal preference.
Markup :
<Button x:Name="MyButton" Content="Btn" Command="{Binding DoSomethingCommand}"/>
Code-behind* or ViewModel :
(* if you're not using MVVM make sure to add MyButton.DataContext = this; so you're sure that the button can databind to your code behind effectively)
public DelegateCommand<object> DoSomethingCommand
{
get
{
if(mDoSomethingCommand == null)
mDoSomethingCommand = new DelegateCommand(DoSomething, canDoSomething);
return mDoSomethingCommand;
}
private DelegateCommand<object> mDoSomethingCommand;
// here's where the command is actually executed
void DoSomething(object o)
{}
// here's where the check is made whether the command can actually be executed
// insert your own condition here
bool canDoSomething(object o)
{ return true; }
// here's how you can force the command to check whether it can be executed
// typically a reaction for a PropertyChanged event or whatever you like
DoSomethingCommand.RaiseCanExecuteChanged();
The argument that's passed to the above function is the CommandParameter dependency property (in Prism it's an attached property as well as the Command property if memory serves me right).
When it's set, you can pass a value of your choosing to the command that you wish to execute.
Hope that helps.

How to register to/listen to richtextbox command's?

I'm creating a simple editor within our application using the WPF RichTextBox. Above it I've added the reguslar buttons like Bold, Italic, etc. These buttons use the RichTextBox's commands to set these properties, but next to these buttons, the commands also get send with CTRL+B, CTRL+I, etc. I want these buttons to represent the current state of the RichTextBox at the cursor. I already found out how to get this state and it works when I update this state on the SelectionChanged event. This event ofcourse isn't fired when Bold is toggled so there is no direct feedback.
I would like to know if there is a way to listen to the commands being called, without affecting its original behaviour or some other ideas to solve my problems.
I tried listening to the command the following way:
CommandBinding boldBinding = new CommandBinding(EditingCommands.ToggleBold, CommandExecuted);
_richTextBox.CommandBindings.Add(boldBinding);
and
private void CommandExecuted(object sender, ExecutedRoutedEventArgs e) {
UpdateProperties();
e.Handled = false;
}
This did update the properties, but the RichTextBox didn't seem to receive the command anymore.
I also tried to make my own commands on the control containing the RichTextBox, but when CTRL+B is pressed when the RichTextBox has focus, the original RichTextBox commands are called instead of the new one.
Many thanks in advance!
Liewe
In order to listen to the commands being called, you can use the events raised by CommandManager: Executed or PreviewExecuted.
If you change your XAML to:
<RichTextBox x:Name="_richTextBox" ...
CommandManager:PreviewExecuted="OnRichTextBoxCommand" ... />
you get the OnRichTextBoxCommand method called right before the command is executed. Unfortunately, using the Executed attached event does not work.
This method is called for each event, so you have to filter them:
private void OnRichTextBoxCommand(object sender, ExecutedRoutedEventArgs e) {
if (e.Command == EditingCommands.ToggleBold) {
UpdateProperties();
}
}
It may be even a bit more complex, as the current selection may not have changed when this method is called, so you have to post yourself a message, e.g. like this:
Dispatcher.BeginInvoke(new Action(UpdateProperties));
(if you reference already System.Core, you have the Action type, otherwise define a delegate taking no parameter and returning void, and use in instead.)

Can't set focus to a child of UserControl

I have a UserControl which contains a TextBox. When my main window loads I want to set the focus to this textbox so I added Focusable="True" GotFocus="UC_GotFocus" to the UserControls definition and FocusManager.FocusedElement="{Binding ElementName=login}" to my main windows definition. In the UC_GotFocus method i simply call .Focus() on the control i want to focus on but this doesn't work.
All i need to do is have a TextBox in a UserControl receive focus when the application starts.
Any help would be appreciated, thanks.
I recently fixed this problem for a login splash screen that is being displayed via a storyboard when the main window is first loaded.
I believe there were two keys to the fix. One was to make the containing element a focus scope. The other was to handle the Storyboard Completed event for the storyboard that was triggered by the window being loaded.
This storyboard makes the username and password canvas visible and then fades into being 100% opaque. The key is that the username control was not visible until the storyboard ran and therefore that control could not get keyboard focus until it was visible. What threw me off for awhile was that it had "focus" (i.e. focus was true, but as it turns out this was only logical focus) and I did not know that WPF had the concept of both logical and keyboard focus until reading Kent Boogaart's answer and looking at Microsoft's WPF link text
Once I did that the solution for my particular problem was straightforward:
1) Make the containing element a focus scope
<Canvas FocusManager.IsFocusScope="True" Visibility="Collapsed">
<TextBox x:Name="m_uxUsername" AcceptsTab="False" AcceptsReturn="False">
</TextBox>
</Canvas>
2) Attach a Completed Event Handler to the Storyboard
<Storyboard x:Key="Splash Screen" Completed="UserNamePassword_Storyboard_Completed">
...
</Storyboard>
and
3) Set my username TextBox to have the keyboard focus in the storyboard completed event handler.
void UserNamePassword_Storyboard_Completed(object sender, EventArgs e)
{
m_uxUsername.Focus();
}
Note that calling item.Focus() results in the call Keyboard.Focus(this), so you don't need to call this explicitly. See this question about the difference between Keyboard.Focus(item) and item.Focus.
Its stupid but it works:
Pop a thread that waits a while then comes back and sets the focus you want. It even works within the context of an element host.
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
System.Threading.ThreadPool.QueueUserWorkItem(
(a) =>
{
System.Threading.Thread.Sleep(100);
someUiElementThatWantsFocus.Dispatcher.Invoke(
new Action(() =>
{
someUiElementThatWantsFocus.Focus();
}));
}
);
}
Just recently I had a list-box that housed some TextBlocks. I wanted to be able to double click on the text block and have it turn into a TextBox, then focus on it and select all the text so the user could just start typing the new name (Akin to Adobe Layers)
Anyway, I was doing this with an event and it just wasn't working. The magic bullet for me here was making sure that I set the event to handled. I figure it was setting focus, but as soon as the event went down the path it was switching the logical focus.
The moral of the story is, make sure you're marking the event as handled, that might be your issue.
“When setting initial focus at application startup, the element to
receive focus must be connected to a PresentationSource and the
element must have Focusable and IsVisible set to true. The recommended
place to set initial focus is in the Loaded event handler"
(MSDN)
Simply add a "Loaded" event handler in the constructor of your Window (or Control), and in that event handler call the Focus() method on the target control.
public MyWindow() {
InitializeComponent();
this.Loaded += new RoutedEventHandler(MyWindow_Loaded);
}
void MyWindow_Loaded(object sender, RoutedEventArgs e) {
textBox.Focus();
}
since i tried a fuzquat's solution and found it the most generic one, i thought i'd share a different version, since some complained about it looking messy. so here it is:
casted.Dispatcher.BeginInvoke(new Action<UIElement>(x =>
{
x.Focus();
}), DispatcherPriority.ApplicationIdle, casted);
no Thread.Sleep, no ThreadPool. Clean enough i hope.
UPDATE:
Since people seem to like pretty code:
public static class WpfExtensions
{
public static void BeginInvoke<T>(this T element, Action<T> action, DispatcherPriority priority = DispatcherPriority.ApplicationIdle) where T : UIElement
{
element.Dispatcher.BeginInvoke(priority, action);
}
}
now you can call it like this:
child.BeginInvoke(d => d.Focus());
WPF supports two different flavors of focus:
Keyboard focus
Logical focus
The FocusedElement property gets or sets logical focus within a focus scope. I suspect your TextBox does have logical focus, but its containing focus scope is not the active focus scope. Ergo, it does not have keyboard focus.
So the question is, do you have multiple focus scopes in your visual tree?
I found a good series of blog posts on WPF focus.
Part 1: It’s Basically Focus
Part 2: Changing WPF focus in code
Part 3: Shifting focus to the first available element in WPF
They are all good to read, but the 3rd part specifically deals with setting focus to a UI element in a UserControl.
Set your user control to Focusable="True" (XAML)
Handle the GotFocus event on your control and call yourTextBox.Focus()
Handle the Loaded event on your window and call yourControl.Focus()
I have a sample app running with this solution as I type. If this does not work for you, there must be something specific to your app or environment that causes the problem. In your original question, I think the binding is causing the problem.
I hope this helps.
After having a 'WPF Initial Focus Nightmare' and based on some answers on stack, the following proved for me to be the best solution.
First, add your App.xaml OnStartup() the followings:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
new RoutedEventHandler(WindowLoaded));
Then add the 'WindowLoaded' event also in App.xaml :
void WindowLoaded(object sender, RoutedEventArgs e)
{
var window = e.Source as Window;
System.Threading.Thread.Sleep(100);
window.Dispatcher.Invoke(
new Action(() =>
{
window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}));
}
The threading issue must be use as WPF initial focus mostly fails due to some framework race conditions.
I found the following solution best as it is used globally for the whole app.
Hope it helps...
Oran
I converted fuzquat's answer to an extension method. I'm using this instead of Focus() where Focus() did not work.
using System;
using System.Threading;
using System.Windows;
namespace YourProject.Extensions
{
public static class UIElementExtension
{
public static void WaitAndFocus(this UIElement element, int ms = 100)
{
ThreadPool.QueueUserWorkItem(f =>
{
Thread.Sleep(ms);
element.Dispatcher.Invoke(new Action(() =>
{
element.Focus();
}));
});
}
}
}
I've noticed a focus issue specifically related to hosting WPF UserControls within ElementHosts which are contained within a Form that is set as an MDI child via the MdiParent property.
I'm not sure if this is the same issue others are experiencing but you dig into the details by following the link below.
Issue with setting focus within a WPF UserControl hosted within an ElementHost in a WindowsForms child MDI form
I don't like solutions with setting another tab scope for UserControl. In that case, you will have two different carets when navigating by keyboard: on the window and the another - inside user control. My solution is simply to redirect focus from user control to inner child control. Set user control focusable (because by default its false):
<UserControl ..... Focusable="True">
and override focus events handlers in code-behind:
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
MyTextBox.Focus();
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
Keyboard.Focus(MyTextBox);
}
What did the trick for me was the FocusManager.FocusedElement attribute. I first tried to set it on the UserControl, but it didn't work.
So I tried putting it on the UserControl's first child instead:
<UserControl x:Class="WpfApplication3.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid FocusManager.FocusedElement="{Binding ElementName=MyTextBox, Mode=OneWay}">
<TextBox x:Name="MyTextBox"/>
</Grid>
... and it worked! :)
I have user control - stack panel with two text boxes.The text boxes were added in contructor, not in the xaml. When i try to focus first text box, nothing happend.
The siggestion with Loaded event fix my problem. Just called control.Focus() in Loaded event and everthing.
Assuming you want to set focus for Username textbox, thus user can type in directly every time it shows up.
In Constructor of your control:
this.Loaded += (sender, e) => Keyboard.Focus(txtUsername);
After trying combinations of the suggestions above, I was able to reliably assign focus to a desired text box on a child UserControl with the following. Basically, give focus to the child control and have the child UserControl give focus to its TextBox. The TextBox's focus statement returned true by itself, however did not yield the desired result until the UserControl was given focus as well. I should also note that the UserControl was unable to request focus for itself and had to be given by the Window.
For brevity I left out registering the Loaded events on the Window and UserControl.
Window
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
ControlXYZ.Focus();
}
UserControl
private void OnControlLoaded(object sender, RoutedEventArgs e)
{
TextBoxXYZ.Focus();
}
I set it in the PageLoaded() or control loaded, but then I'm calling WCF async service and doing stuff that seems to lose the focus. I have to to set it at the end of all the stuff I do. That's fine and all, but sometimes I make changes to the code and then I forget that I'm also setting the cursor.
I had same problem with setting keyboard focus to canvas in WPF user control.
My solution
In XAML set element to Focusable="True"
In element_mousemove event create simple check:
if(!element.IsKeyBoardFocused)
element.Focus();
In my case it works fine.

Resources