我有一组控件,我在可滚动控件内垂直堆叠。
每个控件都包含文本(如iPhone上的消息气泡),气泡会根据文本的高度调整大小。
我面临的问题是,当我调整父级的大小以使其更小时,气泡开始重叠,当我调整大小以使气泡为一行时,每个气泡之间的空间太大。
我想做的是让每个气泡将气泡顶部从气泡上方的气泡释放到10分钟,这是最快的方式,没有任何闪烁(因为目前调整大小时没有闪烁)
我已经考虑过将每个控件嵌入到另一个父控件中(例如,一个网格控件行),但是之后添加的每个泡泡都将负责调整其自身的父级,然后锚点将不再适用于它们的顶部,左侧,和正确的定位。
如何做到这一点? (对不起,问题的细节在上面,因为由于复杂性和具体细节,它无法真正用于简单的单线问题)
提前致谢:)
AS REQUESTED,SCREENSHOTS和CODE
这是正常观点 调整大小后,向下滚动到不在可见段中的控件 然后重新调整大小,然后向上滚动
现在好东西..... CODE .....
以下是我的自定义控件的代码:
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缩放,而不是字体缩放)。
答案 0 :(得分:1)
我觉得找到了它。
只需在Redraw函数中添加此行,即可实现仅更新的6个对象
Debug.WriteLine(m.Name + "-" + m.Top + "-" + m.Width);
-10-234
-58-217
-106-217
-154-217
-202-217
-250-217
第一个错误
测试方法中的这一行修复了创建过程
messageControl1.SuspendLayout(); //add
while (x < 20)
{
messageControl1.Add("Testing", MessageControl.BubblePositionEnum.Right);
messageControl1.Add("Testing", MessageControl.BubblePositionEnum.Right);
messageControl1.Add("Testing", MessageControl.BubblePositionEnum.Left);
x++;
}
messageControl1.ResumeLayout(); //add
messageControl1.Invalidate(); //add
正如您所见,滚动就在最后。
第二个错误
看起来隐藏元素有不同的大小,你可以看到调试结果
所以我只保存firt元素高度并分配给每个人。
Debug.WriteLine("------------------------------------------------");
int firstHeight = 0;
foreach (Message m in this.Controls)
{
if (count > 0)
{
Debug.WriteLine(m.Height);
m.Height = firstHeight;
m.Top = last.Top + firstHeight + _BubbleSpacing + AutoScrollPosition.Y;
if (VerticalScroll.Visible)
{
m.Width = new_width - (_BubbleIndent + _BubbleSpacing + SystemInformation.VerticalScrollBarWidth);
}
else
{
m.Width = new_width - (_BubbleIndent + _BubbleSpacing);
}
}
else
{
firstHeight = m.Height;
}
Debug.WriteLine(m.Name + "-" + m.Top + "-" + m.Width);
last = m;
count++;
}
答案 1 :(得分:1)
我在谈论通过FlowLayoutPanel进行此操作。 仔细查看所有代码,因为我在整个过程中都进行了重大更改。我建议将其粘贴到一个空白项目中进行播放。
最初加载时的表单:
缩小尺寸后的表格:
此处的表单现在滚动到底部,以显示FlowLayoutPanel已经为我重新安排了一切:
重新编写的代码:
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));
}
}
}
}
}
答案 2 :(得分:0)
在这种情况下,我创建了一个代表迷宫的网格。
我的表单有一个Panel控件,然后我在计算Top,Left值时创建按钮。对我来说,按钮大小是固定的,你可以使用你的父值。
我为行=&#34; 01&#34;和col =&#34; 02&#34;命名按钮grid0102
。
然后选择位置,大小和文本属性。
最后是BackColor for walls
int buttonSize = 20;
Panel myPanel = (Panel)this.Controls["panelArea"];
string[] myGrid = getGrid(0);
for (int row = 0; row < r; row++)
{
char[] rowChar = myGrid[row].ToCharArray();
for (int col = 0; col < c; col++)
{
Button newButton = new Button();
newButton.Name = "grid" + row.ToString("D3") + col.ToString("D3");
newButton.Location = new Point { X = buttonSize * col, Y = buttonSize * row };
newButton.Size = new Size { Width = buttonSize, Height = buttonSize };
newButton.Text = rowChar[col].ToString();
if (rowChar[col] == '%') newButton.BackColor = Color.Green;
myPanel.Controls.Add(newButton);
Debug.WriteLine(newButton.Location);
}
}
注意已添加
但是如果问题是处理调整大小,只需在代码中放入代码,并在调整Resize事件时调用它。
private void panelArea_Resize(object sender, EventArgs e)
{
UI_Resize();
}