Where Do You Code A Save Changes On Exit?

Topics: CAB & Smart Client Software Factory
Aug 17, 2007 at 4:54 PM
When I have used forms in a traditional Windows app I have always tried to offer the user an opportunity to realize they have unsaved changes when they are moving from one process to another or are exiting the app. I am trying to do the same thing in my SCSF app and tried to put code in the OnDisposing event of the WorkItem but settng the args.Cancel to true did not work at all. Where is the correct place to put code to attempt to save changes and stop the processing if the changes did not save correctly?
Aug 17, 2007 at 6:44 PM
This may help a bit - how to wrap the FormClosingEventHandler of the Shell Form and vote for close via the EventArgs:
http://staff.southworks.net/blogs/matiaswoloski/archive/2006/10/07/Structured-Shutdown-2D00-Cancelable-events-with-CAB.aspx

Something I am currently looking to solve myself is to capture the difference between the Shell closing and just an internal MDI child form closing. Since the FormClosing of the active MDI child form fires before the Shell FormClosing Event . . . and I don't have a way of knowing whether the user clicked the Shell's "x" to close or the child form's "x" . . . and I may need to branch my code accordingly.


Aug 17, 2007 at 7:35 PM
Hi!

Maybe, you can try with the SmartPartClosing event:


event EventHandler<WorkspaceCancelEventArgs> SmartPartClosing. This event is raised when a SmartPart is closing. The WorkspaceCancelEventArgs can be used to cancel the event.


private void _rightWorkspace_SmartPartClosing(object sender, Microsoft.Practices.CompositeUI.SmartParts.WorkspaceCancelEventArgs e)
{
    if (MessageBox.Show("Are you sure?", "Exit", MessageBoxButtons.YesNo) == DialogResult.No)
    {
        e.Cancel = true;
    }
}

Hope it helps!

Sebastian Iacomuzzi
http://staff.southworks.net/blogs/siacomuzzi
Aug 17, 2007 at 8:28 PM
I am currently using the SmartPartClosing event. Therein lies my problem. This fires before the FormClosing event. The WorkspaceCancelEventArgs do not supply a close reason.

I may need to use the ParentForm.FormClosing event instead because the FormClosingEventArgs gives me a CloseReason of UserClose (for the child smartpart) or MDIClose (for the Shell Form) to show me the scope of what the user is closing (i.e. the entire application or just the current active smartpart).

I am digging into this further . . . Thanks!
Aug 17, 2007 at 10:46 PM
I believe I have a solution working for my application. It centers around storing and expanding the CancelVoteEventArgs and capturing the ParentForm_Closing event within the ChildMDI Form (aka View). I will post more here once I have it more fleshed out . . .

public class CancelVoteEventArgs : EventArgs
{
public CancelVoteEventArgs()
{
_cancelVotes = new List<bool>();
_closeReason = System.Windows.Forms.CloseReason.None;
_isChildMDIFormClosing = false;
}

private List<bool> _cancelVotes;
private System.Windows.Forms.CloseReason _closeReason;
private bool _isChildMDIFormClosing;

public List<bool> CancelVotes
{
get { return _cancelVotes; }
set { _cancelVotes = value; }
}

public bool Canceled
{
get
{
return _cancelVotes.Contains(true);
}
}

public bool IsChildMDIFormClosing
{
get
{
return _isChildMDIFormClosing;
}
set
{
_isChildMDIFormClosing = value;
}
}

public System.Windows.Forms.CloseReason CloseReason
{
get
{
return _closeReason;
}
set
{
_closeReason = value;
}
}
}//End of class
}//End of namespace


CODE IN THE VIEW (MDI CHILD). The CloseReason comes up as UserClosing if Child Form's "x" is clicked. It comes up as MDIClosing if Shell Form's "x" is clicked.

private void ParentForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
CancelVoteEventArgs ce = (CancelVoteEventArgs)_presenter.WorkItem.RootWorkItem.Itemscs.Items.CancelVoteEventArgs;
ce.CloseReason = e.CloseReason;
ce.IsChildMDIFormClosing = true;

EventTopic et = _presenter.WorkItem.EventTopicscs.EventTopicNames.ShellFormClosing;
et.Fire(this, ce, _presenter.WorkItem, PublicationScope.Global);

if (ce.Canceled)
{
e.Cancel = true;
}
//Reset the CancelVoteEventArgs
ce.CancelVotes.Clear();
ce.CloseReason = CloseReason.None;
ce.IsChildMDIFormClosing = false;
}
}
Aug 23, 2007 at 4:36 PM
My solution is complete. My situation is using an UltraMDITabWorkspace to host MDI child views in Tabs within a ShellForm.

Here is my solution in overview. There may be a more elegant way.

1. I use the CancelVoteEventArgs as described above, and have the ShellFormClosing event fire my custom event ShellClosing:
EventPublication(EventTopicNames.ShellFormClosing, PublicationScope.Global)
public event EventHandler<CancelVoteEventArgs> ShellClosing;

2. I subscribe to ShellFormClosing in my Module Controller(s).

3. The ShellFormClosing Event fires a PromptForSave Event in each of my WorkItems so each can handle their related child views appropriately. The prompt for save takes care of checking whether the currently active smartpart is dirty (has changes) and whether it is valid (required fields filled-in). I can then vote in the CancelVoteEventArgs if I need to cancel and it flows back to the Shell Form which retains a handle to the CancelVoteEventArgs which I passed thru.

4. There is a trick. I want to use the same PromptForSave code if the user just closes a single Tab (smartpart/view) instead of closing the entire solution. I capture the ParentFormClosing event in each view and I interrogate the CloseReason to determine whether the view is being closed by the user (UserClosing) or by the Shell Form (MDIClosing). If the CloseReason is UserClosing, then I fire the ShellFormClosing Event directly from the view's ParentFormClosing which then fires the PromptForSave.

Of course, this is just one approach to the problem.

Cheers,
Bryan
Oct 29, 2008 at 8:08 AM
Edited Oct 29, 2008 at 8:16 AM
Hi!

is this solution still up to date? Or is there another approach as this post is from last year.

I am using the Infragistics UltraDockWorkspace Control for my Smartparts and I cannot find an event that will get fired when I "close" one of my Views.
Actually it will not be closed, the view is still there but only hidden. Thats why I can't use the SmartPartClosing event in my case.

Thanks
Marc