在C#可滚动容器中调整大小时控制对齐 [英] Control snapping on resize in C# scrollable container

查看:94
本文介绍了在C#可滚动容器中调整大小时控制对齐的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组控件,它们垂直堆叠在可滚动控件中。

I have a set of controls which I am stacking vertically inside a scrollable control.

每个控件都包含文本(例如iPhone上的消息气泡),气泡

Each control contains text (like message bubbles on an iPhone), which the bubble resizes based on the height of the text.

我面临的问题是,当我调整父对象的大小使其变小时,气泡开始重叠,并且当我调整大小时所以气泡是一条线,每个气泡之间有太多空间。

The problem I face, is when I resize the parent so it is smaller, the bubbles start to overlap, and when I resize so the bubbles are one-line, there is too much space in between each bubble.

我想做的是让每个气泡卡在气泡顶部将气泡从其上方的气泡中抽出10pts,这是最快的方法,不会出现闪烁(由于当前没有调整大小的闪烁)

What I would like to do, is to have each bubble snap the top of the bubble to 10pts off the bubble above it, the fastest way possible without any flicker (as there is presently no flicker on resize)

我已经考虑过将每个控件嵌入另一个控件中父级(例如,网格控件行),但随后添加的每个气泡将负责调整其自身父级的大小,然后锚点将不再对其顶部,左侧和右侧位置起作用。

I have thought about embedding each control into another parent (eg, a grid control row), but then each bubble added would be responsible for resizing the parent of itself, and then anchors would no longer work for their top, left, and right positioning.

该怎么做? (对不起,该问题的详细信息已在上方,因为由于其复杂性和特定性,它实际上无法用简单的单词表达出来)

How can this be done ? (sorry, the details of the question are above as it can't really be worded into a simple one liner question due to the complexity and specifics)

在此先感谢: )

根据要求,屏幕快照和代码

通常这是视图b $ b
调整大小后,向下滚动到不在可见段

调整大小,然后向上滚动

This is the view normally After Resizing, then scrolling down to controls that weren't in visible segment And resizing back, then scrolling back up

现在好了东西.....代码.....

Now the good stuff..... CODE.....

这是我的自定义控件的代码:

Here is the code for my custom control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

public class MessageControl : ScrollableControl {

    public List<Message> Messages { get; private set; }

    private Color _LeftBubbleColor=Color.FromArgb(217,217,217);
    private Color _RightBubbleColor=Color.FromArgb(192,206,215);
    private Color _LeftBubbleTextColor=Color.FromArgb(52,52,52);
    private Color _RightBubbleTextColor=Color.FromArgb( 52, 52, 52 );
    private bool _DrawArrow=true;
    private int _BubbleIndent=40;
    private int _BubbleSpacing=10;
    public enum BubblePositionEnum { Left, Right }

    public Color LeftBubbleColor { get { return _LeftBubbleColor; } set {_LeftBubbleColor = value; } }
    public Color RightBubbleColor { get { return _RightBubbleColor; } set { _RightBubbleColor=value; } }
    public Color LeftBubbleTextColor { get { return _LeftBubbleTextColor; } set { _LeftBubbleTextColor=value; } }
    public Color RightBubbleTextColor { get { return _RightBubbleTextColor; } set { _RightBubbleTextColor=value; } }
    public int BubbleIndent { get { return _BubbleIndent; } set { _BubbleIndent = value; } }
    public int BubbleSpacing { get { return _BubbleSpacing; } set { _BubbleSpacing=value; } }
    public bool DrawArrow { get { return _DrawArrow; } set { _DrawArrow = value; } }

    public MessageControl() {
        Messages = new List<Message>();
        SetStyle( ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer|ControlStyles.ResizeRedraw|ControlStyles.SupportsTransparentBackColor|ControlStyles.UserPaint, true );
        DoubleBuffered=true;
        BackColor=Color.Orange;
        Anchor=AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Bottom;
        AutoScroll=true;
    }

    public void Remove( Message message ) {
        this.Invalidate();
        Messages.Remove( message );
        RedrawControls();
    }

    public void Remove( Message[] messages ) {
        this.Invalidate();
        foreach ( Message m in messages ) {
            Messages.Remove( m );
        }
        RedrawControls();
    }

    public void Add( string Message, BubblePositionEnum Position ) {
        Message b = new Message(Position);

        if ( Messages.Count>0 ) {
            b.Top = Messages[Messages.Count-1].Top + Messages[Messages.Count-1].Height + _BubbleSpacing+AutoScrollPosition.Y;
        } else {
            b.Top = _BubbleSpacing+AutoScrollPosition.Y;
        }

        b.Text = Message;
        b.DrawBubbleArrow=_DrawArrow;

        if ( VerticalScroll.Visible ) {
            b.Width=Width-( _BubbleIndent+_BubbleSpacing+SystemInformation.VerticalScrollBarWidth );
        } else {
            b.Width=Width-( _BubbleIndent+_BubbleSpacing );
        }
        if ( Position==BubblePositionEnum.Right ) {
            b.Left = _BubbleIndent;
            b.BubbleColor = _RightBubbleColor;
            b.ForeColor = _RightBubbleTextColor;
        } else {
            b.Left = _BubbleSpacing;
            b.BubbleColor=_LeftBubbleColor;
            b.ForeColor=_LeftBubbleTextColor;
        }

        Messages.Add(b);
        this.Controls.Add(b);
    }

    protected override void OnResize( System.EventArgs e ) {
        RedrawControls();
        base.OnResize( e );
    }

    private void RedrawControls() {
        int count=0;
        Message last=null;
        int new_width=this.Width;
        SuspendLayout();
        foreach ( Message m in this.Controls ) {
            if ( count>0 ) {
                m.Top=last.Top+last.Height+_BubbleSpacing+AutoScrollPosition.Y;
                if ( VerticalScroll.Visible ) {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing+SystemInformation.VerticalScrollBarWidth );
                } else {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing );
                }
            }
            last=m;
            count++;
        }
        ResumeLayout();
        Invalidate();
    }

    public class Message : Control {
        private GraphicsPath Shape;
        private Color _TextColor=Color.FromArgb( 52, 52, 52 );
        private Color _BubbleColor=Color.FromArgb( 217, 217, 217 );
        private bool _DrawBubbleArrow=true;
        private BubblePositionEnum _BubblePosition = BubblePositionEnum.Left;

        public override Color ForeColor { get { return this._TextColor; } set { this._TextColor=value; this.Invalidate(); } }
        public BubblePositionEnum BubblePosition { get { return this._BubblePosition; } set { this._BubblePosition=value; this.Invalidate(); } }
        public Color BubbleColor { get { return this._BubbleColor; } set { this._BubbleColor=value; this.Invalidate(); } }
        public bool DrawBubbleArrow { get { return _DrawBubbleArrow; } set { _DrawBubbleArrow=value; Invalidate(); } }
        public Message(BubblePositionEnum Position) {
            _BubblePosition=Position;
            SetStyle( ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer|ControlStyles.ResizeRedraw|ControlStyles.SupportsTransparentBackColor|ControlStyles.UserPaint, true );
            DoubleBuffered=true;
            Size=new Size( 152, 38 );
            BackColor=Color.Transparent;
            ForeColor=Color.FromArgb( 52, 52, 52 );
            Font=new Font( "Segoe UI", 10 );
            Anchor=AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right;
        }

        protected override void OnResize( System.EventArgs e ) {
            Shape=new GraphicsPath();

            var _Shape=Shape;
            if ( BubblePosition==BubblePositionEnum.Left ) {
                _Shape.AddArc( 9, 0, 10, 10, 180, 90 );
                _Shape.AddArc( Width-11, 0, 10, 10, -90, 90 );
                _Shape.AddArc( Width-11, Height-11, 10, 10, 0, 90 );
                _Shape.AddArc( 9, Height-11, 10, 10, 90, 90 );
            } else {
                _Shape.AddArc( 0, 0, 10, 10, 180, 90 );
                _Shape.AddArc( Width-18, 0, 10, 10, -90, 90 );
                _Shape.AddArc( Width-18, Height-11, 10, 10, 0, 90 );
                _Shape.AddArc( 0, Height-11, 10, 10, 90, 90 );
            }
            _Shape.CloseAllFigures();

            Invalidate();
            base.OnResize( e );
        }

        protected override void OnPaint( PaintEventArgs e ) {
            base.OnPaint( e );
            Bitmap B=new Bitmap( this.Width, this.Height );
            Graphics G=Graphics.FromImage( B );

            SizeF s=G.MeasureString( Text, Font, Width-25 );
            this.Height=(int)( Math.Floor( s.Height )+10 );

            B=new Bitmap( this.Width, this.Height );
            G=Graphics.FromImage( B );
            var _G=G;

            _G.SmoothingMode=SmoothingMode.HighQuality;
            _G.PixelOffsetMode=PixelOffsetMode.HighQuality;
            _G.Clear( BackColor );

            // Fill the body of the bubble with the specified color
            _G.FillPath( new SolidBrush( _BubbleColor ), Shape );
            // Draw the string specified in 'Text' property
            if ( _BubblePosition==BubblePositionEnum.Left ) {
                _G.DrawString( Text, Font, new SolidBrush( ForeColor ), new Rectangle( 13, 4, Width-25, Height-5 ) );
            } else {
                _G.DrawString( Text, Font, new SolidBrush( ForeColor ), new Rectangle( 5, 4, Width-25, Height-5 ) );
            }

            // Draw a polygon on the right side of the bubble
            if ( _DrawBubbleArrow==true ) {
                if(_BubblePosition == BubblePositionEnum.Left) {
                    Point[] p = {
                        new Point(9, 9),
                        new Point(0, 15),
                        new Point(9, 20)
                   };
                    _G.FillPolygon( new SolidBrush( _BubbleColor ), p );
                    _G.DrawPolygon( new Pen( new SolidBrush( _BubbleColor ) ), p );
                } else {
                    Point[] p = {
                        new Point(Width - 8, 9),
                        new Point(Width, 15),
                        new Point(Width - 8, 20)
                    };
                    _G.FillPolygon( new SolidBrush( _BubbleColor ), p );
                    _G.DrawPolygon( new Pen( new SolidBrush( _BubbleColor ) ), p );
                }
            }
            G.Dispose();
            e.Graphics.InterpolationMode=InterpolationMode.HighQualityBicubic;
            e.Graphics.DrawImageUnscaled( B, 0, 0 );
            B.Dispose();
        }
    }
}

对于我的清单:

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC Manifest Options
            If you want to change the Windows User Account Control level replace the 
            requestedExecutionLevel node with one of the following.

        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            Specifying requestedExecutionLevel node will disable file and registry virtualization.
            If you want to utilize File and Registry Virtualization for backward 
            compatibility then delete the requestedExecutionLevel node.
        -->
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- A list of all Windows versions that this application is designed to work with. 
      Windows will automatically select the most compatible environment.-->

      <!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>

      <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>

      <!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>

      <!-- If your application is designed to work with Windows 8.1, uncomment the following supportedOS node-->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>

    </application>
  </compatibility>

  <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
  <!-- <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>-->

  <asmv1:application>
    <asmv1:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv1:windowsSettings>
  </asmv1:application>
</asmv1:assembly>

并让表单本身演示对

    int x = 0;
    while ( x<20 ) {
        messageControl1.Add( "Testing", MessageControl.BubblePositionEnum.Right );
        messageControl1.Add( "Testing", MessageControl.BubblePositionEnum.Right );
        messageControl1.Add( "Testing", MessageControl.BubblePositionEnum.Left );
        x++;
    }

该表格已设置为可缩放为DPI(正确,因此更改当您进行编辑以进行测试时,使用清单作为DPI缩放,而不是字体缩放。)

The form is set to scale to DPI (which is correct, so change that when you edit to test, and use my manifest as that is DPI scaling, not Font scaling).

推荐答案

这就是我在我在谈论通过FlowLayoutPanel做到这一点。 在我进行了重大更改后,请仔细查看所有代码。我建议将此代码粘贴到一个空白项目中以进行操作。

Here's what I'm talking about doing it via the FlowLayoutPanel. Take a close look at all of the code as I've made significant changes throughout. I recommend pasting this over a blank project to play with it.

最初加载时的表单:

调整大小后的表单较小:

The form after being resized smaller:

此处的表单现在滚动到底部以显示FlowLayoutPanel已处理完毕重新为我安排一切:

Here's the form now scrolled to the bottom to show that the FlowLayoutPanel has taken care of re-arranging everything for me:

重新编写的代码:

public partial class Form1 : Form
{

    public Form1()
    {
        this.InitializeComponent();
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        this.UpdateStyles();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        for (int x = 0; x < 20; x++)
        {
            messageControl1.Add(x.ToString("00") + ": Testing testing testing ...", MessageControl.BubblePositionEnum.Right);
            messageControl1.Add(x.ToString("00") + ": Testing with variable length strings.  This one is longer!", MessageControl.BubblePositionEnum.Right);
            messageControl1.Add(x.ToString("00") + ": Testing is fun.", MessageControl.BubblePositionEnum.Left);
        }
    }

}

public class MessageControl : FlowLayoutPanel 
{

    public List<Message> Messages { get; private set; }

    private Color _LeftBubbleColor = Color.FromArgb(217, 217, 217);
    private Color _RightBubbleColor = Color.FromArgb(192, 206, 215);
    private Color _LeftBubbleTextColor = Color.FromArgb(52, 52, 52);
    private Color _RightBubbleTextColor = Color.FromArgb(52, 52, 52);
    private bool _DrawArrow = true;
    private int _BubbleIndent = 40;
    private int _BubbleSpacing = 10;
    public enum BubblePositionEnum { Left, Right }

    public Color LeftBubbleColor { get { return _LeftBubbleColor; } set { _LeftBubbleColor = value; } }
    public Color RightBubbleColor { get { return _RightBubbleColor; } set { _RightBubbleColor = value; } }
    public Color LeftBubbleTextColor { get { return _LeftBubbleTextColor; } set { _LeftBubbleTextColor = value; } }
    public Color RightBubbleTextColor { get { return _RightBubbleTextColor; } set { _RightBubbleTextColor = value; } }
    public int BubbleIndent { get { return _BubbleIndent; } set { _BubbleIndent = value; } }
    public int BubbleSpacing { get { return _BubbleSpacing; } set { _BubbleSpacing = value; } }
    public bool DrawArrow { get { return _DrawArrow; } set { _DrawArrow = value; } }

    public MessageControl()
    {
        this.Messages = new List<Message>();
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        this.UpdateStyles();
        this.BackColor = Color.Orange;
        this.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
        this.AutoScroll = true;
    }

    public void Remove(Message message)
    {
        this.Messages.Remove(message);
        this.Controls.Remove(message);   
        this.Invalidate();
        this.Refresh();
    }

    public void Remove(Message[] messages)
    {
        this.SuspendLayout();
        foreach (Message m in messages)
        {
            Messages.Remove(m);
            this.Controls.Remove(m);
        }
        this.ResumeLayout();
        this.Invalidate();
        this.Refresh();
    }

    public void Add(string Message, BubblePositionEnum Position)
    {
        Message b = new Message(this, Message, Position);
        b.DrawBubbleArrow = _DrawArrow;
        b.Width = this.ClientSize.Width;
        Messages.Add(b);
        this.Controls.Add(b);
    }

    protected override void OnLayout(LayoutEventArgs levent)
    {
        this.ResizeMessages();
        base.OnLayout(levent);
    }

    protected override void OnResize(System.EventArgs e)
    {
        this.ResizeMessages();
        base.OnResize(e);
    }

    private void ResizeMessages()
    {
        this.SuspendLayout();
        foreach (Message m in this.Messages)
        {
            m.Width = this.ClientSize.Width - 9;
        }
        this.ResumeLayout();
    }

    public class Message : Control
    {

        private MessageControl _mc;
        private GraphicsPath Shape;
        private Color _TextColor = Color.FromArgb(52, 52, 52);
        private Color _BubbleColor = Color.FromArgb(217, 217, 217);
        private bool _DrawBubbleArrow = true;
        private BubblePositionEnum _BubblePosition = BubblePositionEnum.Left;

        public override Color ForeColor { get { return this._TextColor; } set { this._TextColor = value; this.Invalidate(); } }
        public BubblePositionEnum BubblePosition { get { return this._BubblePosition; } set { this._BubblePosition = value; this.Invalidate(); } }
        public Color BubbleColor { get { return this._BubbleColor; } set { this._BubbleColor = value; this.Invalidate(); } }
        public bool DrawBubbleArrow { get { return _DrawBubbleArrow; } set { _DrawBubbleArrow = value; Invalidate(); } }

        private Message() { }

        public Message(MessageControl mc, string Message, BubblePositionEnum Position)
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
            this.UpdateStyles();

            this._mc = mc;
            this._BubblePosition = Position;
            this.Text = Message;
            this.BubbleColor = (Position == BubblePositionEnum.Right ? mc.RightBubbleColor : mc.LeftBubbleColor);
            this.BackColor = this.BubbleColor;
            this.ForeColor = (Position == BubblePositionEnum.Right ? mc.RightBubbleTextColor : mc.LeftBubbleTextColor);
            this.Font = new Font("Segoe UI", 10);

            this.Size = new Size(152, 38);
            this.Anchor = AnchorStyles.Left | AnchorStyles.Right;
        }

        protected override void OnResize(System.EventArgs e)
        {
            base.OnResize(e);

            Shape = new GraphicsPath();
            if (BubblePosition == BubblePositionEnum.Left)
            {
                Shape.AddArc(9, 0, 10, 10, 180, 90);
                Shape.AddArc(Width - 10 - this._mc.BubbleIndent, 0, 10, 10, -90, 90);
                Shape.AddArc(Width - 10 - this._mc.BubbleIndent, Height - 11, 10, 10, 0, 90);
                Shape.AddArc(9, Height - 11, 10, 10, 90, 90);
            }
            else
            {
                Shape.AddArc(this._mc._BubbleIndent, 0, 10, 10, 180, 90);
                Shape.AddArc(Width - 18, 0, 10, 10, -90, 90);
                Shape.AddArc(Width - 18, Height - 11, 10, 10, 0, 90);
                Shape.AddArc(this._mc._BubbleIndent, Height - 11, 10, 10, 90, 90);
            }

            if (_DrawBubbleArrow == true)
            {
                Point[] p;
                if (_BubblePosition == BubblePositionEnum.Left)
                {
                    p = new Point[] {
                        new Point(9, 9),
                        new Point(0, 15),
                        new Point(9, 20)
                    };                      
                }
                else
                {
                    p = new Point[] {
                        new Point(Width - 8, 9),
                        new Point(Width, 15),
                        new Point(Width - 8, 20)
                    };
                }
                Shape.AddPolygon(p);
            }

            Shape.CloseAllFigures();
            this.Region = new Region(Shape);

            this.Invalidate();
            this.Refresh();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            var G = e.Graphics;
            int RenderWidth = this.Width - 10 - this._mc.BubbleIndent;
            SizeF s = G.MeasureString(Text, Font, RenderWidth);
            this.Height = (int)(Math.Floor(s.Height) + 10);

            G.SmoothingMode = SmoothingMode.HighQuality;
            G.PixelOffsetMode = PixelOffsetMode.HighQuality;
            G.InterpolationMode = InterpolationMode.HighQualityBicubic;

            // Draw the string specified in 'Text' property
            using (SolidBrush brush = new SolidBrush(this.ForeColor))
            {
                if (_BubblePosition == BubblePositionEnum.Left)
                {
                    G.DrawString(Text, Font, brush, new Rectangle(13, 4, RenderWidth, Height - 5));
                }
                else
                {
                    G.DrawString(Text, Font, brush, new Rectangle(this._mc.BubbleIndent + 5, 4, RenderWidth, Height - 5));
                }
            }
        }

    }

}

这篇关于在C#可滚动容器中调整大小时控制对齐的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆