Handling the "X" close button in WPF under MVVM - c#

I'm creating a basic database application in WPF, and I have started using the MVVM pattern.
I have a dialog that asks the user to select an item from a ListBox and click "OK." After that, I take the item the user clicked from a property in the view model and pass it into another dialog. But if the user clicks "Cancel," I set that value to null, and the action is canceled: I don't open the next dialog and return to the main screen. Example:
public class SelectEquipmentViewModel : WorkspaceViewModel
{
private bool _selected;
public Equipment SelectedEquipment
{
// Item selected by the user
}
// Action for "SelectCommand," which is attached to
// the "Select" button in the view
public void ExecuteSelect()
{
_selected = true;
// Fires a RequestClose event in WorkspaceViewModel,
// which is attached to the view's Close method
RequestClose();
}
public override void RequestClose()
{
if (!_selected)
{
// The user clicked "Cancel"
SelectedEquipment = null;
}
base.RequestClose();
}
}
This has been working great, but the problem comes if the user clicks the red "X" close button in the window's control box. The RequestClose method never gets invoked, and the selected item isn't set to null, which is bad.
I've considered attaching the view model to the Closing event of the view, but I feel this could get messy if I start creating handlers for all these events.
What would be the "preferred" way of handling this situation?
Thanks.

I think that using the EventToCommand behavior to wire up the Window object's Closing event to a new ExecuteCancel command is pretty clean.
public void ExecuteCancel()
{
_selected = false;
// Fires a RequestClose event in WorkspaceViewModel,
// which is attached to the view's Close method
RequestClose();
}
Where do you think this will get messy? If you add a Cancel button, it could use the same ExecuteCancel bits...

Behaviors are what you want to use to execute a command when the user presses the "X" button on window using MVVM. Check out Reed Copsey's blog here: http://reedcopsey.com/2009/10/09/using-behaviors-to-allow-the-viewmodel-to-manage-view-lifetime-in-m-v-vm/
You can download a sample application here...
I use this method all the time to allow the ViewModel manage the life of the view.

Approach without additional dependencies is described in article Handling a Window's Closed and Closing events in the View-Model and code with example provided. This does not add code behind xaml.
(Thanks to Reed Copsey's link)

Related

Cannot raise InteractionRequest in OnNavigatedTo Method?

I'm navigating to a view when its module gets loaded:
public void OnImportsSatisfied()
{
this.ModuleManager.LoadModuleCompleted +=
(s, e) =>
{
if (e.ModuleInfo.ModuleName == EmailModuleName)
{
this.RegionManager.RequestNavigate(
RegionNames.MainContentRegion,
InboxViewUri);
}
};
}
when I navigate to this view I want to raise a NotificationRequest (and his PopupWindow); this method is defined in the ViewModel:
void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
MyInteractionRequest.Raise(...);
}
Through debugging I discovered that the InteractionRequest has no subscribers to its Raised event for the first time I navigate to the view, and that's why no interaction window is shown. In order to make the InteractionRequest work I need to navigate to another view and then go back.
Why does this happend? Does the view get created too late after the viewmodel and so it cant subscribe the InteractionTrigger to the Interaction Request?
My guess is that the view has not been initialized by the time you raise the request. This means no bindings have been created and no handlers have been added. You approach in general is bad practice. If you must show a popup when the page is first show, add an event to command behavior for the view's loaded event and show your popup there. This ensures that the pages has been fully loaded and all bindings have been made.

Window.Current.Activated not fired on every UI interaction

I am working on UWP application and I'm having a logic that depends on application focus gain/loss:
...
Window.Current.Activated += Current_Activated;
...
//My code depends on this flag
public bool IsViewCurrentlyInFocus { get; private set; }
private void Current_Activated(object sender, WindowActivatedEventArgs e)
{
IsViewCurrentlyInFocus = e.WindowActivationState != CoreWindowActivationState.Deactivated;
}
What I've notice is that user can interact with my application (for instance: scrolling the listview) although the event isn't raised - only when tapping/clicking on the page...
How can I determine that some sort of interaction is made with my application?
Thanks in advance
Please see the documentation:
Occurs when a window becomes the foreground window.
In a nutshell, this is a Window event, it does not affect the elements within the window. In your scenario, you can make use of the IsActive property to determine whether your Window is being used. For individual elements, I would suggest using the GotFocus event and IsFocused property respectively.
If you are specifically trying to detect the scroll in a ListView, this isn't possible, however you can check this answer.

In C#, How to cancel the default mouse click event when using bindingNavigator

I am a new C# user and now I have encountered a problem in using BindingNavigator.
I am using bindingNavigator to update records in a table of database. Before I leave current updated record and enter the next record by clicking Next button, I will perform some validation, if there is any thing incorrect, I hope it could raise a warning to give me chance to correct the wrong fields instead of moving to the next record.
I added some lines in bindingNavigatorMoveNextItem_MouseDown event, but it still move to next item even there are some thing wrong with current record(fields have some logical connection). Can any expert here help me out about that? Many Thanks!
You have two approaches: either overriding WndProc and prevent mouse click window message from calling the base's WndProc, or simply overriding OnMouseClick:
class Hello : BindingNavigator
{
private bool canFire = false;
protected override void OnMouseClick(MouseEventArgs e) // second approach
{
// don't call base method so that event doesn't fire up
if (this.canFire)
base.OnMouseClick(e);
}
}
I know this is old.. but for anyone else...
You should use the normal buttons, and just use the validating event, canceling if anything fails your validation.
The control does not display the property in the designer, but you can still set it : bindingnavigator.CausesValidation = true;
I do this in the form load.
this alone still won't do it.
you also need to set the focus.
bindingnavigator.focus();
I do this in the bindingnavigator_ItemClicked event so it happens no matter what button is clicked.

Execute method On-Load of an object on a form in C#

E.g. instead of having a button to initiate the method, the method automatically happens without any user interaction - automatically.
private void button13_Click(object sender, EventArgs e)
{
try
{
ServiceController sc = new ServiceController();
sc.ServiceName = "Spooler";
if (sc.Status.ToString().ToLower() == "stopped")
{
serviceStatusLabel.Text = "Installed but stopped";
}
if (sc.Status.ToString().ToLower() == "running")
{
serviceStatusLabel.Text = "Installed and started";
}
}
catch
{
serviceStatusLabel.Text = "Service not installed";
}
}
I just want the Label object to show the service status when the form is loaded up, without using a button
EDIT: Given your comment, are you actually after the Form.Load event? It sounds like it. Any event handlers subscribed to that event will be executed "when the form is displayed for the first time".
(The confusing thing is that your title talks about "On-Load" of an object whereas it sounds like you really want the method to be called when the form is loaded.)
It's not really clear what you mean by "when its output on the form" but you might want to look at the TextChanged and VisibleChanged events. That's if you want something to happen when the label is altered.
If you're looking for when the service status is altered, it doesn't look like there's an event raised for that, I'm afraid. Note that it would be much cleaner to switch on the enum value rather than to convert it to a string, lower it, and then compare that with hard-coded constants.
... Do I get your question correctly?
You want a piece of code to be executed when an object or the form is loaded?
Well that's easy :p
Click on your object (or form) in the designer, in the properties dock, click the lightning bolt icon, go to the Load or Show event, and double-click the box.
A new piece of code should now be created in the code view, something like this:
private void Object_Load(blabla) handles Object.Load
{
}
Whatever code is in that event will be executed when the object is loaded or shown.
If you create a handler for the Load event, it will run when the form gets loaded.

Focus control from ViewModel

Why is the 1st button "active" when I am not hovering over the button or anything. This seems to happen after I change tabs.
I suspect that when I change tabs, it focuses the 1st control. Is that the case? I am developing a MVVM app, so from my view model, how might I focus the text box instead?
Since WPFs concept of focus is kinda complicated, I have a class called FocusEnforcer.
It really really makes sure the desired control gets the focus, no matter what.
public static class FocusEnforcer
{
public static void EnforceFocus(UIElement element)
{
if (!element.Focus())
{
element.Dispatcher.BeginInvoke(DispatcherPriority.Input,
new ThreadStart(delegate()
{
element.Focus();
}));
}
}
}
This kind of behavior requires just that: a behavior. Or, at least, a new attached property.
Create an attached property for tab controls.
Create a handler for changes to this attached property.
In this handler, subscribe to the TabControl's SelectionChanged event.
In the SelectionChanged event handler, use the TabControl.FindName method to get the text box.
Execute the FocusManager.SetFocusedElement method using the TabControl as the focus scope.

Categories