다음을 통해 공유


Avoiding the extra prompt asking the user if they would like to close this window

In this article, I will discuss a technique to deal with a common problem in USD. The following is an IE dialog. This dialog will display when a web page attempts to call window.close() in javascript.  The intent of this message was to let the user know a page was trying to close the browser and to give the opportunity for the user to prevent the action.

2016-07-05_10-54-10

Because USD is hosting the IE browser, you may run into cases where you see this same message. In particular, the email form in CRM will do this when you click send. That is because internally when you click send on the email, it ultimate calls the browsers window.close() function.

The Real Problem

Problem #1 is simply that you have an extra click you must do before you can move on to something else. This extra click takes time and is really meaningless in this context.

Problem #2 is what happens when "No" is chosen. The email may actually display in the draft mode even though it has already been sent. If you then continue to make edits and save, it will produce an error indicating that the email is not in the correct state. Of course it isn't, because the email can't be edited after it has been sent but the user may have forgotten that they actually already sent the email because the display did not update.

Problem #3 is what happens when "Yes" is chosen. If you choose yes, the browser will close and USD makes an attempt to recognize this event and closes the hosted control. Ultimately this would be the desired behavior, however, in some circumstances, the browser closes within the WebBrowser .NET control and the control doesn't recognize it and does not notify USD. Now the next time you do something like calling Navigate action on the control, an invalid handle exception, that is unhandled in .NET will be thrown, which can crash USD. Later versions of USD have improved the stability on this scenario but you would still have problem #1.

Best case scenario is that you only deal with problem #1 in that it is a nuisance that the user must deal with.

An approach to deal with the problem

Utilizing the techniques described in Inserting USD Events into HTML Javascript logic and acting upon it, where I discuss techniques for manipulating javascript to interact with the USD environment, we can work around this issue and make a better user experience.

CRM happens to call a function internally called closeWindow(focus). This function is the one that ultimately calls window.close(). Now, it would be an unsupported thing to actually modify this function in CRM, but we can still use it as a workaround for our scenario by injecting the override script from USD itself. That way we are not changing the CRM code at all and no other clients are affected. It is possible that in the next version of CRM, this function could change or be removed. In that case, we may be defining a function that is no longer used or we may be modifying behavior we no longer want. We could then deactivate the action call or simply delete it. As a result, I would suggest marking this Action Call we plan to create with a note indicating that it should be reviewed after each CRM upgrade. This one would be fairly benign though because the likely worst case scenario is that it does some unnecessary work.

Solving the problem

Within our PageLoadComplete event for our Email control, we can add an action call designed to insert the modified script and override the CRM implementation.

2016-07-05_10-52-17

Now every time a frame loads within the web pages, it will inject the above script. Instead of calling window.close(), our new function will call window.open with the special USD close event syntax. USD will then take care of closing the hosted control. You may, alternatively, call the event syntax and create a custom event for yourself to handle to do some alternate behavior such as closing a panel layout that hosts the email control.  As a result, when the user clicks the send button on the email, the email tab will simply close, just as we intended and we will no longer receive the above prompt.

Comments

  • Anonymous
    August 19, 2016
    Hello, how can i awoid ie dialog when in case control I press system button "Close Case'?
    • Anonymous
      August 26, 2016
      For the Close Case dialog, I would simply use standard CRM methods to modify the button in the ribbon.xml. That is a pretty standard CRM task. I haven't seen anyone use USD for this.
  • Anonymous
    September 22, 2016
    Hi Jayme, Few questions in my mind, 1.) If we only upgrade USD clientset up file & don't upgrade USD Solution at CRM, then should we expectthe better performance2.) if you have CRM 2015 on-prem implementation, can one think toupgrade to USD to recent version at CRMAny Comment ?
    • Anonymous
      September 22, 2016
    1. You can upgrade the USD client without upgrading the solution. You may not be able to take advantage of some new features that required new configuration entities such as Customization Files but the client is designed to work with the old solution as well. Performance is unrelated to the solution on the server.2) The latest solution files require 2016 but you can use the newer client against the older solution.
      • Anonymous
        September 22, 2016
        Hi Jayme,Thanks for your response. I have USD 1.2 Version installed at CRM 2015 (On Prem) as a solution & I have upgraded USD to latest version (i.e. 2.1.1). Now I have to configure caching for users, in order to reduce USD loading time. As described here, https://technet.microsoft.com/en-us/library/dn646861.aspx . Now, do I have to add USD 1.2 Version or 2.1.1 for caching ?
        • Anonymous
          September 23, 2016
          Just one clarification, I have upgraded USD Client version to 2.1.1
        • Anonymous
          October 07, 2016
          Caching hasn't changed since V1, so it doesn't matter. One thing, the latest USD client version is now preventing loading of a 1.0 solution configuration so while it works for a few versions, the team will specifically enforce that the solution will eventually need to be updated as well.
  • Anonymous
    February 03, 2017
    The comment has been removed
    • Anonymous
      February 03, 2017
      I have not found a good way to do this in USD today, however, I have seen a way to removing the X and close options in the menu. Then you can provide a toolbar button to shutdown the application and can include a prompt. The following would need to be added to a custom hosted control. You would have two actions, EnableClose and DisableClose and would call the corresponding functions below. public const int SC_CLOSE = 0xF060; public const int MF_BYCOMMAND = 0; public const int MF_ENABLED = 0; public const int MF_GRAYED = 1; [DllImport("user32.dll")] public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool revert); [DllImport("user32.dll")] public static extern int EnableMenuItem(IntPtr hMenu, int IDEnableItem, int enable); private void EnableClose(RequestActionEventArgs args) { Window w = Application.Current.MainWindow; var hwnd = new WindowInteropHelper(w).Handle; var style = GetClassLong(hwnd, GCL_STYLE); SetClassLong(hwnd, GCL_STYLE, style & ~CS_NOCLOSE); SetCloseButton(true, hwnd); } private void DisableClose(RequestActionEventArgs args) { Window w = Application.Current.MainWindow; var hwnd = new WindowInteropHelper(w).Handle; var style = GetClassLong(hwnd, GCL_STYLE); SetClassLong(hwnd, GCL_STYLE, style | CS_NOCLOSE); SetCloseButton(false, hwnd); } // If "enable" is true, the close button will be enabled (the default state). // If "enable" is false, the Close button will be disabled. void SetCloseButton(bool enable, IntPtr hwnd) { IntPtr hMenu = GetSystemMenu(hwnd, false); if (hMenu != IntPtr.Zero) { EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED)); } }