/// <summary> /// Renders the tab thumbnail (<paramref name="image" />) using the given dimensions and coordinates and blends it properly with the underlying desktop /// elements. /// </summary> /// <param name="image">Thumbnail to display.</param> /// <param name="opacity">Opacity that <paramref name="image" /> should be displayed with.</param> /// <param name="width">Width of <paramref name="image" />.</param> /// <param name="height">Height of <paramref name="image" />.</param> /// <param name="position">Screen position that <paramref name="image" /> should be displayed at.</param> public void UpdateWindow(Bitmap image, byte opacity, int width, int height, POINT position) { IntPtr windowHandle = User32.GetWindowDC(Handle); IntPtr deviceContextHandle = Gdi32.CreateCompatibleDC(windowHandle); IntPtr bitmapHandle = image.GetHbitmap(Color.FromArgb(0)); IntPtr oldBitmapHandle = Gdi32.SelectObject(deviceContextHandle, bitmapHandle); SIZE size = new SIZE { cx = 0, cy = 0 }; POINT destinationPosition = new POINT { x = 0, y = 0 }; if (width == -1 || height == -1) { // No width and height specified, use the size of the image size.cx = image.Width; size.cy = image.Height; } else { // Use whichever size is smallest, so that the image will be clipped if necessary size.cx = Math.Min(image.Width, width); size.cy = Math.Min(image.Height, height); } // Set the opacity and blend the image with the underlying desktop elements using User32.UpdateLayeredWindow BLENDFUNCTION blendFunction = new BLENDFUNCTION { BlendOp = Convert.ToByte((int) AC.AC_SRC_OVER), SourceConstantAlpha = opacity, AlphaFormat = Convert.ToByte((int) AC.AC_SRC_ALPHA), BlendFlags = 0 }; User32.UpdateLayeredWindow(Handle, windowHandle, ref position, ref size, deviceContextHandle, ref destinationPosition, 0, ref blendFunction, ULW.ULW_ALPHA); Gdi32.SelectObject(deviceContextHandle, oldBitmapHandle); Gdi32.DeleteObject(bitmapHandle); Gdi32.DeleteDC(deviceContextHandle); User32.ReleaseDC(Handle, windowHandle); }
/// <summary> /// Renders the tabs and then calls <see cref="User32.UpdateLayeredWindow" /> to blend the tab content with the underlying window ( /// <see cref="_parentForm" />). /// </summary> /// <param name="cursorPosition">Current position of the cursor.</param> /// <param name="forceRedraw">Flag indicating whether a full render should be forced.</param> public void Render(Point cursorPosition, bool forceRedraw = false) { if (!IsDisposed && _parentForm.TabRenderer != null && _parentForm.WindowState != FormWindowState.Minimized && _parentForm.ClientRectangle.Width > 0) { cursorPosition = GetRelativeCursorPosition(cursorPosition); using (Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb)) { using (Graphics graphics = Graphics.FromImage(bitmap)) { DrawTitleBarBackground(graphics); // Since classic mode themes draw over the *entire* titlebar, not just the area immediately behind the tabs, we have to offset the tabs // when rendering in the window Point offset = _parentForm.WindowState != FormWindowState.Maximized && DisplayType == DisplayType.Classic ? new Point(0, SystemInformation.CaptionButtonSize.Height) : _parentForm.WindowState != FormWindowState.Maximized ? new Point(0, SystemInformation.VerticalResizeBorderThickness - SystemInformation.BorderSize.Height) : new Point(0, 0); // Render the tabs into the bitmap _parentForm.TabRenderer.Render(_parentForm.Tabs, graphics, offset, cursorPosition, forceRedraw); // Cut out a hole in the background so that the control box on the underlying window can be shown if (DisplayType == DisplayType.Classic && (_parentForm.ControlBox || _parentForm.MaximizeBox || _parentForm.MinimizeBox)) { int boxWidth = 0; if (_parentForm.ControlBox) boxWidth += SystemInformation.CaptionButtonSize.Width; if (_parentForm.MinimizeBox) boxWidth += SystemInformation.CaptionButtonSize.Width; if (_parentForm.MaximizeBox) boxWidth += SystemInformation.CaptionButtonSize.Width; CompositingMode oldCompositingMode = graphics.CompositingMode; graphics.CompositingMode = CompositingMode.SourceCopy; graphics.FillRectangle( new SolidBrush(Color.Transparent), Width - boxWidth, 0, boxWidth, SystemInformation.CaptionButtonSize.Height); graphics.CompositingMode = oldCompositingMode; } IntPtr screenDc = User32.GetDC(IntPtr.Zero); IntPtr memDc = Gdi32.CreateCompatibleDC(screenDc); IntPtr oldBitmap = IntPtr.Zero; IntPtr bitmapHandle = IntPtr.Zero; try { // Copy the contents of the bitmap into memDc bitmapHandle = bitmap.GetHbitmap(Color.FromArgb(0)); oldBitmap = Gdi32.SelectObject(memDc, bitmapHandle); SIZE size = new SIZE { cx = bitmap.Width, cy = bitmap.Height }; POINT pointSource = new POINT { x = 0, y = 0 }; POINT topPos = new POINT { x = Left, y = Top }; BLENDFUNCTION blend = new BLENDFUNCTION { // We want to blend the bitmap's content with the screen content under it BlendOp = Convert.ToByte((int) AC.AC_SRC_OVER), BlendFlags = 0, SourceConstantAlpha = 255, // We use the bitmap's alpha channel for blending instead of a pre-defined transparency key AlphaFormat = Convert.ToByte((int) AC.AC_SRC_ALPHA) }; // Blend the tab content with the underlying content if (!User32.UpdateLayeredWindow( Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, ULW.ULW_ALPHA)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Error while calling UpdateLayeredWindow()."); } } // Clean up after ourselves finally { User32.ReleaseDC(IntPtr.Zero, screenDc); if (bitmapHandle != IntPtr.Zero) { Gdi32.SelectObject(memDc, oldBitmap); Gdi32.DeleteObject(bitmapHandle); } Gdi32.DeleteDC(memDc); } } } } }