admin管理员组

文章数量:1400863

Problem: I'm working on a custom vertical scrollbar control in WinForms. When I move the scrollbar (dragging the slider up and down), the memory usage of the application constantly increases. After I stop interacting with the scrollbar, the memory usage doesn't decrease, indicating a possible memory leak. I'm unsure where the issue lies in my code.

What I've Tried: Detached and reattached event handlers appropriately.

Attempted to dispose of the slider when no longer needed.

Used Invalidate() to refresh the control, but the issue still persists.

Tried handling custom resources (e.g., the slider control) manually to prevent leaks.

Expected Behavior: The memory usage should not increase while interacting with the scrollbar, and it should return to normal once the interaction stops.

Code:

using Chef_s_Codex.CustomUIElements;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Test.Custom
{
    [DefaultEvent("VelChanged")]
    public partial class CustonVScrollBar : UserControl
    {
        public delegate void VelChangedEventHandler(object sender, EventArgs e);
        public event VelChangedEventHandler VelChanged;

        private Color chennelColor = Color.Transparent;
        private Color slederColor = Color.LightGray;
        private Color focusChennelColor = Color.Transparent;
        private Color focusSlederColor = Color.Gray;
        private int chennelWidth = 15;
        private int slederWidht = 15;
        private int slederHeight = 75;
        private int cornerradius = 7;
        private int maxValue = 100;
        private int minValue = 0;
        private int vel = 0;
        private int scaleValue = 5;

        private int locYSlider = 0;
        private bool isMove = false;
        private int locYLast = 0;
        private bool isFocus = false;
        private int deltaY;
        private RoundedPanel slider;

        #region Properties

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Color ChennelColor
        {
            get { return chennelColor; }
            set { chennelColor = value; this.BackColor = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Color SlederColor
        {
            get { return slederColor; }
            set { slederColor = value; if (slider != null) slider.BackColor = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Color FocusChennelColor
        {
            get { return focusChennelColor; }
            set { focusChennelColor = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Color FocusSlederColor
        {
            get { return focusSlederColor; }
            set { focusSlederColor = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int ChennelWidth
        {
            get { return chennelWidth; }
            set { chennelWidth = value; Invalidate(); }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int SlederWidht
        {
            get { return slederWidht; }
            set { slederWidht = value; if (slider != null) slider.Width = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int MaxValue
        {
            get { return maxValue; }
            set { maxValue = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int MinValue
        {
            get { return minValue; }
            set { minValue = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int Vel
        {
            get { return vel; }
            set
            {
                if (value > maxValue) vel = maxValue;
                else if (value < minValue) vel = minValue;
                else vel = value;

                locYSlider = UpdateLocYSlider(vel);
            }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int ScaleValue
        {
            get { return scaleValue; }
            set { scaleValue = value; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int SlederHeight
        {
            get { return slederHeight; }
            set { slederHeight = value; if (slider != null) slider.Height = slederHeight; }
        }

        [Category("Scrollbar")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int CornerRadius
        {
            get { return cornerradius; }
            set { cornerradius = value; }
        }

        #endregion

        public CustonVScrollBar()
        {
            InitializeComponent();

            this.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            roundedPanel1.MouseDown -= CustomScrollbar_MouseDown;
            roundedPanel1.MouseUp -= CustomScrollbar_MouseUp;
            roundedPanel1.MouseMove -= CustomScrollbar_MouseMove;
            roundedPanel1.MouseDown += CustomScrollbar_MouseDown;
            roundedPanel1.MouseUp += CustomScrollbar_MouseUp;
            roundedPanel1.MouseMove += CustomScrollbar_MouseMove;

            if (slederHeight < roundedPanel1.Height)
            {
                if (slider != null)
                {
                    roundedPanel1.Controls.Remove(slider);
                    slider.Dispose();
                    slider = null;
                }

                SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
                slider = new RoundedPanel();
                roundedPanel1.Controls.Add(slider);
                slider.BorderStyle = BorderStyle.None;
                slider.RadiusBottomLeft = 5;
                slider.RadiusBottomRight = 5;
                slider.RadiusTopLeft = 5;
                slider.RadiusTopRight = 5;
                slider.Height = slederHeight;
                slider.Width = slederWidht;
                slider.BackColor = slederColor;
                slider.Location = new Point((roundedPanel1.Width - slederWidht) / 2, 20);
                slider.Enabled = false;
            }

            locYSlider = 1;
            locYLast = 1;
            vel = UpdateValue();
        }

        protected override void CreateHandle()
        {
            base.CreateHandle();
            roundedPanel1.BackColor = chennelColor;
            this.BackColor = this.Parent.BackColor;
        }

        #region Callbacks

        private void CustomScrollbar_MouseMove(object? sender, MouseEventArgs e)
        {
            if (slederHeight >= roundedPanel1.Height || !isMove) return;

            int allowedTop = 1;
            int allowedBottom = roundedPanel1.Height - 1;

            deltaY = e.Y - locYLast;
            if (deltaY == 0) return;

            int newLocYSlider = locYSlider + deltaY;
            newLocYSlider = Math.Clamp(newLocYSlider, allowedTop, allowedBottom - slederHeight);

            if (newLocYSlider == locYSlider) return; // Prevent unnecessary updates

            locYSlider = newLocYSlider;
            locYLast = e.Y;

            vel = UpdateValue();
            OnVelChanged();
        }

        protected virtual void OnVelChanged()
        {
            VelChanged?.Invoke(this, EventArgs.Empty);
        }

        private int UpdateValue()
        {
            int availableHeight = this.Height - slederHeight;

            if (availableHeight <= 0) return minValue; // Prevent division by zero

            double ratio = (double)(locYSlider) / availableHeight;

            int value = minValue + (int)((maxValue - minValue) * ratio);

            slider.Location = new Point(slider.Location.X, locYSlider);

            return Math.Clamp(value, minValue, maxValue);
        }

        private void CustomScrollbar_MouseUp(object? sender, MouseEventArgs e)
        {
            slider.BackColor = slederColor;

            if (slederHeight >= roundedPanel1.Height) return;

            isMove = false;
            isFocus = false;
        }

        private void CustomScrollbar_MouseDown(object? sender, MouseEventArgs e)
        {
            slider.BackColor = focusSlederColor;

            if (slederHeight >= roundedPanel1.Height) return;

            isFocus = true;
            locYLast = e.Y;

            if (e.Button == MouseButtons.Left)
            {
                locYSlider = e.Y - (slederHeight / 2);

                if (locYSlider < 0) locYSlider = 0;
                if (locYSlider > roundedPanel1.Height - slederHeight) locYSlider = roundedPanel1.Height - slederHeight;

                vel = UpdateValue();
                OnVelChanged();

                isMove = true;
            }
        }

        private int UpdateLocYSlider(int valueSlider)
        {
            double d = (double)roundedPanel1.Height / (double)(maxValue - minValue);
            int y = (int)(valueSlider * d);
            if (y > roundedPanel1.Height - slederHeight) y -= slederHeight;
            return y;
        }

        #endregion
    }
}

and here is roundedPanel:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test.Custom
{
    public class RoundedPanel : Panel
    {
        private int radiusTopLeft = 0;
        private int radiusTopRight = 0;
        private int radiusBottomLeft = 0;
        private int radiusBottomRight = 0;

        public bool draw;
        public bool isTest;

        Graphics graphics;

        [Browsable(true)]
        [Category("Corner Radius")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int RadiusTopLeft
        {
            get => radiusTopLeft;
            set { radiusTopLeft = Math.Max(0, value); Invalidate(); }
        }

        [Browsable(true)]
        [Category("Corner Radius")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int RadiusTopRight
        {
            get => radiusTopRight;
            set { radiusTopRight = Math.Max(0, value); Invalidate(); }
        }

        [Browsable(true)]
        [Category("Corner Radius")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int RadiusBottomLeft
        {
            get => radiusBottomLeft;
            set { radiusBottomLeft = Math.Max(0, value); Invalidate(); }
        }

        [Browsable(true)]
        [Category("Corner Radius")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int RadiusBottomRight
        {
            get => radiusBottomRight;
            set { radiusBottomRight = Math.Max(0, value); Invalidate(); }
        }

        public RoundedPanel()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
            this.BorderStyle = BorderStyle.None;
            graphics = this.CreateGraphics();
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
        }

        private GraphicsPath GetFigurePath(RectangleF rect)
        {
            GraphicsPath path = new GraphicsPath();
            path.StartFigure();

            // Top-left corner
            if (RadiusTopLeft > 0)
                path.AddArc(rect.X, rect.Y, RadiusTopLeft * 2, RadiusTopLeft * 2, 180, 90);
            else
                path.AddLine(rect.X, rect.Y, rect.X + 1, rect.Y);

            // Top-right corner
            if (RadiusTopRight > 0)
                path.AddArc(rect.Width - RadiusTopRight * 2, rect.Y, RadiusTopRight * 2, RadiusTopRight * 2, 270, 90);
            else
                path.AddLine(rect.Width - 1, rect.Y, rect.Width, rect.Y + 1);

            // Bottom-right corner
            if (RadiusBottomRight > 0)
                path.AddArc(rect.Width - RadiusBottomRight * 2, rect.Height - RadiusBottomRight * 2, RadiusBottomRight * 2, RadiusBottomRight * 2, 0, 90);
            else
                path.AddLine(rect.Width, rect.Height - 1, rect.Width - 1, rect.Height);

            // Bottom-left corner
            if (RadiusBottomLeft > 0)
                path.AddArc(rect.X, rect.Height - RadiusBottomLeft * 2, RadiusBottomLeft * 2, RadiusBottomLeft * 2, 90, 90);
            else
                path.AddLine(rect.X + 1, rect.Height, rect.X, rect.Height - 1);

            path.CloseFigure();
            return path;
        }

        protected override void OnResize(EventArgs eventargs)
        {
            base.OnResize(eventargs);

            RectangleF rectangleF = new RectangleF(0, 0, this.Width, this.Height + 1);

            if (this.Region != null)
            {
                this.Region.Dispose();
            }

            using (GraphicsPath path = GetFigurePath(rectangleF))
            using (Pen pen = new Pen(this.Parent != null ? this.Parent.BackColor : this.BackColor, 2))
            {

                this.Region = new Region(path);
                graphics.DrawPath(pen, path);

            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.Region != null)
                {
                    this.Region.Dispose();
                    this.Region = null;
                }
            }
            base.Dispose(disposing);
        }
    }
}


本文标签: cCustom Scrollbar memory leakStack Overflow