我有一组控件,我将它们垂直堆叠在可滚动控件中。
每个控件都包含文本(如 iPhone 上的消息气泡),气泡会根据文本的高度调整其大小。
我面临的问题是,当我调整父对象的大小使其更小时,气泡开始重叠,当我调整大小使气泡成为一条线时,每个气泡之间的空间太大。
我想做的是让每个气泡将气泡顶部捕捉到其上方气泡的 10 点,这是没有任何闪烁的最快方式(因为目前在调整大小时没有闪烁)
我曾考虑将每个控件嵌入另一个父控件(例如,网格控件行),但是添加的每个气泡都将负责调整其父控件的大小,然后锚将不再适用于它们的顶部、左侧和右侧定位.
如何才能做到这一点 ?(对不起,问题的细节在上面,因为由于复杂性和细节,它不能真正被写成一个简单的单行问题)
提前致谢 :)
根据要求,屏幕截图和代码
这通常是 调整大小后的视图,然后向下滚动到不在可见段中的控件 并调整大小,然后向上滚动
现在好东西......代码......
这是我的自定义控件的代码:
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 缩放,而不是字体缩放)。