コード例 #1
0
        protected override void OnPaint(PaintEventArgs e)
        {
            //Assume anything Vista or later has the same issues.
            if (IsVistaOrLater())
            {
                //Windows Vista is opportunistic when it comes to wait conditions (e.g. locks, Mutexes, etc)
                //in that it will actually process WM_PAINT messages on the current thread, even though
                //it is supposed to be blocking in a WaitSleepJoin state.  This behaviour can actually
                //break rendering for a couple of reasons:
                //  1. We do custom double-buffering, and it's possible that we could process a paint message
                //     for an image that hasn't actually been rendered to the back buffer yet.
                //  2. The renderer itself accesses the pixel data of the ImageSops, which is a synchronized operation.
                //     In the case where 2 threads try to load the pixel data of an image simultaneously, the renderer can end up
                //     blocking execution on the main UI thread in the middle of a rendering operation.  If we
                //     allow another tile to paint in this situation, it actually causes some GDI errors because
                //     the previous rendering operation has not yet completed.
                if (_isDrawing || _painting)
                {
                    e.Graphics.Clear(Color.Black);

                    //Queue this tile up for deferred painting and return.
                    if (!_tilesToRepaint.Contains(this))
                    {
                        _tilesToRepaint.Add(this);
                    }

                    return;
                }

                //We're about to paint this tile, so remove it from the queue.
                _tilesToRepaint.Remove(this);
            }

            if (_tile.PresentationImage == null)
            {
                DisposeSurface();
            }

            if (this.Surface == null)
            {
                // Make sure tile gets blacked out if there's
                // no presentation image in it
                e.Graphics.Clear(Color.Black);
            }
            else
            {
                this.Surface.WindowID        = this.Handle;
                this.Surface.ContextID       = e.Graphics.GetHdc();
                this.Surface.ClientRectangle = this.ClientRectangle;
                this.Surface.ClipRectangle   = e.ClipRectangle;

                DrawArgs args = new DrawArgs(this.Surface,
                                             new WinFormsScreenProxy(Screen.FromControl(this)),
                                             DrawMode.Refresh);

                _painting = true;

                try
                {
                    _tile.Draw(args);

                    // if an exception was encountered the last time we rendered the buffer, refresh the error text now
                    if (!string.IsNullOrEmpty(_lastRenderExceptionMessage))
                    {
                        // we cannot simply pass the Graphics because we haven't released its hDC yet
                        // if we do, we'll get a "Object is currently in use elsewhere" exception
                        DrawErrorMessage(_lastRenderExceptionMessage, Surface.ContextID, ClientRectangle);
                    }
                }
                catch (Exception ex)
                {
                    Platform.Log(LogLevel.Error, ex, "An error has occured while refreshing the contents of a tile.");

                    var exceptionMessage = ex is RenderingException ? ((RenderingException)ex).UserMessage : ex.Message;

                    // we cannot simply pass the existing Graphics because we haven't released its hDC yet
                    // if we do, we'll get a "Object is currently in use elsewhere" exception
                    DrawErrorMessage(exceptionMessage, Surface.ContextID, ClientRectangle);
                }
                finally
                {
                    e.Graphics.ReleaseHdc(this.Surface.ContextID);

                    _painting = false;
                }
            }

            // Now that we've finished painting this tile, we can process the deferred paint jobs.
            // The code below is self-fulfilling, in that we remove one tile from the queue and
            // invalidate it, causing it to paint.  When it's done painting, it will remove and
            // invalidate the next one, and so on.
            if (IsVistaOrLater() && _tilesToRepaint.Count > 0)
            {
                TileControl tileToRepaint = _tilesToRepaint[0];
                _tilesToRepaint.RemoveAt(0);

                tileToRepaint.Invalidate();
                tileToRepaint.Update();
            }

            //base.OnPaint(e);
        }