【问题标题】:Create a User Control which uses another User Control创建一个使用另一个用户控件的用户控件
【发布时间】:2017-08-30 04:16:54
【问题描述】:

我有一个用户控件,它充当一个带有SelectedMinValueSelectedMaxMinMax 范围内的TrackBar。

我现在想创建另一个用户控件作为上述用户控件的一种外壳,以便包含悬停标签以显示每个选定值的当前值。

我想在单独的用户控件中执行此操作,因为将所有内容向下移动以为 TrackBar 用户控件中的值标签腾出空间似乎很痛苦。

这是我当前的CustomTrackBar.cs 用户控制。抱歉,有点乱:

public partial class CustomTrackBar : UserControl
{

    #region variables
    private int min = 0;
    private int max = 1000;
    private int selectedMin = 0;
    private int selectedMax;
    private int value;

    private int sliderWidth = 2;
    #endregion

    #region accessors

    public int Min
    {
        get { return min; }
        set { min = value; Invalidate(); }
    }

    public int Max
    {
        get { return max; }
        set { max = value; Invalidate(); }
    }

    public int SelectedMin
    {
        get { return selectedMin; }
        set
        {
            selectedMin = value;
            if (AnythingChanged != null)
            {
                //SelectionChanged(this, null);
                AnythingChanged(this, null);
            }
            MinChanged = true;
            Invalidate();
        }
    }

    public int SelectedMax
    {
        get { return selectedMax; }
        set
        {
            selectedMax = value;
            if (AnythingChanged != null)
            {
                //SelectionChanged(this, null);
                AnythingChanged(this, null);
            }
            MaxChanged = true;
            Invalidate();
            MaxChanged = false;
        }
    }

    public int Value
    {
        get { return value; }
        set
        {
            this.value = value;
            if (AnythingChanged != null)
            {
                //ValueChanged(this, null);
                AnythingChanged(this, null);
            }
            ValueChanged = true;
            Invalidate();
        }
    }

    public int SliderWidth
    {
        get { return sliderWidth; }
        set
        {
            this.sliderWidth = value;
            Invalidate();
        }
    }

    public Boolean MinChanged
    {
        get; private set;
    }
    public Boolean ValueChanged
    {
        get; private set;
    }
    public Boolean MaxChanged
    {
        get; private set;
    }

    #endregion

    //Fired when SelectedMin or SelectedMax changes.
    //public event EventHandler SelectionChanged;

    //Fired when Value changes.
    //public event EventHandler ValueChanged;

    //Fired if anything changes
    public event EventHandler AnythingChanged;


    public CustomTrackBar()
    {
        //Set default values
        Value = (Max - Min) / 2;
        SelectedMax = Max;

        SliderWidth = SliderWidth / (Max - Min); //this line is horrible

        InitializeComponent();



        //Avoid flickering
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

        //Events
        Paint += new PaintEventHandler(CustomTrackBar_Paint);
        MouseDown += new MouseEventHandler(CustomTrackBar_MouseDown);
        MouseMove += new MouseEventHandler(CustomTrackBar_MouseMove);
    }

    //Create a rectangle
    void CustomTrackBar_Paint(object sender, PaintEventArgs e)
    {
        //paint background in white
        e.Graphics.FillRectangle(Brushes.White, ClientRectangle);

        //Create a rectangle object
        Rectangle selectionRect = new Rectangle(
            0,
            0,
            Width,
            Height);

        /*
        Rectangle selectionRect = new Rectangle(
        (SelectedMin - Min) * Width / (Max - Min),
        15,
        (SelectedMax - selectedMin) * Width / (Max - Min),
        Height - 30);
        */

        //paint the rectangle object
        e.Graphics.FillRectangle(Brushes.GreenYellow, selectionRect);

        //Again how does this work?
        Rectangle firstFrameRect = new Rectangle(
            (SelectedMin - SliderWidth/2) * Width / (Max - Min),
            0,
            SliderWidth * Width / (Max - Min),
            Height
            );
        e.Graphics.FillRectangle(Brushes.DarkBlue, firstFrameRect);


        Rectangle lastFrameRect = new Rectangle(
            (SelectedMax - SliderWidth/2) * Width / (Max - Min),
            0,
            SliderWidth * Width / (Max - Min),
            Height
            );

         /*
            Rectangle lastFrameRect = new Rectangle(
            SelectedMax * Width / (Max - Min),
            0,
            5 * Width / (Max - Min),
            Height
            );
         */

        e.Graphics.FillRectangle(Brushes.OrangeRed, lastFrameRect);


        //draw a black frame around our control
        e.Graphics.DrawRectangle(Pens.Black, 0, 0, Width - 1, Height - 1);

        //draw a simple vertical line at the Value position
        e.Graphics.DrawLine(Pens.Black,
            (Value - Min) * Width / (Max - Min) - 5,
            0,
            (Value - Min) * Width / (Max - Min) - 5,
            Height);
    }

    void CustomTrackBar_MouseDown(object sender, MouseEventArgs e)
    {
        //check where the user clicked so we can decide which thumb to move
        int pointedValue = Min + e.X * (Max - Min) / Width;
        int distValue = Math.Abs(pointedValue - Value);
        int distMin = Math.Abs(pointedValue - SelectedMin);
        int distMax = Math.Abs(pointedValue - SelectedMax);
        int minDist = Math.Min(distValue, Math.Min(distMin, distMax));
        if (minDist == distValue)
            movingMode = MovingMode.MovingValue; //this should only move when video plays
        else if (minDist == distMin)
            movingMode = MovingMode.MovingMin;
        else
            movingMode = MovingMode.MovingMax;
        //call this to refreh the position of the selected thumb
        CustomTrackBar_MouseMove(sender, e);
    }

    void CustomTrackBar_MouseMove(object sender, MouseEventArgs e)
    {
        //if the left button is pushed, move the selected thumb
        if (e.Button != MouseButtons.Left)
            return;
        int pointedValue = Min + e.X * (Max - Min) / Width;
        if (movingMode == MovingMode.MovingValue)
        {
            if (pointedValue <= Max && pointedValue >= Min)
                Value = pointedValue;
        }
        else if (movingMode == MovingMode.MovingMin)
        {
            if (pointedValue < SelectedMax && pointedValue >= Min)
                SelectedMin = pointedValue;
        }
        else if (movingMode == MovingMode.MovingMax)
        {
            if (pointedValue > SelectedMin && pointedValue <= Max)
                SelectedMax = pointedValue;
        }
    }


    //To know which thumb is moving
    enum MovingMode { MovingValue, MovingMin, MovingMax }
    MovingMode movingMode;

    public String AllValues()
    {
        return String.Format("{0}\t{1}\t{2}", SelectedMin, Value, SelectedMax);
    }

}

这就是 CustomTrackBar 现在的样子。可以点击和拖动垂直的蓝色、黑色和红色线:

这是我打算让它看起来像的草图:

我真的不知道如何启动新的用户控件。我试图在新的用户控件中创建一个 CustomTrackBar 对象,但这似乎没有绘制任何东西。

另外,创建另一个用户控件以将片段添加到 TrackBar 是否“正确”?我应该坚持到底并在第一个用户控件中做所有事情吗?

谢谢。

【问题讨论】:

    标签: c# forms visual-studio user-controls


    【解决方案1】:

    当您构建的新控件未绘图时,您应该做的第一件事是验证它是否具有正确的非零尺寸并且您没有覆盖绘图代码(您在 CustomTrackBar_Paint 中所做的)。除非您对坐标空间以及您的绘图代码实际在做什么,否则您应该更喜欢合成而不是自定义绘图。

    我所说的组合是指将控件拆分为具有多个内部控件的容器,所有这些控件都已存在于框架中。例如,您可以将UserControl、背景和条形设为Panels,并将数字设为Labels。正确定位和调整它们的大小,当滑块值/最小值/最大值/等发生变化时更新。如果你这样做,你就可以简化你的代码,并且不必接触绘图代码。

    类似这样的:

    public partial class CustomTrackBar : Panel
    {
        private Panel backdrop;
        private Panel minBar;
        private Panel maxBar;
        private Panel currentBar;
        private Label minDisplay;
        private Label maxDisplay;
        private Label currentDisplay;
    
        public CustomTrackBar()
        {
            InitializeComponent(); // This should almost always be first.
            backdrop = new Panel() {
                BackColor = Color.LightGreen,
                // set position, size, etc.
            };
            // add event handlers to backdrop...
            Controls.Add(backdrop);
            // repeat for minBar, maxBar, etc.
    
            SizeChanged += (sender, args) => {
                Update();
            };
    
            // ... remaining initialization logic.
        }
    
        private void Update()
        {
            // adjust the position and size of each inner control...
        }
    }
    

    只有在您完成此操作并衡量了性能影响后,您才应该转向自定义绘图。这将允许您使用工作代码进行增量更改,而不是在黑暗中尝试。

    【讨论】:

    • 这绝对是创建自定义轨迹栏的更好方法。谢谢。
    • 很高兴能帮上忙。
    • 我在将面板放置在其他面板中并确保它们都可见时遇到了困难。我在一个新的用户控件上放置了一个backdropPanel,并在该backdropPanel 中放置了另外3 个panels,它们似乎在下面并且不可见。不知道出了什么问题。
    • 我忘了说:与许多其他框架不同,WinForms 不使用 z-index,而是使用子级的顺序。这意味着孩子的顺序决定了分层。 backdropPanel 出现在顶部,因为它是添加的第一个控件,并且位于第 0 位。较早的控件出现在较晚的控件之前。请参阅下一条评论以获取解决方案。
    • 有几种改变顺序的方法。一种方法是以达到您想要的顺序的方式添加它们。其他更直接的方式包括BringToFrontSendToBackSetChildIndex
    猜你喜欢
    • 2015-10-04
    • 2015-08-03
    • 1970-01-01
    • 2016-01-21
    • 1970-01-01
    • 2013-04-23
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多