Export (0) Print
Expand All
2 out of 7 rated this helpful - Rate this topic

How to: Create an Owner-Drawn List Box

You can create an owner-drawn list box with the .NET Compact Framework, although not as easily as with the full .NET Framework. The .NET Compact Framework does not support the DrawMode, DrawItem, and other drawing members for list box and other controls, but you can program that functionality. This example provides a custom control class to create an owner-drawn list box, and implements the control as a list box for selecting fonts.

This sample contains a base class, OwnerDrawnListBox, from which the FontListBox is derived. The OwnerDrawnListBox core functions include the following:

  • Provides the vertical scroll bar, an array for containing the items, and a bitmap for constructing the list box.

  • Provides an item height property and determines how many items can be drawn given the height of the control.

  • Provides a SelectedIndex property, which determines the selected item by current scroll bar, item height, and mouse pointer coordinate values.

  • Provides a SelectedIndexChanged event.

  • Ensures the visibility of items.

  • Removes the scroll bar, if not needed.

  • Provides keyboard operation with the up and down navigation keys.

The FontListBox class inherits from OwnerDrawnListBox. The implementations and techniques include the following:

  • Determines the proper item height.

  • Draws the items, with the selected item drawn as highlighted, using an overridden OnPaint method. To prevent flicker, this method does not display the control until all its graphical features are drawn.

  • Provides the SelectedFaceName property, containing the name of the selected font.

The DrawFontList class does the following:

  • Creates a new instance of FontListBox, and adds it to the controls collection.

  • Adds an event handler for the SelectedIndexChanged event.

  • Populates the FontListBox with a list of fonts.

  • Provides labels for displaying the sample of the selected font.

To create an owner drawn list box

  1. Add the OwnerDrawnListBox custom control class to your project.

    // Base custom control for DrawFontList 
    class OwnerDrawnListBox : Control
    {
        const int SCROLL_WIDTH = 20;
        int itemHeight         = -1;
        int selectedIndex      = -1;
    
        Bitmap offScreen;
        VScrollBar vs;
        ArrayList items;
    
        public OwnerDrawnListBox()
        {
            this.vs = new VScrollBar();
            this.vs.Parent      = this;
            this.vs.Visible     = false;
            this.vs.SmallChange = 1;
            this.vs.ValueChanged += new EventHandler(this.ScrollValueChanged);
    
            this.items = new ArrayList();
        }
    
        public ArrayList Items
        {
            get
            {
                return this.items;
            }
        }
    
        protected Bitmap OffScreen
        {
            get
            {
                return this.offScreen;
            }
        }
    
        protected VScrollBar VScrollBar
        {
            get
            {
                return this.vs;
            }
        }
    
        public event EventHandler SelectedIndexChanged;
    
        // Raise the SelectedIndexChanged event 
        protected virtual void OnSelectedIndexChanged(EventArgs e)
        {
            if(this.SelectedIndexChanged != null)
                this.SelectedIndexChanged(this, e);
        }
    
        protected override void OnMouseDown(MouseEventArgs e)
        {
            this.SelectedIndex = this.vs.Value + (e.Y / this.ItemHeight);
    
            // Invalidate the control so we can draw the item as selected. 
            this.Refresh();
        }
    
        // Get or set index of selected item. 
        public int SelectedIndex
        {
            get
            {
                return this.selectedIndex;
            }
    
            set
            {
                this.selectedIndex = value;
    
                if (this.SelectedIndexChanged != null)
                    this.SelectedIndexChanged(this, EventArgs.Empty);
    
            }
        }
    
        protected void ScrollValueChanged(object o, EventArgs e)
        {
            this.Refresh();
        }
    
        protected virtual int ItemHeight
        {
            get
            {
                return this.itemHeight;
            }
    
            set
            {
                this.itemHeight = value;
            }
        }
    
        // If the requested index is before the first visible index then set the 
        // first item to be the requested index. If it is after the last visible 
        // index, then set the last visible index to be the requested index. 
        public void EnsureVisible(int index)
        {
            if(index < this.vs.Value)
            {
                this.vs.Value = index;
                this.Refresh();
            }
            else if(index >= this.vs.Value + this.DrawCount)
            {
                this.vs.Value = index - this.DrawCount + 1;
                this.Refresh();
            }
        }
    
    
        // Need to set focus to the control when it 
        // is clicked so that keyboard events occur. 
        protected override void OnClick(EventArgs e)
        {
            this.Focus();
            base.OnClick(e);
        }
    
        // Selected item moves when you use the keyboard up/down keys. 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch(e.KeyCode)
            {
                case Keys.Down:
                    if(this.SelectedIndex < this.vs.Maximum)
                    {
                        EnsureVisible(++this.SelectedIndex);
                        this.Refresh();
                    }
                    break;
                case Keys.Up:
                    if(this.SelectedIndex > this.vs.Minimum)
                    {
                        EnsureVisible(--this.SelectedIndex);
                        this.Refresh();
                    }
                    break;
                case Keys.PageDown:
                    this.SelectedIndex = Math.Min(this.vs.Maximum, this.SelectedIndex + this.DrawCount);
                    EnsureVisible(this.SelectedIndex);
                    this.Refresh();
                    break;
                case Keys.PageUp:
                    this.SelectedIndex = Math.Max(this.vs.Minimum, this.SelectedIndex - this.DrawCount);
                    EnsureVisible(this.SelectedIndex);
                    this.Refresh();
                    break;
                case Keys.Home:
                    this.SelectedIndex = 0;
                    EnsureVisible(this.SelectedIndex);
                    this.Refresh();
                    break;
                case Keys.End:
                    this.SelectedIndex = this.items.Count - 1;
                    EnsureVisible(this.SelectedIndex);
                    this.Refresh();
                    break;
            }
    
            base.OnKeyDown(e);
        }
    
        // Calculate how many items we can draw given the height of the control. 
        protected int DrawCount
        {
            get
            {
                if(this.vs.Value + this.vs.LargeChange > this.vs.Maximum)
                    return this.vs.Maximum - this.vs.Value + 1;
                else 
                    return this.vs.LargeChange;
            }
        }
    
        protected override void OnResize(EventArgs e)
        {
            int viewableItemCount = this.ClientSize.Height / this.ItemHeight;
    
            this.vs.Bounds = new Rectangle(this.ClientSize.Width - SCROLL_WIDTH,
                0,
                SCROLL_WIDTH,
                this.ClientSize.Height);
    
    
            // Determine if scrollbars are needed 
            if(this.items.Count > viewableItemCount)
            {
                this.vs.Visible = true;
                this.vs.LargeChange = viewableItemCount;
                this.offScreen = new Bitmap(this.ClientSize.Width - SCROLL_WIDTH - 1, this.ClientSize.Height - 2);
            }
            else
            {
                this.vs.Visible = false;
                this.vs.LargeChange = this.items.Count;
                this.offScreen = new Bitmap(this.ClientSize.Width - 1, this.ClientSize.Height - 2);
            }
    
            this.vs.Maximum = this.items.Count - 1;
        }
    }
    
  2. Add the FontListBox class (which is an implementation of OwnerDrawnListBox) to your project.

    // Derive an implementation of the 
    // OwnerDrawnListBox class 
    class FontListBox : OwnerDrawnListBox
    {
        const int FONT_SIZE    = 10;
        const int DRAW_OFFSET  = 5;
    
        public FontListBox()
        {
    
            // Determine what the item height should be 
            // by adding 30% padding after measuring 
            // the letter A with the selected font.
            Graphics g = this.CreateGraphics();
            this.ItemHeight = (int)(g.MeasureString("A", this.Font).Height * 1.3);
            g.Dispose();
        }
    
        // Return the name of the selected font. 
        public string SelectedFaceName
        {
            get
            {
                return (string)this.Items[this.SelectedIndex];
            }
        }
    
        // Determine what the text color should be 
        // for the selected item drawn as highlighted
        Color CalcTextColor(Color backgroundColor)
        {
            if(backgroundColor.Equals(Color.Empty))
                return Color.Black;
    
            int sum = backgroundColor.R + backgroundColor.G + backgroundColor.B;
    
            if(sum > 256)
                return Color.Black;
            else 
                return Color.White;
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
            Font font;
            Color fontColor;
    
            // The base class contains a bitmap, offScreen, for constructing 
            // the control and is rendered when all items are populated. 
            // This technique prevents flicker.
            Graphics gOffScreen = Graphics.FromImage(this.OffScreen);
            gOffScreen.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
    
            int itemTop = 0;
    
            // Draw the fonts in the list. 
            for(int n = this.VScrollBar.Value; n < this.VScrollBar.Value + DrawCount; n++)
            {
                // If the font name contains "dings" it needs to be displayed
                // in the list box with a readable font with the default font. 
                if(((string)this.Items[n]).ToLower().IndexOf("dings") != -1)
                    font = new Font(this.Font.Name, FONT_SIZE, FontStyle.Regular);
                else
                    font = new Font((string)this.Items[n], FONT_SIZE, FontStyle.Regular);
    
                // Draw the selected item to appear highlighted 
                if(n == this.SelectedIndex)
                {
                    gOffScreen.FillRectangle(new SolidBrush(SystemColors.Highlight),
                        1,
                        itemTop + 1,
                        // If the scroll bar is visible, subtract the scrollbar width 
                        // otherwise subtract 2 for the width of the rectangle 
                        this.ClientSize.Width - (this.VScrollBar.Visible ? this.VScrollBar.Width : 2),
                        this.ItemHeight);
                    fontColor = CalcTextColor(SystemColors.Highlight);
                }
                else
                    fontColor = this.ForeColor;
    
                // Draw the item
                gOffScreen.DrawString((string)this.Items[n], font, new SolidBrush(fontColor), DRAW_OFFSET, itemTop);
                itemTop += this.ItemHeight;
            }
    
            // Draw the list box
            e.Graphics.DrawImage(this.OffScreen, 1, 1);
    
            gOffScreen.Dispose();
        }
    
        // Draws the external border around the control. 
    
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(new Pen(Color.Black), 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
        }
    }
    
  3. In the form, add two Label controls and name them descLabel and sampleLabel; the latter will show a font example selected from FontListBox.

  4. Create an instance of FontListBox for the form and name it box.

  5. Add the following code to the constructor of the Load event for form.

    // Create a new instance of FontListBox.
    box = new FontListBox();
    box.Parent = this;
    
    // Configure the event handler for the SelectedIndexChanged event.
    box.SelectedIndexChanged += new EventHandler(this.SelectedIndexChanged);
    
    // Draw the bounds of the FontListBox.
    box.Bounds = new Rectangle(5, 5, 150, 100);
    
    // Add fonts to list, repeat the list 4 times 
    // so that the scroll bar can be demonstrated. 
    for(int n = 0; n < 4; n++)
    {
        box.Items.Add("Bookdings");
        box.Items.Add("Courier New");
        box.Items.Add("Frutiger Linotype");
        box.Items.Add("Tahoma");
    }
    
    
    // Labels to show the selected font and a sample. 
    // Assumes an instance descLabel is declared for the form.
    descLabel = new Label();
    descLabel.Parent = this;
    descLabel.Text = "Font Sample:";
    descLabel.Bounds = new Rectangle(10, box.Bottom + 20, this.ClientSize.Width - 10, 30);
    
    // Assumes an instance sampleLabel is declared for the form.
    sampleLabel = new Label();
    sampleLabel.Parent = this;
    sampleLabel.Bounds = new Rectangle(10, descLabel.Bottom, this.ClientSize.Width - 10, 30);
    sampleLabel.Text = "AaBbCc 123";
    
  6. Add code for the SelectedIndexChanged event of the FontListBox control to the form, which displays the selected font in the sampleLabel control.

    // Event handler for SelectedIndexChanged to 
    // display a sample of the selected font. 
    private void SelectedIndexChanged(object o, EventArgs e)
    {
        sampleLabel.Font = new Font(box.SelectedFaceName, 12, FontStyle.Regular);
    }
    

This example requires references to the following namespaces:

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2014 Microsoft. All rights reserved.