using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Linq; using System.Drawing.Drawing2D; using System.Drawing.Text; using HotelPms.Share.Windows.Util; using HotelPms.Share.Windows.Animations; using HotelPms.Share.Windows.GraphicsApi; namespace HotelPms.Share.Windows.Component { public class PanelEx : Panel { private int imgPadding = 5; public int ArcWidth { get; set; } = 16; public int OffsetX { get; set; } = 0; public int OffsetY { get; set; } = 0; /// /// 模糊 /// public int Blur { get; set; } = 15; /// /// 蔓延 /// public int Spread { get; set; } = 15; public bool UseTitleBookMark { get; set; } = true; public Color TitleBookMarkColor { get; set; } = Color.Pink; public string Title { get; set; } = string.Empty; public Font TitleFont { get; set; } = DefaultFont; public Font SignFont { get; set; } = DefaultFont; public int TitleHeight { get; set; } = 0; public Color TitleForeColor { get; set; } = Color.Black; public Color TitleBackColor { get; set; } = Color.Gainsboro; public int MaxHeight { get; set; } = -1; private bool m_Expan = true; /// /// 拡張 /// public bool Expan { get { return m_Expan; } set { m_Expan = value; if(!m_Expan && MaxHeight == -1) { MaxHeight = this.Height; } //this.Height = m_Expan ? MaxHeight : MixHeight; animationSelf.StartNewAnimation(m_Expan ? AnimationDirection.Out : AnimationDirection.In); Invalidate(); } } private StringFormat m_StringFormat = new StringFormat(); private Rectangle m_TitleRect; private string activeName = string.Empty; public string ActiveName { get { return activeName; } set { activeName = value; Invalidate(); } } private readonly Dictionary animationDict = new Dictionary(); private AnimationManager animationSelf; public PanelEx() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); m_StringFormat.LineAlignment = StringAlignment.Center; m_StringFormat.Alignment = StringAlignment.Near; animationSelf = new AnimationManager { Increment = 0.03, AnimationType = AnimationType.Linear, InterruptAnimation = false, }; animationSelf.OnAnimationProgress += _animationManager_OnAnimationProgress; } private void _animationManager_OnAnimationProgress(object sender) { Invalidate(); } protected override void OnControlAdded(ControlEventArgs e) { if (e.Control is TextBox || e.Control is ComboBox) { e.Control.Enter += Control_Enter; e.Control.Leave += Control_Leave; IPanelEx child = e.Control as IPanelEx; if (child != null) { System.Diagnostics.Debug.WriteLine($"{e.Control.Handle}:{e.Control.Name}:{child.PanelExEnabled}"); //←未設定 AnimationManager animationManager = new AnimationManager { Increment = 0.06, AnimationType = AnimationType.EaseInOut, InterruptAnimation = false, }; animationManager.OnAnimationProgress += _animationManager_OnAnimationProgress; animationDict.Add(e.Control, animationManager); } } base.OnControlAdded(e); } private void Control_Leave(object sender, EventArgs e) { ActiveName = string.Empty; IPanelEx child = sender as IPanelEx; if (child != null && child.PanelExEnabled && (child.TitleStyle == CTextBox.ETitleStyle.Material || child.TitleStyle == CTextBox.ETitleStyle.Icon)) { if (child.TitleStyle == CTextBox.ETitleStyle.Icon && (sender as Control).Text.Length > 0) { return; } animationDict[child as Control].StartNewAnimation(AnimationDirection.Out); } } private void Control_Enter(object sender, EventArgs e) { ActiveName = (sender as Control).Name; IPanelEx child = sender as IPanelEx; if (child != null && child.PanelExEnabled && (child.TitleStyle == CTextBox.ETitleStyle.Material || child.TitleStyle == CTextBox.ETitleStyle.Icon)) { if (child.TitleStyle == CTextBox.ETitleStyle.Icon && (sender as Control).Text.Length > 0) { return; } animationDict[child as Control].StartNewAnimation(AnimationDirection.In); } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias; DrawShadow(Controls.OfType().Where(c => ActiveName.Length > 0 && ActiveName == c.Name), e.Graphics); DrawStyle(Controls.OfType().Where(c => c.BorderStyle == BorderStyle.None), e.Graphics); DrawTitle(e); SetHeight(e); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (TitleHeight == 0) { return; } Cursor = m_TitleRect.Contains(e.Location) ? Cursors.Hand : Cursors.Default; } protected override void OnMouseClick(MouseEventArgs e) { base.OnMouseClick(e); if(!m_TitleRect.Contains(e.Location)) { return; } Expan = !Expan; } private void DrawTitle(PaintEventArgs e) { if(TitleHeight == 0) { return; } m_TitleRect = new Rectangle(UseTitleBookMark ? 5 : 0, 0, this.Width, TitleHeight); using (SolidBrush backBrush = new SolidBrush(TitleBackColor)) { e.Graphics.FillRectangle(backBrush, m_TitleRect); } if (UseTitleBookMark) { Rectangle markRect = new Rectangle(0, 0, 5, TitleHeight); using (SolidBrush backBrush = new SolidBrush(TitleBookMarkColor)) { e.Graphics.FillRectangle(backBrush, markRect); } } //文言 using (SolidBrush foreBrush = new SolidBrush(TitleForeColor)) { e.Graphics.DrawString(Title, TitleFont, foreBrush, m_TitleRect, m_StringFormat); } //矢印 int width = 30; Rectangle signRect = new Rectangle(this.Width - width, 0, width, TitleHeight); using (SolidBrush foreBrush = new SolidBrush(TitleForeColor)) { e.Graphics.DrawString(m_Expan ? "▲" : "▼", SignFont, foreBrush, signRect, m_StringFormat); } } private void SetHeight(PaintEventArgs e) { if (TitleHeight == 0 || MaxHeight <= 0) { return; } if (!animationSelf.IsAnimating()) { this.Height = m_Expan ? MaxHeight : TitleHeight; System.Diagnostics.Debug.WriteLine($"animationSelf⇒END"); } else { //Animate int height = MaxHeight - TitleHeight; int step = (int)(height * animationSelf.GetProgress()); if (m_Expan) { this.Height = TitleHeight + (height - step); } else { this.Height = MaxHeight - step; } System.Diagnostics.Debug.WriteLine($"animationSelf⇒{step}"); } } private void DrawShadow(IEnumerable controls, Graphics g) { foreach (var control in controls) { DrawOutsetShadow(g, control); } } private void DrawStyle(IEnumerable controls, Graphics g) { foreach (var control in controls) { DrawControlStyle(g, control); } } private Rectangle GetControlRect(Control control) { int titleWidth = 0; if (control is IPanelEx) { IPanelEx child = control as IPanelEx; if(child.PanelExEnabled) { if (child.TitleStyle == CTextBox.ETitleStyle.Label) { titleWidth = child.TitleWidth; } else if (child.TitleStyle == CTextBox.ETitleStyle.Icon) { titleWidth = control.Height + imgPadding; } } } int left = control.Left - ArcWidth / 2 - titleWidth; int top = control.Top - ArcWidth / 2; int width = control.Width + ArcWidth + titleWidth; int height = control.Height + ArcWidth; Rectangle r = new Rectangle(left, top, width, height); return r; } /// /// 枠線 /// /// /// private void DrawControlStyle(Graphics g, Control control) { bool drawBorder = true; Color borderColor = Color.FromArgb(255, 0, 120, 215); //255,0,120,215 (濃青) #7bc1f7 140, 123, 193, 247 (青) #22AC38 255, 34, 172, 56 (緑) Rectangle r = GetControlRect(control); if (control is IPanelEx) { IPanelEx child = control as IPanelEx; if (child.PanelExEnabled) { borderColor = control.Focused ? child.BorderFocusColor : child.BorderColor; if (child.TitleStyle == CTextBox.ETitleStyle.Material) { drawBorder = false; } } else { drawBorder = false; } } if (drawBorder) { using (GraphicsPath path = new GraphicsPath()) { path.AddArc(r.X, r.Y, ArcWidth, ArcWidth, 180, 90); path.AddArc(r.X + r.Width - ArcWidth - 1, r.Y, ArcWidth, ArcWidth, -90, 90); path.AddArc(r.X + r.Width - ArcWidth - 1, r.Y + r.Height - ArcWidth - 1, ArcWidth, ArcWidth, 0, 90); path.AddArc(r.X, r.Y + r.Height - ArcWidth - 1, ArcWidth, ArcWidth, 90, 90); path.CloseFigure(); using (Brush brush = new SolidBrush(control.BackColor)) { g.FillPath(brush, path); } using (Pen pen = new Pen(borderColor)) //255,0,120,215 (濃青) #7bc1f7 140, 123, 193, 247 (青) #22AC38 255, 34, 172, 56 (緑) { g.DrawPath(pen, path); } } } //Labelを各 if (control is IPanelEx) { IPanelEx child = control as IPanelEx; if (child.PanelExEnabled) { if (child.TitleStyle == CTextBox.ETitleStyle.Label) { int labelWidth = child.TitleWidth; using (GraphicsPath path = new GraphicsPath()) { path.AddArc(r.X, r.Y, ArcWidth, ArcWidth, 180, 90); path.AddLine(new Point(r.X + labelWidth, r.Y), new Point(r.X + labelWidth, r.Y + r.Height - 1)); path.AddArc(r.X, r.Y + r.Height - ArcWidth - 1, ArcWidth, ArcWidth, 90, 90); path.CloseFigure(); using (Brush brush = new SolidBrush(child.TitleBackColor)) { g.FillPath(brush, path); } using (Pen pen = new Pen(borderColor)) //255,0,120,215 (濃青) #7bc1f7 140, 123, 193, 247 (青) #22AC38 255, 34, 172, 56 (緑) 255,122,122,122 gray { g.DrawPath(pen, path); } } TextRenderer.DrawText(g, child.TitleText, child.TitleFont, new Rectangle(r.X, r.Y, child.TitleWidth, r.Height), child.TitleForeColor, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter); } else if (child.TitleStyle == CTextBox.ETitleStyle.Icon) { if (child.ForeImage != null) { Rectangle imgRect = new Rectangle(control.Left - control.Height - imgPadding, control.Top, control.Width, control.Height); GdiPlus.DrawImage(g, child.ForeImage as Bitmap, imgRect); } if (child is CTextBox) { CTextBox textBox = (child as CTextBox); Size sizeText = TextRenderer.MeasureText(textBox.TipText, child.TitleFont); int endX = r.X + 15; //タイトル文字最終的な位置 X int endY = r.Y - sizeText.Height * 2 / 3; //タイトル文字最終的な位置 Y if (animationDict.ContainsKey(control) && animationDict[control].IsAnimating()) { double per = animationDict[control].GetProgress(); //位置、フォントサイズの変化  animationDict[control].GetProgress() in ⇒ enter ⇒ 増大, out ⇒ leave ⇒ 減少 float fontSize = textBox.Font.Size - (textBox.Font.Size - child.TitleFont.Size) * (float)per; double curX = textBox.Left - (textBox.Left - endX) * per; double curY = textBox.Top - (textBox.Top - endY) * per; using (Font font = new Font(child.TitleFont.FontFamily, fontSize)) { Rectangle textR = new Rectangle((int)curX, (int)curY, (int)(sizeText.Width * 1.2), sizeText.Height); using (Brush brush = new SolidBrush(this.BackColor)) { g.FillRectangle(brush, textR); } TextRenderer.DrawText(g, textBox.TipText, font, textR, borderColor, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter); } } else { if ((control.Focused || textBox.Text.Length > 0) && textBox.TipText.Length > 0) { Rectangle textR = new Rectangle(endX, endY, (int)(sizeText.Width * 1.2), sizeText.Height); using (Brush brush = new SolidBrush(this.BackColor)) { g.FillRectangle(brush, textR); } TextRenderer.DrawText(g, textBox.TipText, child.TitleFont, textR, borderColor, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter); } } } } else if (child.TitleStyle == CTextBox.ETitleStyle.Material && animationDict.ContainsKey(control)) { var lineY = control.Bottom + 3; if (!animationDict[control].IsAnimating()) { using (Pen pen = new Pen(control.Focused ? child.ShadowColor : child.BorderColor, control.Focused ? 2F : 1F)) { g.DrawLine(pen, control.Location.X, lineY, control.Location.X + control.Width, lineY); } } else { //Animate int animationWidth = (int)(control.Width * animationDict[control].GetProgress()); int halfAnimationWidth = animationWidth / 2; int animationStart = control.Location.X + control.Width / 2; using (Pen pen = new Pen(child.BorderColor, 1F)) { g.DrawLine(pen, control.Location.X, lineY, control.Location.X + control.Width, lineY); } using (Pen pen = new Pen(child.ShadowColor, 2F)) { g.DrawLine(pen, animationStart - halfAnimationWidth, lineY, animationStart - halfAnimationWidth + animationWidth, lineY); } } } } } } // drawing the loop on an image because of speed private void DrawOutsetShadow(Graphics g, Control control) { Color shadowColor = Color.FromArgb(140, 123, 193, 247); //#7bc1f7 140, 123, 193, 247 (青) #22AC38 140, 34, 172, 56 (緑) if (control is IPanelEx) { IPanelEx child = control as IPanelEx; if (child.PanelExEnabled) { shadowColor = child.ShadowColor; if (child.TitleStyle == CTextBox.ETitleStyle.Material) { return; } } } var rOuter = control.Bounds; var rInner = control.Bounds; if (control is CTextBox && (control as CTextBox).BorderStyle == BorderStyle.None) { Rectangle r = GetControlRect(control); rOuter = r; rInner = r; } rInner.Offset(OffsetX, OffsetY); rInner.Inflate(-Blur, -Blur); rOuter.Inflate(Spread, Spread); rOuter.Offset(OffsetX, OffsetY); var originalOuter = rOuter; using (var img = new Bitmap(originalOuter.Width, originalOuter.Height, g)) { using (var g2 = Graphics.FromImage(img)) { var currentBlur = 0; do { var transparency = (rOuter.Height - rInner.Height) / (double)(Blur * 2 + Spread * 3); transparency -= 0.02; var color = Color.FromArgb(((int)(255 * (transparency * transparency))), shadowColor); var rOutput = rInner; rOutput.Offset(-originalOuter.Left, -originalOuter.Top); DrawRoundedRectangle(g2, rOutput, currentBlur, Pens.Transparent, color); rInner.Inflate(1, 1); currentBlur = (int)((double)Blur * (1 - (transparency * transparency))); } while (rOuter.Contains(rInner)); g2.Flush(); } g.DrawImage(img, originalOuter); } } private void DrawRoundedRectangle(Graphics gfx, Rectangle bounds, int cornerRadius, Pen drawPen, Color fillColor) { int strokeOffset = Convert.ToInt32(Math.Ceiling(drawPen.Width)); bounds = Rectangle.Inflate(bounds, -strokeOffset, -strokeOffset); var gfxPath = new GraphicsPath(); if (cornerRadius > 0) { gfxPath.AddArc(bounds.X, bounds.Y, cornerRadius, cornerRadius, 180, 90); gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y, cornerRadius, cornerRadius, 270, 90); gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90); gfxPath.AddArc(bounds.X, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); } else { gfxPath.AddRectangle(bounds); } gfxPath.CloseAllFigures(); gfx.FillPath(new SolidBrush(fillColor), gfxPath); if (drawPen != Pens.Transparent) { var pen = new Pen(drawPen.Color); pen.EndCap = pen.StartCap = LineCap.Round; gfx.DrawPath(pen, gfxPath); } } } }