AirScout/AeroWizard/AeroWizard/StepList.cs

272 wiersze
8.8 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace AeroWizard
{
/// <summary>
/// Shows a list of all the pages in the WizardControl
/// </summary>
[ProvideProperty("StepText", typeof(WizardPage))]
[ProvideProperty("StepTextIndentLevel", typeof(WizardPage))]
internal class StepList : ScrollableControl, IExtenderProvider
{
private const TextFormatFlags defBulletTextFormat = TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding;
private const TextFormatFlags defStringTextFormat = TextFormatFlags.EndEllipsis | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter;
private System.Drawing.Font boldFont;
private int bulletWidth;
private int defItemHeight;
private Dictionary<WizardPage, int> indentLevels = new Dictionary<WizardPage, int>();
private WizardControl myParent;
private System.Drawing.Font ptrFont;
private Dictionary<WizardPage, string> stepTexts = new Dictionary<WizardPage, string>();
/// <summary>
/// Initializes a new instance of the <see cref="StepList"/> class.
/// </summary>
public StepList()
{
}
/// <summary>
/// Occurs when a visual aspect of an owner-drawn StepList changes.
/// </summary>
[Category("Appearance")]
public event EventHandler<DrawStepListItemEventArgs> DrawItem;
/// <summary>
/// Occurs when an owner-drawn StepList is created and the sizes of the list items are determined.
/// </summary>
[Category("Appearance")]
public event EventHandler<MeasureStepListItemEventArgs> MeasureItem;
/// <summary>
/// Gets or sets a value indicating whether the StepList is drawn by the operating system or by code that you provide.
/// </summary>
/// <value>
/// <c>true</c> if the StepList is drawn by code that you provide; otherwise, <c>false</c>.
/// </value>
[DefaultValue(false), Category("Appearance"), Description("Indicates if controls is drawn by owner.")]
public bool OwnerDraw { get; set; }
/// <summary>
/// Gets the default size of the control.
/// </summary>
/// <returns>The default <see cref="T:System.Drawing.Size" /> of the control.</returns>
protected override Size DefaultSize => new Size(150, 200);
/// <summary>
/// Gets the step text.
/// </summary>
/// <param name="page">The page.</param>
/// <returns>Step text for the specified wizard page.</returns>
[DefaultValue((string)null), Category("Appearance"), Description("Alternate text to provide to the StepList. Default value comes the Text property of the WizardPage.")]
public string GetStepText(WizardPage page)
{
string value;
if (stepTexts.TryGetValue(page, out value))
return value;
return page.Text;
}
/// <summary>
/// Gets the step text indent level.
/// </summary>
/// <param name="page">The page.</param>
/// <returns>Step text indent level for the specified wizard page.</returns>
[DefaultValue(0), Category("Appearance"), Description("Indentation level for text provided to the StepList.")]
public int GetStepTextIndentLevel(WizardPage page)
{
int value;
if (indentLevels.TryGetValue(page, out value))
return value;
return 0;
}
bool IExtenderProvider.CanExtend(object extendee) => (extendee is WizardPage);
/// <summary>
/// Sets the step text.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="value">The value.</param>
public void SetStepText(WizardPage page, string value)
{
if (string.IsNullOrEmpty(value) || value == page.Text)
stepTexts.Remove(page);
else
stepTexts[page] = value;
Refresh();
}
/// <summary>
/// Sets the step text indent level.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="value">The indent level.</param>
public void SetStepTextIndentLevel(WizardPage page, int value)
{
if (value < 0) value = 0;
if (value == 0)
indentLevels.Remove(page);
else
indentLevels[page] = value;
Refresh();
}
/// <summary>
/// Raises the <see cref="E:DrawItem"/> event.
/// </summary>
/// <param name="e">The <see cref="DrawStepListItemEventArgs"/> instance containing the event data.</param>
protected virtual void OnDrawItem(DrawStepListItemEventArgs e)
{
var h = DrawItem;
if (h != null)
h(this, e);
}
/// <summary>
/// Raises the <see cref="E:FontChanged"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
using (Graphics g = CreateGraphics())
{
defItemHeight = (int)Math.Ceiling(TextRenderer.MeasureText(g, "Wg", Font).Height * 1.2);
ptrFont = new Font("Marlett", Font.Size);
boldFont = new Font(Font, FontStyle.Bold);
bulletWidth = TextRenderer.MeasureText(g, "4", ptrFont, new Size(0, defItemHeight), defBulletTextFormat).Width;
}
}
/// <summary>
/// Raises the <see cref="E:MeasureItem"/> event.
/// </summary>
/// <param name="e">The <see cref="MeasureStepListItemEventArgs"/> instance containing the event data.</param>
protected virtual void OnMeasureItem(MeasureStepListItemEventArgs e)
{
var h = MeasureItem;
if (h != null)
h(this, e);
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.Paint" /> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs" /> that contains the event data.</param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (myParent == null) return;
int y = Padding.Top;
WizardPageCollection pages = myParent.Pages;
bool hit = false;
for (int i = 0; i < pages.Count; i++)
{
var curPage = pages[i];
if (!curPage.Suppress)
{
Size itemSize = new Size(Width - Padding.Horizontal, defItemHeight);
if (OwnerDraw)
{
var meArg = new MeasureStepListItemEventArgs(e.Graphics, Font, curPage, new Size(Width, defItemHeight));
OnMeasureItem(meArg);
itemSize = meArg.ItemSize;
}
if (y + itemSize.Height > (Height - Padding.Bottom))
break;
bool isSelected = myParent.SelectedPage == curPage;
if (isSelected) hit = true;
var eArg = new DrawStepListItemEventArgs(e.Graphics, Font, new Rectangle(new Point(Padding.Left, y), itemSize), curPage, isSelected, hit);
if (OwnerDraw)
OnDrawItem(eArg);
else
DefaultDrawItem(eArg);
y += itemSize.Height;
}
}
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.ParentChanged" /> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
SetupControl(this.GetParent<WizardControl>());
}
private static void Split(Rectangle rect, int xPos, out Rectangle r1, out Rectangle r2)
{
r1 = r2 = rect;
r1.Width = r2.X = xPos;
r2.Width = r2.Width - xPos;
}
private void DefaultDrawItem(DrawStepListItemEventArgs e)
{
Color fc = ForeColor, bc = BackColor;
int level = GetStepTextIndentLevel(e.Item);
bool isRTL = RightToLeft == System.Windows.Forms.RightToLeft.Yes;
var tffrtl = isRTL ? TextFormatFlags.Right : 0;
Rectangle rect, prect;
if (isRTL)
{
Split(e.Bounds, Width - bulletWidth, out rect, out prect);
prect.X = e.Bounds.Width - (bulletWidth * (level + 1));
rect.Width = Width - (bulletWidth * (level + 1));
}
else
{
Split(e.Bounds, bulletWidth, out prect, out rect);
prect.X = bulletWidth * level;
rect.X = bulletWidth * (level + 1);
}
if (!e.Completed)
fc = SystemColors.GrayText;
using (Brush br = new SolidBrush(bc))
e.Graphics.FillRectangle(br, Rectangle.Union(rect, prect));
TextRenderer.DrawText(e.Graphics, e.Completed ? (isRTL ? "3" : "4") : "a", ptrFont, prect, fc, defBulletTextFormat | tffrtl);
TextRenderer.DrawText(e.Graphics, GetStepText(e.Item), e.Selected ? boldFont : Font, rect, fc, defStringTextFormat | tffrtl);
}
private void pages_Changed(object sender, EventedList<WizardPage>.ListChangedEventArgs<WizardPage> e)
{
Refresh();
}
private void ResetStepText(WizardPage page)
{
SetStepText(page, null);
}
private void SetupControl(WizardControl p)
{
if (myParent != null)
{
WizardPageCollection pages = myParent.Pages;
pages.ItemAdded -= pages_Changed;
pages.ItemChanged -= pages_Changed;
pages.ItemDeleted -= pages_Changed;
}
myParent = p;
if (myParent != null)
{
WizardPageCollection pages = myParent.Pages;
pages.ItemAdded += pages_Changed;
pages.ItemChanged += pages_Changed;
pages.ItemDeleted += pages_Changed;
}
Refresh();
}
private bool ShouldSerializeStepText(WizardPage page) => (GetStepText(page) != page.Text);
}
}