using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace AeroWizard { /// /// Shows a list of all the pages in the WizardControl /// [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 indentLevels = new Dictionary(); private WizardControl myParent; private System.Drawing.Font ptrFont; private Dictionary stepTexts = new Dictionary(); /// /// Initializes a new instance of the class. /// public StepList() { } /// /// Occurs when a visual aspect of an owner-drawn StepList changes. /// [Category("Appearance")] public event EventHandler DrawItem; /// /// Occurs when an owner-drawn StepList is created and the sizes of the list items are determined. /// [Category("Appearance")] public event EventHandler MeasureItem; /// /// Gets or sets a value indicating whether the StepList is drawn by the operating system or by code that you provide. /// /// /// true if the StepList is drawn by code that you provide; otherwise, false. /// [DefaultValue(false), Category("Appearance"), Description("Indicates if controls is drawn by owner.")] public bool OwnerDraw { get; set; } /// /// Gets the default size of the control. /// /// The default of the control. protected override Size DefaultSize => new Size(150, 200); /// /// Gets the step text. /// /// The page. /// Step text for the specified wizard page. [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; } /// /// Gets the step text indent level. /// /// The page. /// Step text indent level for the specified wizard page. [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); /// /// Sets the step text. /// /// The page. /// The value. public void SetStepText(WizardPage page, string value) { if (string.IsNullOrEmpty(value) || value == page.Text) stepTexts.Remove(page); else stepTexts[page] = value; Refresh(); } /// /// Sets the step text indent level. /// /// The page. /// The indent level. public void SetStepTextIndentLevel(WizardPage page, int value) { if (value < 0) value = 0; if (value == 0) indentLevels.Remove(page); else indentLevels[page] = value; Refresh(); } /// /// Raises the event. /// /// The instance containing the event data. protected virtual void OnDrawItem(DrawStepListItemEventArgs e) { var h = DrawItem; if (h != null) h(this, e); } /// /// Raises the event. /// /// The instance containing the event data. 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; } } /// /// Raises the event. /// /// The instance containing the event data. protected virtual void OnMeasureItem(MeasureStepListItemEventArgs e) { var h = MeasureItem; if (h != null) h(this, e); } /// /// Raises the event. /// /// A that contains the event data. 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; } } } /// /// Raises the event. /// /// An that contains the event data. protected override void OnParentChanged(EventArgs e) { base.OnParentChanged(e); SetupControl(this.GetParent()); } 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.ListChangedEventArgs 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); } }