I have a panel that has a collection of custom drawn controls. I know how to programmically scroll the panel but the problem is how the controls draw when scrolling. when scrolling right the control shows as normal but when scrolling left it is not looking correctly. This just the start of the complete application so I just have some basic testing in place right now. Need to find a way for the control to draw correctly when scrolling.
Form code:
public partial class Form1 : Form
{
Panel pn;
private int location = 0;
public Form1()
{
InitializeComponent();
pn = new Panel()
{
Width = this.ClientRectangle.Width - 20,
Height = 120,
BackColor = Color.Black,
Left = 5,
Top = 20
};
pn.AutoScroll = false;
pn.VerticalScroll.Maximum = 100;
pn.HorizontalScroll.Maximum = this.ClientRectangle.Width - 100;
pn.VerticalScroll.Visible = false;
pn.HorizontalScroll.Visible = false;
pn.AutoScrollPosition = new Point(0, 0);
pn.AutoScroll = true;
this.KeyPreview = true;
this.KeyDown += new KeyEventHandler(keyPress);
for(int i = 0; i<10;i++)
{
CustomControl1 cc = null;
if (i % 2 != 0)
cc = new CustomControl1()
{
isOdd = true,
Width = 100,
Height = 100,
Left = (100*i)+5,
Top = 0
};
else
cc = new CustomControl1()
{
isOdd = false,
Width = 100,
Height = 100,
Left = (100 * i) + 5,
Top = 0
};
pn.Controls.Add(cc);
}
this.Controls.Add(pn);
}
private void keyPress(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.A:
if(location - 20 >0)
{
location -= 20;
pn.HorizontalScroll.Value = location;
}
else
{
location = 0;
pn.AutoScrollPosition = new Point(location, 0);
}
break;
case Keys.D:
if(location +20 < pn.HorizontalScroll.Maximum)
{
location += 20;
pn.HorizontalScroll.Value = location;
}
else
{
location = pn.VerticalScroll.Maximum;
pn.AutoScrollPosition = new Point(location, 0);
}
break;
}
foreach(Control c in pn.Controls)
{
c.Invalidate();
}
}
}
Control Code:
public partial class CustomControl1 : Control
{
public bool isOdd { get; set; }
public CustomControl1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
Graphics gr = pe.Graphics;
Rectangle rc = new Rectangle(pe.ClipRectangle.X,pe.ClipRectangle.Y,100,100);
Brush br = new SolidBrush(Color.Red);
if (isOdd)
br = new SolidBrush(Color.Yellow);
gr.FillEllipse(br, rc);
base.OnPaint(pe);
}
protected override void OnPaintBackground(PaintEventArgs pe)
{
base.OnPaintBackground(pe);
}
}
Don't clip your drawing:
//Rectangle rc = new Rectangle(pe.ClipRectangle.X, pe.ClipRectangle.Y, 100, 100);
Rectangle rc = new Rectangle(0, 0, 100, 100);
I created my custom Tooltip, but what I need is that it will be with the same style as system draws it when you do not switch on the OwnerDraw flag.
How can I create custom tooltip which looks exactly like the "original" one?
ttSessionInfo.ToolTipTitle = UiTranslator.Instance.GetLabel(UiLabels.DC_DSE_Session);
var toolTipSessionsText = sessions.Aggregate(
new StringBuilder(),
(p_strBuilder, p_session) => p_strBuilder.AppendLine(string.Format("{0}: {1}", p_session.SessionName,
p_session.IsConnected ? connectedText : disconnectedText))).ToString();
ttSessionInfo.SetToolTip(LiveUpdatePb, toolTipSessionsText);
Result is:
I need the same tooltip exactly to show on another control, but to paint ,lets say, the second row "Alex Session: Connected" with red color.
I added a sample that re-implements ToolTip class and codes to use it.
class:
class CustomToolTip : ToolTip
{
public CustomToolTip()
{
this.OwnerDraw = true;
this.Popup += new PopupEventHandler(this.OnPopup);
this.Draw += new DrawToolTipEventHandler(this.OnDraw);
}
string m_EndSpecialText;
Color m_EndSpecialTextColor =Color.Red;
public Color EndSpecialTextColor
{
get { return m_EndSpecialTextColor; }
set { m_EndSpecialTextColor = value; }
}
public string EndSpecialText
{
get { return m_EndSpecialText; }
set { m_EndSpecialText = value; }
}
private void OnPopup(object sender, PopupEventArgs e) // use this event to set the size of the tool tip
{
e.ToolTipSize = new Size(200, 100);
}
private void OnDraw(object sender, DrawToolTipEventArgs e) // use this event to customise the tool tip
{
Graphics g = e.Graphics;
LinearGradientBrush b = new LinearGradientBrush(e.Bounds,
Color.GreenYellow, Color.MintCream, 45f);
g.FillRectangle(b, e.Bounds);
g.DrawRectangle(new Pen(Brushes.Red, 1), new Rectangle(e.Bounds.X, e.Bounds.Y,
e.Bounds.Width - 1, e.Bounds.Height - 1));
//g.DrawString(e.ToolTipText, new Font(e.Font, FontStyle.Bold), Brushes.Silver,
// new PointF(e.Bounds.X + 6, e.Bounds.Y + 6)); // shadow layer
g.DrawString(e.ToolTipText, new Font(e.Font, FontStyle.Bold), Brushes.Black,
new PointF(e.Bounds.X + 5, e.Bounds.Y + 5)); // top layer
SolidBrush brush = new SolidBrush(EndSpecialTextColor);
g.DrawString(EndSpecialText, new Font(e.Font, FontStyle.Bold), brush,
new PointF(e.Bounds.X + 5, e.Bounds.Bottom - 15)); // top layer
brush.Dispose();
b.Dispose();
}
}
following the use of above class
private void button1_Click(object sender, EventArgs e)
{
CustomToolTip toolTip1 = new CustomToolTip();
toolTip1.ShowAlways = true;
toolTip1.SetToolTip(button1, "Click me to execute.");
toolTip1.EndSpecialText = "Hello I am special";
}
I have a set of controls which I am stacking vertically inside a scrollable control.
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.
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)
Thanks in advance :)
AS REQUESTED, SCREENSHOTS and CODE
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();
}
}
}
And for my manifest :
<?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>
And for the form itself to demo the control on
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++;
}
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).
I think found it.
Just add this line in the Redraw function to realize only 6 object where being updated
Debug.WriteLine(m.Name + "-" + m.Top + "-" + m.Width);
-10-234
-58-217
-106-217
-154-217
-202-217
-250-217
first bug
And this line in the test method fix the creation process
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
As you can see the scroll is at the end.
second bug
Looks like hide elements have diferent size, you can see the debug result.
So I just save firt element Height and assign to everyone.
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++;
}
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 when it initially loads:
The form after being resized smaller:
Here's the form now scrolled to the bottom to show that the FlowLayoutPanel has taken care of re-arranging everything for me:
The re-worked code:
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));
}
}
}
}
}
In this case I'm creating a grid to represent a labyrinth.
My form have a Panel control, then I create buttons inside calculating Top, Left values. For me button size is fixed, you could use your parent value.
I name the buttons grid0102 for row = "01"and col="02"
Then select Location, Size and Text properties.
And finally 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);
}
}
NOTE ADDED
But if the problem is handling resize, just encampsule that code in a function and call it when Resize event occurs.
private void panelArea_Resize(object sender, EventArgs e)
{
UI_Resize();
}
I've created a custom control in C#. A highly-simplified version of it is shown below:
class WellControl : Control
{
Pen circlePen = new Pen(Color.Black, 5);
Brush wellShader = new SolidBrush(Color.BlueViolet);
Brush wellBlanker = new SolidBrush(Color.LightGray);
public WellControl(int wellNum)
{
InitializeComponent();
}
private void DrawWell()
{
using (Graphics well = this.CreateGraphics())
{
this.Size = new Size(WellSize, WellSize);
if (this.selected)
{
well.FillEllipse(wellShader, ellipseCoords);
}
else
{
well.FillEllipse(wellBlanker, ellipseCoords);
}
well.DrawEllipse(circlePen, ellipseCoords);
using (Font wellNumberFont = new Font("Arial", 14, FontStyle.Bold))
{
well.DrawString(WellNum.ToString(), wellNumberFont, Brushes.Black, new Point(13, 13));
}
}
}
private void WellPaintEventHandler(object sender, EventArgs e)
{
DrawWell();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.Paint += new System.Windows.Forms.PaintEventHandler(WellPaintEventHandler);
this.ResumeLayout();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
//dispose all the custom stuff
}
}
}
When I add it to a form, it renders properly (again, simplified example):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
for (int i = 0; i < 4; i++)
{
WellControl newWell = new WellControl(i + 1);
newWell.Location = new Point(15, 50 * i);
newWell.Size = new System.Drawing.Size(45, 45);
this.Controls.Add(newWell);
}
}
}
I have another custom control, "Plate", which is intended to hold many "Wells". The code intended to draw the wells evenly across the plate probably sucks right now but I'm just trying to see something:
class PlateControl : Control
{
Pen blackPen = new Pen(Color.Black, 3);
public PlateControl()
{
this.Size = new Size(600, 800);
List<WellControl> plateWells = new List<WellControl>();
int column = 1;
int row = 0;
for (int i = 1; i <= 96; i++)
{
column = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(i / 8)));
row = i % 8;
WellControl newWell = new WellControl(i + 1);
newWell.Name = "wellControl" + i;
newWell.Location = new Point(column * 50, row * 50);
newWell.Size = new System.Drawing.Size(45, 45);
newWell.TabIndex = i;
newWell.WellSize = 45;
plateWells.Add(newWell);
newWell.Visible = true;
}
InitializeComponent();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.Paint += new System.Windows.Forms.PaintEventHandler(PlatePaintEventHandler);
this.ResumeLayout();
}
private void DrawPlate()
{
using (Graphics plate = this.CreateGraphics())
{
Point topLeft = new Point(0, 0);
Point topRight = new Point(600, 0);
Point bottomRight = new Point(600, 400);
Point bottomLeft = new Point(0, 400);
plate.DrawLine(blackPen, topLeft, topRight);
plate.DrawLine(blackPen, topRight, bottomRight);
plate.DrawLine(blackPen, bottomRight, bottomLeft);
plate.DrawLine(blackPen, bottomLeft, topLeft);
}
}
private void PlatePaintEventHandler(object sender, EventArgs e)
{
DrawPlate();
}
}
If I add this control to a Winform using this.Controls.Add(new PlateControl()), the rectangle renders, but not the WellControls I added to the PlateControl in the constructor loop. What am I doing incorrectly?
You need to add List of WallControl to plate control.
public PlateControl()
{
this.Size = new Size(600, 800);
List<WellControl> plateWells = new List<WellControl>();
int column = 1;
int row = 0;
for (int i = 1; i <= 96; i++)
{
column = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(i / 8)));
row = i % 8;
WellControl newWell = new WellControl(i + 1);
newWell.Name = "wellControl" + i;
newWell.Location = new Point(column * 50, row * 50);
newWell.Size = new System.Drawing.Size(45, 45);
newWell.TabIndex = i;
newWell.WellSize = 45;
plateWells.Add(newWell);
newWell.Visible = true;
}
this.Controls.AddRange(plateWells.ToArray());
InitializeComponent();
}
I faced with the problem with custom DataGridView. I want to draw a bottom line, but I can't avoid of drawing this line that remain on cells when I scrolling the grid.
public partial class GridTest : DataGridView
{
private static Color _gridColor = Color.FromArgb(219, 225, 232);
private int gridHeight;
private bool resizing;
private const int Xoffset = 5;
private const int Yoffset = 10;
Color cl1 = Color.FromArgb(255, 255, 255);
Color cl2 = Color.FromArgb(229, 233, 238);
Color cl3 = Color.FromArgb(234, 237, 242);
Color cl4 = Color.FromArgb(170, 186, 208);
private Color _clrColumnHeader;
public Color ColorColumnHeader
{
get { return _clrColumnHeader; }
set { _clrColumnHeader = value; }
}
public GridTest()
{
InitializeComponent();
Init();
}
public GridTest(IContainer container)
{
container.Add(this);
InitializeComponent();
Init();
}
private void Init()
{
var _headerHeight = 50;
ColumnHeadersHeight = _headerHeight;
BackgroundColor = Color.White;
RowTemplate.Height += 5;
AllowUserToAddRows = false;
AllowUserToDeleteRows = false;
AllowUserToResizeColumns = false;
AllowUserToResizeRows = false;
AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders);
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal;
RowHeadersVisible = false;
MultiSelect = false;
ReadOnly = true;
AutoGenerateColumns = false;
EnableHeadersVisualStyles = false;
ShowCellErrors = false;
ShowEditingIcon = false;
ShowRowErrors = false;
}
private Blend gradientBlend()
{
float[] myFactors = { 0.0f, .00f, .8f, 1.0f };
float[] myPositions = { 0.0f, .4f, .8f, 1.0f };
var myBlend = new Blend {Factors = myFactors, Positions = myPositions};
return myBlend;
}
private void DrawGradientOnHeader(DataGridViewCellPaintingEventArgs e)
{
var newRect = new Rectangle(e.CellBounds.X+1, e.CellBounds.Y, e.CellBounds.Width -1, e.CellBounds.Height-1);
var gradient = new LinearGradientBrush(newRect, cl1, cl2, LinearGradientMode.Vertical);
gradient.Blend = gradientBlend();
e.Graphics.FillRectangle(gradient, newRect);
gradient.Dispose();
}
private static void DrawWhiteLine(DataGridViewCellPaintingEventArgs e, int x, int offset)
{
var brush = new SolidBrush(Color.White);
var pen = new Pen(brush);
int y = e.CellBounds.Y;
int x1 = x + offset;
int y1 = e.CellBounds.Height - 1;
var pt1 = new Point(x1, y);
var pt2 = new Point(x1, y1);
e.Graphics.DrawLine(pen, pt1, pt2);
brush.Dispose();
pen.Dispose();
}
private void DrawSeparators(DataGridViewCellPaintingEventArgs e)
{
DrawWhiteLine(e,e.CellBounds.Right,-1);
DrawWhiteLine(e, e.CellBounds.Left,1);
int x = e.CellBounds.X;
int y = e.CellBounds.Y;
int y1 = e.CellBounds.Height - 1;
var newRect2 = new Rectangle(x, y, 1, y1);
var gradient = new LinearGradientBrush(newRect2, cl3, cl4, LinearGradientMode.Vertical);
gradient.Blend = gradientBlend();
e.Graphics.FillRectangle(gradient, newRect2);
gradient.Dispose();
}
private static void DrawLines(DataGridViewCellPaintingEventArgs e)
{
var brush= new SolidBrush(Color.FromArgb(170, 186, 208));
var pen = new Pen(brush);
//Верхняя линяя над хидером
e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Top , e.CellBounds.Right, e.CellBounds.Top );
//Нижняя линия под хидером
e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Bottom - 1, e.CellBounds.Right, e.CellBounds.Bottom-1);
brush.Dispose();
pen.Dispose();
}
private void DrawText(DataGridViewCellPaintingEventArgs e)
{
var clr_font = _clrColumnHeader;
var rect = new Rectangle(e.CellBounds.X + Xoffset, e.CellBounds.Y + Yoffset, e.CellBounds.Width,
e.CellBounds.Height);
var formatFlags = TextFormatFlags.WordBreak;
if (e.Value != null)
{
try
{
TextRenderer.DrawText(e.Graphics, (String)e.Value, e.CellStyle.Font, rect, clr_font, formatFlags);
}
catch
{
TextRenderer.DrawText(e.Graphics, "", e.CellStyle.Font, rect, clr_font, formatFlags);
}
}
}
private void EraseTheCell(DataGridViewCellPaintingEventArgs e)
{
using (Brush backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
e.Graphics.FillRectangle(backColorBrush, e.CellBounds);
}
}
protected override void OnColumnWidthChanged(DataGridViewColumnEventArgs e)
{
base.OnColumnWidthChanged(e);
Invalidate();
}
private int oldRowIndex = 0;
protected override void OnCurrentCellChanged(EventArgs e)
{
base.OnCurrentCellChanged(e);
if (oldRowIndex != -1)
{
InvalidateRow(oldRowIndex);
}
oldRowIndex = CurrentCellAddress.Y;
}
protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
base.OnCellPainting(e);
if (e.RowIndex == -1) // Рисуем ColumnHeader
{
EraseTheCell(e);
DrawGradientOnHeader(e);
DrawSeparators(e);
DrawText(e);
DrawLines(e);
e.Handled = true;
}
//e.CellStyle.BackColor = Color.White;
}
protected override void PaintBackground(Graphics graphics, Rectangle clipBounds, Rectangle gridBounds)
{
base.PaintBackground(graphics, clipBounds, gridBounds);
var brush = new SolidBrush(Color.Black);
var pen = new Pen(brush);
var y_crd = clipBounds.Bottom - 10;
var x_crd = clipBounds.Right;
graphics.FillRectangle(brush, clipBounds);
var newbounds = new Rectangle
{
X = clipBounds.X,
Y = clipBounds.Y,
Width = clipBounds.Width,
Height = clipBounds.Height - 1
};
var brushWhite = new SolidBrush(Color.White);
graphics.FillRectangle(brushWhite, newbounds);
//graphics.DrawLine(pen, 0, y_crd, x_crd, y_crd);
brush.Dispose();
pen.Dispose();
}
}