首页 > 解决方案 > 呈现自定义控件(表单)C# 时出现意外行为

问题描述

介绍

我正在开发具有自定义 3D 形状和 PathGradient 颜色的自定义控件 [此处:表单]。一切都很顺利,甚至我已经实现了我真正想要的

点击查看表格的实际图片形状和颜色

生成此表单的整个代码如下:-

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.ComponentModel;

namespace CustomControls
{
    public class SplashFORM : Form
    {
        [Description("If True then user can close the form by pressing Alt + F4 while it is focused.")]
        public bool CanClose { get; set; } = false;
        private readonly Timer Drawer = new Timer();

        public SplashFORM()
        {
            FormBorderStyle = FormBorderStyle.None;
            StartPosition = FormStartPosition.CenterScreen;
        }

        protected override void OnFormClosing(FormClosingEventArgs FCEArgs)
        {
            if (!CanClose) { FCEArgs.Cancel = true; return; }
            base.OnFormClosing(FCEArgs);
        }
        
        protected override void OnResize(EventArgs e)
        {
            Invalidate();
            base.OnResize(e);
        }

        protected override void OnLoad(EventArgs EArgs)
        {
            if (!DesignMode)
            {
                DrawForm(null, null);
            }

            base.OnLoad(EArgs);
        }

        private void DrawForm(object _, EventArgs __)
        {
            using (Bitmap BackIMG = new Bitmap(Width, Height))
            {
                using (Graphics Gfx = Graphics.FromImage(BackIMG))
                {
                    Gfx.SmoothingMode = SmoothingMode.HighQuality;
                    FillRoundedRectangle(Gfx);

                    foreach (Control C in Controls)
                    {
                        using (Bitmap BitMP = new Bitmap(C.Width, C.Height))
                        {
                            Rectangle Rect = new Rectangle(0, 0, C.Width, C.Height);
                            C.DrawToBitmap(BitMP, Rect);
                            Gfx.DrawImage(BitMP, C.Location);
                        }
                    }

                    SetBitmap(BackIMG, Left, Top, Handle);
                }
            }
        }

        protected override void OnPaint(PaintEventArgs PEArgs)
        {
            if (DesignMode)
            {
                Graphics Gfx = PEArgs.Graphics;
                Gfx.SmoothingMode = SmoothingMode.HighQuality;
                FillRoundedRectangle(Gfx);
            }

            base.OnPaint(PEArgs);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams Params = base.CreateParams;
                Params.ClassStyle = 0x00020000;
                Params.Style |= 0x00020000;
                if (!DesignMode) { Params.ExStyle |= 0x00080000; }
                return Params;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs PEArgs)
        {
            using (Brush GPBrush = Helper.Get_SplashBrush(new Rectangle(0, 0, Width - 1, Height - 1)))
            { PEArgs.Graphics.FillRectangle(GPBrush, ClientRectangle); }
        }

        private void SetBitmap(Bitmap BitMP, int CLeft, int CTop, IntPtr CHndl)
        {
            if (BitMP.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException("The BitMP must be 32ppp with alpha-channel.");
            IntPtr ScrnDC = Win32.GetDC(IntPtr.Zero);
            IntPtr MemDC = Win32.CreateCompatibleDC(ScrnDC);
            IntPtr HBitMP = IntPtr.Zero;
            IntPtr OBitMP = IntPtr.Zero;
            byte OPCity = 255;

            try
            {
                HBitMP = BitMP.GetHbitmap(Color.FromArgb(0));
                OBitMP = Win32.SelectObject(MemDC, HBitMP);
                Win32.Size _Size = new Win32.Size(BitMP.Width, BitMP.Height);
                Win32.Point _PointSource = new Win32.Point(0, 0);
                Win32.Point _TopPos = new Win32.Point(CLeft, CTop);

                Win32.BLENDFUNCTION _Blend = new Win32.BLENDFUNCTION
                {
                    BlendOp = Win32.AC_SRC_OVER,
                    BlendFlags = 0,
                    SourceConstantAlpha = OPCity,
                    AlphaFormat = Win32.AC_SRC_ALPHA
                };

                Win32.UpdateLayeredWindow(CHndl, ScrnDC, ref _TopPos, ref _Size, MemDC, ref _PointSource, 0, ref _Blend, Win32.ULW_ALPHA);
            }
            finally
            {
                Win32.ReleaseDC(IntPtr.Zero, ScrnDC);

                if (HBitMP != IntPtr.Zero)
                {
                    Win32.SelectObject(MemDC, OBitMP);
                    Win32.DeleteObject(HBitMP);
                }

                Win32.DeleteDC(MemDC);
            }
        }

        private GraphicsPath RoundedRect()
        {
            Rectangle _2DSize = new Rectangle(0, 0, Width - 1, Height - 1);
            int Diameter = 50 * 2;
            Size _Size = new Size(Diameter, Diameter);
            Rectangle _Arc = new Rectangle(_2DSize.Location, _Size);
            GraphicsPath _Path = new GraphicsPath();

            _Path.AddArc(_Arc, 180, 90);
            _Arc.X = _2DSize.Right - 50;
            _Path.AddArc(_Arc, 270, 90);
            _Arc.Y = _2DSize.Bottom - 5;
            _Path.AddArc(_Arc, 0, 90);
            _Arc.X = _2DSize.Left - Diameter;
            _Path.AddArc(_Arc, 90, 90);
            _Path.CloseFigure();
            return _Path;
        }

        private void FillRoundedRectangle(Graphics Gfx)
        {
            if (Gfx == null) throw new ArgumentNullException("Graphics supplied is null");
            using (GraphicsPath GPth = RoundedRect())
            {
                var Bnds = new Rectangle(0, 0, Width - 1, Height - 1);
                PointF[] PT = new PointF[]
            {
                new PointF(-50, -50),
                new PointF(Bnds.Right, 0),
                new PointF(Bnds.Right, Bnds.Bottom),
                new PointF(40, Bnds.Bottom),
                new PointF((float)((float)Bnds.Right / 2 - ((float)Bnds.Bottom * 0.15)), (float)((float)Bnds.Bottom / 2 + ((float)Bnds.Bottom * 0.35)))
            };
                Brush GPBrush = Helper.Get_SplashBrush(Bnds);
                //
                Gfx.FillPath(GPBrush, GPth);
                Region = new Region(GPth);
                //using (Brush GPBrush = Helper.Get_SplashBrush(new Rectangle(0, 0, Width - 1, Height - 1)))
                //{ Gfx.FillPath(GPBrush, GPth); }
            }
        }
    }

    internal class Win32
    {
        public const Int32 ULW_COLORKEY = 0x00000001;
        public const Int32 ULW_ALPHA = 0x00000002;
        public const Int32 ULW_OPAQUE = 0x00000004;
        public const byte AC_SRC_OVER = 0x00;
        public const byte AC_SRC_ALPHA = 0x01;

        [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

        [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("user32.dll", ExactSpelling = true)]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern Bool DeleteDC(IntPtr hdc);

        [DllImport("gdi32.dll", ExactSpelling = true)]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern Bool DeleteObject(IntPtr hObject);

        public enum Bool
        {
            False = 0,
            True
        };

        [StructLayout(LayoutKind.Sequential)]
        public struct Point
        {
            public Int32 x;
            public Int32 y;

            public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct Size
        {
            public Int32 cx;
            public Int32 cy;

            public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BLENDFUNCTION
        {
            public byte BlendOp;
            public byte BlendFlags;
            public byte SourceConstantAlpha;
            public byte AlphaFormat;
        }
    }

    internal class Helper
    {
        public static Brush Get_SplashBrush(Rectangle Bnds)
        {
            PointF[] PTsGH;
            Color[] CLRsGH;
            PathGradientBrush PGB;

            PTsGH = new PointF[]
            {
                new PointF(-50, -50),
                new PointF(Bnds.Right, 0),
                new PointF(Bnds.Right, Bnds.Bottom),
                new PointF(40, Bnds.Bottom),
                new PointF((float)((float)Bnds.Right / 2 - ((float)Bnds.Bottom * 0.15)), (float)((float)Bnds.Bottom / 2 + ((float)Bnds.Bottom * 0.35)))
            };

            CLRsGH = new Color[]
            {
                Color.FromArgb(120,40,40),
                Color.FromArgb(60, 100, 40),
                Color.FromArgb(50, 50, 120),
                Color.FromArgb(0, 60, 100),
                Color.FromArgb(240, 120, 20, 40)
            };

            PGB = new PathGradientBrush(PTsGH)
            {
                SurroundColors = CLRsGH,
                CenterColor = Color.FromArgb(160, 124, 20)
            };

            return PGB;
        }

        public enum BrushType
        {
            Linear, Path, Solid
        }

        public enum ShapeType
        {
            Rectangular, Circular, Triangular, SplashSpecial
        }
    }
}

问题

到目前为止一切都很好,但是当我尝试使用此代码更改表单的不透明度时

protected override void OnClick(EventArgs e)
{
    Opacity -= 0.05;
    base.OnClick(e);
}

然后表格变得不透明并且看起来很脏

点击查看丑形形象。

我想使用计时器更改表单的不透明度以显示淡入和淡出效果!

我尝试使用Invalidate(),Update()Refresh()在更改不透明度后的表单上但仍然没有运气:(

有没有办法解决这个问题,或者有什么替代我想要实现的目标?

标签: c#winformscustom-controls

解决方案


推荐阅读