示例#1
0
        ///<summary>Non-blocking call. Every type of progress window should eventually call this method which does the hard stuff for the calling method.
        ///Spawns a separate thread that will instantiate a FormProgressBase within the new thread's context by invoking the func passed in.
        ///The FormProgressBase that funcGetNewProgress returns should be a form that is instatiated within the func (in order to avoid cross-threading).
        ///The global static FormProgressCurS will get set to the newly instantiated progress so that the entire application knows progress is showing.
        ///Finally returning a close action for the calling method to invoke whenever long computations are finished.
        ///Two critical portions of the closing method are 1 - it closes progress gracefully and 2 - FormProgressCurS gets set to null.</summary>
        public static Action ShowProgressBase(Func <FormProgressBase> funcGetNewProgress, string threadName = "Thread_ODProgress_ShowProgressBase")
        {
            if (ODEnvironment.IsWindows7(false) || ODInitialize.IsRunningInUnitTest)
            {
                return(new Action(() => {
                    //Do nothing.
                }));
            }
            FormProgressBase FormPB      = null;
            ManualResetEvent manualReset = new ManualResetEvent(false);
            ODThread         odThread    = new ODThread(o => {
                //It is very important that this particular thread instantiates the form and not the calling method.
                //This is what allows the progress window to show and be interacted with without joining or invoking back to the parent thread.
                FormPB = funcGetNewProgress();
                AddActiveProgressWindow(FormPB);                //Let the entire application know that a progress window is showing.
                FormPB.Shown      += (obj, eArg) => manualReset.Set();
                FormPB.FormClosed += (obj, eArg) => RemoveActiveProgressWindow(FormPB);
                FormPB.ShowDialog();                //We cannot utilize the "owner" overload because that would cause a cross threaded exception.
            });

            odThread.SetApartmentState(ApartmentState.STA);            //This is required for ReportComplex due to the history UI elements.
            odThread.AddExceptionHandler(e => e.DoNothing());          //The progress window had an exception... Not worth crashing the program over this.
            odThread.Name = threadName;
            odThread.Start();
            //Force the calling thread to wait for the progress window to actually show to the user before continuing.
            manualReset.WaitOne();
            return(() => {
                FormPB.ForceClose = true;
                odThread.Join(Timeout.Infinite);
            });
        }
示例#2
0
 private static void RemoveActiveProgressWindow(FormProgressBase formPB)
 {
     _lockProgressCur.EnterWriteLock();
     try {
         _listActiveProgressForms.Remove(formPB);
     }
     finally {
         _lockProgressCur.ExitWriteLock();
     }
 }
示例#3
0
        ///<summary>Invokes one of the funcs passed in based on if there are any active progress windows showing and has focus.
        ///Will invoke funcShowProgress if a progress window is active.  Otherwise; invokes funcShow.
        ///Recursively calls itself as needed if the active progress window was in the middle of closing when this method was invoked.</summary>
        ///<param name="funcShowOverProgress">The func that should execute if a progress window is currently showing to the user.</param>
        ///<param name="funcShow">The func that should execute if no progress window is currently showing to the user.</param>
        ///<returns>The dialog result from the func that ended up getting invoked.</returns>
        private static System.Windows.Forms.DialogResult ShowHelper(Func <FormProgressBase, System.Windows.Forms.DialogResult> funcShowOverProgress,
                                                                    Func <System.Windows.Forms.DialogResult> funcShow)
        {
            //Unit tests are not designed to display message boxes.
            //Throw an exception instead of displaying the message so that unit tests cannot get locked up.
            if (ODInitialize.IsRunningInUnitTest)
            {
                throw new ApplicationException("Message boxes are not allowed for unit tests.");
            }
            //Get the active form for the current application.  This property will return null if another application has focus (not our application).
            //This is rare enough that it is acceptable to default the parent of the message box to the progress window (if one is present).
            //This may cause the MessageBox to show up behind a form that HAD focus with a progress window behind it (e.g. Registration Key Edit window).
            Form FormActive = Form.ActiveForm;
            //Get the "active" progress window if one is present.  Utilize a shallow copy so race conditions don't affect us inadvertently.
            FormProgressBase FormPB = ODProgress.FormProgressActive;

            //So that the logic is easier to follow, check for the two scenarios that can cause an immediate kick out.
            if (FormPB == null ||                                     //The easiest scenario is when there is no active progress window.
                (FormActive != null && FormActive != FormPB))         //There is a progress window but there is another form owned by the application that has focus.
            {
                //There is no progress window showing or there is one showing but we know that a different window of our application has focus.
                return(funcShow());               //Show the message box like normal and don't override its Parent property with the progress window.
            }
            //There is a progress window present and it could be the active form for the application or another application has focus and we don't know.
            //It is rare enough for applications to leave progress windows open while showing dialogs or new forms to the user.
            //Default to forcing the active progress window to be the parent form of the new message box because that scenario is so rare.
            System.Windows.Forms.DialogResult dialogResult = System.Windows.Forms.DialogResult.Abort;
            try {
                FormPB.InvokeIfRequired(() => dialogResult = funcShowOverProgress(FormPB));
            }
            catch (ObjectDisposedException ode) {
                //Explicitly catch object disposed exceptions due to rare race conditions.
                //The active progress window that was just showing could have been placed on the "invoke stack" for close and disposal prior to our invoke.
                //The active progress window would successfully close and dispose first and then FormPB would be a reference to a disposed window
                //which would cause .InvokeIfRequired() to throw an ObjectDisposedException.
                //No error should be thrown in this scenario and instead we should retry this method because the active progress window could be different.
                //E.g. there will be a different active progress window if multiple progress windows were showing at the same time
                //OR we we eventually get back to the main thread which will not require invoking over to a progress window at all.
                ode.DoNothing();
                dialogResult = ShowHelper(funcShowOverProgress, funcShow);             //Recursive call on purpose.
            }
            return(dialogResult);
        }