/// <summary>
        /// Helper method to read from a web service asynchronously.
        /// </summary>
        public static void OpenReadAsync(Uri uri, object userState, EventHandler <OpenReadEventArgs> callback,
                                         bool forceBrowserAuth = false, string proxyUrl = null)
        {
            System.Diagnostics.Debug.WriteLine(uri.ToString());
            if (m_browserAuthInProgress)
            {
                m_openReadRequests.Enqueue(new RequestInfo(uri, userState, callback, forceBrowserAuth));
            }
            else
            {
                string id           = "hiddenSLViewerAuthFrame";
                bool   iframeExists = HtmlPage.Document.GetElementById(id) != null;

                if (!iframeExists || forceBrowserAuth)
                {
                    m_browserAuthInProgress = true;

                    // The portal may be PKI-secured.  Try injecting an iframe into the HTML DOM to hit the portal endpoint.
                    // In IE, this will force the browser to perform client certificate authentication.  If the initial request
                    // to the portal endpoint fails, the client cert auth will not occur, so we must try this first.

                    // Insert the iframe
                    var iframe         = WebUtil.insertHiddenIFrame("hiddenSLViewerAuthFrame");
                    var loadErrorTimer = new DispatcherTimer()
                    {
                        Interval = TimeSpan.FromSeconds(10)
                    };

                    // Create a handler for the iframe's onload event
                    EventHandler onloadHandler = null;
                    onloadHandler = (o, e) =>
                    {
                        loadErrorTimer.Stop();
                        loadErrorTimer.Tick -= onloadHandler;
                        iframe.DetachEvent("onload", onloadHandler);
                        doOpenRead(uri, userState, callback);
                    };

                    // Register the handler with the iframe's onload event and the load error timer's tick event
                    iframe.AttachEvent("onload", onloadHandler);
                    loadErrorTimer.Tick += onloadHandler;

                    // Set the source of the iframe to make it hit the portal.  Append ticks to circumvent caching.
                    var tempUrl = uri.ToString() + "&r=" + DateTime.Now.Ticks;
                    iframe.SetAttribute("src", tempUrl);

                    // Start the load error timer.  If the timer ticks, the onload handler will be called.  This is needed
                    // in scenarios where the iframe's onload event never fires.
                    loadErrorTimer.Tick += onloadHandler;
                    loadErrorTimer.Start();
                }
                else
                {
                    doOpenRead(uri, userState, callback);
                }
            }
        }