Stopping an Unloaded event in WPF - wpf

I think this isn't the best way to go about doing this but this is the only way I can think of.
I have this WPF UserControl and it is called via a menu item (click on the menu and open the UserControl).
There are a number of things the user can do on the UserControl and I keep track of the changes made (via EF and a variable that is set to true if the user makes any changes).
Now comes the part where I know I'm doing wrongly. I want the UserControl to check if there have been changes or additions made.
I have placed this in the Unloaded event trigger.
Private Sub TheUserControl_Unloaded(sender As Object, e As RoutedEventArgs) Handles Me.Unloaded
If (changesMade) Then
Dim answer = MessageBox.Show("Changes have been.....", "Alert", MessageBoxButton.OKCancel)
If (answer = MessageBoxResult.OK) Then
If (saveChanges() = False) Then
'stop unloading???
End If
Else
MessageBox.Show("Discarding changes.")
End if
End if
End Sub
I realised that I have placed this in the wrong EventControl but I have no idea which Event Control to place it in.
So now I'm asking, where can I put this "check" to ensure that if the user navigates away from this page (by clicking on another item in the menu) they will be asked to save the changes made before the UserControl closes and if an error occurs in the saving, it would stop the UserControl from Unloading.
Thanks.

Related

VB .Net WPF changing windows

I am trying to create a WPF application with a number of windows and am getting a bit confused with the correct way to do this. I understand that I need to keep the main window open for the program to run, so I hide this for the sub windows. However, the problem occurs when I want to change from one sub window to another and can be narrowed down to the following: how to catch the event when the user presses the close button in the top right (there seems to be no way to reference when this button is pressed in VB .net)?
Currently, I am closing the current window and then creating a new instance of the next window to change from one to the other, like the following:
Private Sub btnContinue_Click(sender As Object, e As RoutedEventArgs) Handles btnContinue.Click
Dim wd As New NextWindow
Me.Close()
wd.ShowDialog()
End Sub
There is an exit button:
Private Sub btnExit_Click(sender As Object, e As RoutedEventArgs) Handles btnExit.Click
Me.Close()
End Sub
and I have the following in the window closing event:
Application.Current.MainWindow.Show()
But how do I differentiate if the exit button is pressed or the continue button or the default close button in the top right of every window? if the exit button or close button is pressed, I would like to close the current window and then show the main window. If the continue button is pressed, I would like to close the current window and then show the next window.
I hope this makes sense.
I am in the belief that this old question holds your answer:
How to catch the event of the window close button(red X button on window right top corner) in wpf form?
This shows an event on Window_Closing that may be what you are looking for.
Hope that helps!

Allow a user to override a default action via an event (eg use of e.handled or e.cancel = true or false)

I have a WPF Custom control which I have been building as I happen to need a particular functionality and I want to learn how to do this properly. Most of the control has now come together nicely and is generally pretty usable. There is now one feature that I'd like to implement, however, I'm not sure if it's possible in the first place or if it is how I would or should go about implementing it.
So here goes by way of my explanation of what I have and what I would like. Feel free to ask for extra clarification if required.
My control raises a custom event which passes along some custom event args. The event is declared like so in the control.
Public Event RecordControlButtonClicked(sender As Object, e As VtlDataNavigatorEventArgs)
In most cases when this event is raised the end user has simply clicked the button, nothing other than raising the event will take place because the developer using the control will most likely want to decide how they handle the situation at that particular moment and the custom event args provide more than enough info to do that.
There are however a couple of buttons where in all honesty the developer will probably want to happen what I (as the developer of the control) envisage should happen most of the time but on the odd occasion might not and therefore needs the opportunity to cancel out of it. So what I'm wondering is how I might achieve the following:
The end user clicks a button
The control button raises my custom event
The developer using my custom controls decides they wish to ignore what I (the creator of the control) thinks should happen, so they do something like e.handled = true in the code where they handle my custom event.
Somehow that message gets back to the button that raised the event, it seems that it's been 'handled' or 'canceled' and as a result does nothing
My question is how do I create that sort of functionality and is the event that I currently have declared the correct sort of event to handle this?
Well just in case anyone stumbles across this looking for an answer.
It transpires that my basic mistake lay in the way that I'd Created my custom events class. Originally I'd followed several examples that I'd read about and had my custom events inherit from EventArgs;
Public Class MyCustomEventArgs
Inherits EventArgs
End Class
What I should have done was have it inherit from CancelEventArgs;
Public Class MyCustomEventArgs
Inherits System.ComponentModel.CancelEventArgs
End Class
That provides the Property that I was after. Now when the event is actually handled upstreap the developer can add an e.cancel = true and all I need to do back in the originating call to raise the event is listen out for e.cancel ;
RaiseEvent MyCustomEvent(sender, e)
if e.cancel = true
'presumably don't do something
Else
'do what needs to be done
End If

Ookii.Dialogs - making sure another dialog appears above Progress dialog

If you're unfamiliar with Ookii.Dialogs, I suggest you look at this web page first. It is open source and you can find source code, compiled binary, documentation and sample app as a download there.
In my application, I'm using Ookii.Dialogs.Wpf.ProgressDialog to ShowDialog(this) a progress dialog that does some processing on files (this is always the application's main window). As expected, the progress dialog takes about a second before it actually becomes visible (even if it is already doing the processing of my files).
In the DoWork thread of the progress dialog, I'm also checking whether the output files already exist and asking the user whether to overwrite each file or skip the output. I use Ookii.Dialogs.Wpf.TaskDialog to ShowDialog(this) a "Task Dialog with Command Links" (looks like this) and ask the user the overwrite question -- except when the OS doesn't support it, I fall back to a regular MessageBox (the problem applies to the message box as well).
The problem occurs when my application finds an existing file right at the beginning of the progress dialog's DoWork thread. When the task dialog appears asking user whether to overwrite:
Expected behavior: The task dialog must stay on top. When the progress dialog appears (after 1s delay), it must appear behind the task dialog.
Actual behavior: The task dialog does not stay on top. When the progress dialog appears after 1s delay), it appears on top of the task dialog.
The actual behavior does not occur in subsequent overwrite requests when the progress dialog is already visible. The task dialog appears correctly on top of the progress dialog for the subsequent ones, although the user can switch back and forth the two dialogs (just can't switch to the main window from either of them).
I'm looking for this:
Best solution: Make the task dialog appear modal on the progress dialog. If the user tries to switch to progress dialog, user must not be allowed to do so.
Second best: Make the first appearance of task dialog remain on top even when the progress dialog appears after the 1s delay.
I'm not looking for the following:
Set the always-on-top flag of task dialog. I don't want the task dialog to appear always-on-top of every window on user's computer.
Add a delay before task dialog's appearance. I have tried Thread.Sleep() even in a loop, it simply hangs the execution of everything and doesn't solve the problem.
Wait till the progress dialog appears before showing the task dialog. Could work in theory, except I didn't find a way to know if the progress dialog has appeared or not.
Make the dialogs modeless. I want them both to be modal. (Besides I have tried making them modeless; it doesn't help solve the problem.)
A solution that works only for the task dialog. It must work for the regular message box as well.
I know this may a little late coming but I just pre-empted a similar problem in my own program and it reminded me of this question.
I used the windows FindWindowEx API function against the task dialogues window title property to find the dialogues window handle, then used my own class to make that an Iwin32Window which can then be used as a parent for a message box or task-dialogue.
the appropriate code is below (VB.NET)
'allows us to use the handle as a window
class window
Implements IWin32Window
private _handle
public sub new(handle as intptr)
_handle = handle
end sub
Public ReadOnly Property Handle As System.IntPtr Implements System.Windows.Forms.IWin32Window.Handle
Get
Return _h
End Get
End Property
end class
'Declare the needed DLL import
class NativeMethods
<DllImport("user32.dll", SetLastError:=True, ThrowOnUnmappableChar:=True, CharSet:=CharSet.Unicode, bestFitMapping:=False)>
Public Shared Function FindWindowEx(hwndParent As IntPtr, hwndChildAfter As IntPtr, lpszClass As String, lpszWindow As String) As IntPtr
End Function
end class
'Make sure you've set a window title when you run your task
sub runTask()
dim myDlg as Ookii.Dialogs.ProgressDialog
'Do what you need to here
myDlg.WindowTitle = "make sure you set a title"
end sub
sub myTask(sender as object, e As System.ComponentModel.DoWorkEventArgs)
'Checks and balances here
if fileExists then
'this is the dialog that's running our task
Dim dlg As Ookii.Dialogs.ProgressDialog = DirectCast(sender, Ookii.Dialogs.ProgressDialog)
'Find the window handle
Dim dlgHandle As IntPtr = NativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, Nothing, dlg.WindowTitle)
'make it an Iwin32Window
dim dlgWindow as new window(dlgHandle)
'That can then be used as a parent for the message box or the task dialog
MessageBox.Show(dlgWindow,"The file exists, overwrite?")
end if
end sub
I'm not really very good at commenting my code or making my explanations understood so If you have any questions about what's going on I'll try and help.
Today I've read the question and found a solution for me. Tried it for TaskDialog, and this ist working. Didn't try with other dialogs.
I wrote a DialogControler to deal with MVVM.
This DialogControlar has a property called 'Owner' which contains the WPF-MainWindow.
Then I use this for invoking (in my case I've been coming from a backgroundthread in the viewmodel) and also set this in ShowDialog.
My Code:
using (var dialog = new Ookii.Dialogs.Wpf.TaskDialog())
{
dialog.WindowTitle = "My title";
dialog.MainInstruction = "My text";
var okButton = new Ookii.Dialogs.Wpf.TaskDialogButton(Ookii.Dialogs.Wpf.ButtonType.Ok);
dialog.Buttons.Add(okButton);
Owner.Dispatcher.Invoke(new Action(() => dialog.ShowDialog(Owner)));
}
With this I got a topmost window.

DataGridView NullReferenceException on InitializeEditingControlValue after losing focus

I have a DataGridView bound to a List[of Parts]. The last item in the list is always a 'dummy part', which is there to create an extra row in the grid so the user can start typing into the first cell to add a part to the list.
As soon as the user types more than x characters into that particular cell, a new DataGridView with search results appears under the cell where he is typing (a bit look Google's suggestions), and he can select one of the matches to add the part to the list.
I do this by handling the EditControlShowing event, which assigns the 'editing control' to a TextBox variable which has a handler for TextChanged.
The bug:
I want the focus to change to the second DataGridView (search results) if the user hits the down arrow key.
The EditControlShowing handler works fine first time, moving the focus to the second DataGridView and leaves the first DataGridView in a 'seemingly' functional state, i.e. all other event handlers work. But the second time the EditControlShowing handler fires, the DataGridView throws the NullReferenceException on InitializeEditingControlValue.
Because it is the DataGridView's own call which throw the error, the debugger brings me to "Application.Run" in my code, which isn't helpful.
However, I determined that the call which causes the DataGridView to go into that state, and the point at which exception is thrown, is when I steal focus from the first DataGridView by calling Focus() on the second DataGridView (or any other control for that matter).
If however I let the user change focus to the DataGridView (e.g. by clicking with the mouse), then the DataGridView stays fine.
Does anyone know why stealing focus away from a DataGridView during a handler for EditControlShowing should result in it behaving differently afterwards?
I've tried CancelEdit, Refresh, Enable/Disable etc...
Any suggestions on helping me find out what is going on within the DataGridView? I can't spot anything obvious by inspecting it at breakpoints before and after. I don't even know how to find out what object is null as its the DataGridView's own code...
Many thanks.
OK, I resolved this using BeginInvoke in the TextChanged event handler (code is in Boo but should make sense):
//The event handler
private def CellAsTextBoxTextChanged(sender as object, e as EventArgs):
...
self.BeginInvoke(ShowPartSelectionArea, currentCell, _CellAsTextBox.Text)
//The method which amongst other things, sets the focus to another control
private def ShowPartSelectionArea(currentCell as DataGridViewCell, searchString as string):
...
AnotherDataGridView.Focus()
...
I had previously tried using BeginInvoke directly at the point of calling Focus() on the other control, like so:
private def CellAsTextBoxTextChanged(sender as object, e as EventArgs):
...
ShowPartSelectionArea(currentCell, _CellAsTextBox.Text)
private def ShowPartSelectionArea(currentCell as DataGridViewCell, searchString as string):
...
self.BeginInvoke(AnotherDataGridView.Focus())
...
But the latter didn't work, perhaps because the first way actually allows the DataGridView's cell to finish everything it does whereas the second way doesn't.
So for anyone facing the same problem, I'd advise playing about with where in the call stack you use BeginInvoke.

Blacklight Expander problem while expanding programmatically

I am doing a silverlight project in Silverlight 4 and I included the BlackLight project in my project so that I could use their new controls, especialy the dockpanel and the autoexpander, which is causing me at the moment some little problems.
What I would like to do is to have several auto-expander that will expand or collapsed when I click on button. In my case more specifically, each auto-expander have a set of parameter to fill, which in turn will fill the other expander and the current one would collaps and the just filled one would expand.
The idea is simple, yet when I use a button which is on one of my expander, it only works once... It would expand/collapse the first time, then after that, nothing. I trace the code and it seems to go through just fine, but the property value won't change
Here is my code
Private Sub BtnExpand_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles BtnExpand.Click
ClientExpander.IsExpanded = False
ProjetExpander.IsExpanded = True
End Sub
Could it be a known bug, or must I reset some flags to make it work?
Edit : Forgot to mention, if it makes any difference, but the IsExpanded property seems to be a dependancy property.
Thanks.

Resources