简体   繁体   中英

Combobox how to set selectedValue

I have a combobox which I fill font's inside. The method I use to do so is in this link. I will share the answer from that question here.

public YourForm()
{
    InitializeComponent();

    ComboBoxFonts.DrawItem += ComboBoxFonts_DrawItem;           
    ComboBoxFonts.DataSource = System.Drawing.FontFamily.Families.ToList();
}

private void ComboBoxFonts_DrawItem(object sender, DrawItemEventArgs e)
{
    var comboBox = (ComboBox)sender;
    var fontFamily = (FontFamily)comboBox.Items[e.Index];
    var font = new Font(fontFamily, comboBox.Font.SizeInPoints);

    e.DrawBackground();
    e.Graphics.DrawString(font.Name, font, Brushes.Black, e.Bounds.X, e.Bounds.Y);
}

now I have only made 1 change to this code and that's this:

cmbFonts.DrawMode = DrawMode.OwnerDrawFixed;

there is no problem with loading fonts and it's working but I try to set the selectedvalue on my form load. For example I try to set font named "arial". In order to do so I use this:

var dataSource = cmbFonts.DataSource as List<FontFamily>;
int res = -1;
try
{
  res = dataSource.IndexOf(new FontFamily(StaticVariables.FontName));
}
catch { }
if (res != -1)
  cmbFonts.SelectedIndex = res;

now when I do this I get System.ArgumentOutOfRangeException error because I did not add any Items to Combobox I bind a DataSource therefore when I try to set SelectedIndex I get this error and I know that, I also tried this:

cmbFonts.SelectedValue = StaticVariables.FontName;

but when I run my code with breakpoints in Visual studio I see that my SelectedValue never changes. Before the line is executed I see null and after the execution I still see null in SelectedValue , I checked my StaticVariables.FontName variable and the font is presented in there.

I've also tried to use combobox.Text property but no luck, it's like SelectedValue , was empty string before and still same after I skip it with breakpoint.

TL;DR: I try to select an item on form load in combobox which I filled with DataSource

Here's a custom owner-drawn ComboBox class (here, named FontListCombo ) that shows compatible System Font Families, representing each ComboBox Item using the FontFamily name as the Item Text and the corresponding Font to draw the Item's text (a classic ComboBox Font selector, in practice).

The Custom Control automatically fills itself, at run-time, with the list of available Fonts in the System. It also react to WM_FONTCHANGE messages (broadcast when the System Font pool changes; eg, a Font is added to or removed from the Fonts folder), to updated the Font list and reflect the change (to also avoid to try and use a Font that doesn't exist anymore).

The text of the ComboBox Items is drawn using TextRenderer.DrawText() instead of Graphics.DrawString() , since the former grants sharper results in this context.

The ComboBox.Items collection is represented by a collection of FontObject class objects, a public class that stores some of the properties of each FontFamily and also exposes a couple of static methods used internally to return a Font object or a FontFamily object, calling the corresponding public methods of the custom ComboBox:

  • The GetSelectedFont(SizeInPoints, FontStyle) method return a Font object from the current ComboBox.SelectedItem .
  • GetSelectedFontFamily() return the FontFamily object from the current ComboBox.SelectedItem .

It also overrides ToString() , to return a summary of values of its properties.

This kind of object container is a better fit here: storing FontFamily object as the ConboBox Items would, most surely, create different sorts of problems, the most visible and tragic is that some FontFamily objects become invalid over time or even right after they have been stored. These objects are not meant to be stored permanently to begin with, so it's not exactly a surprise.

As shown in the example, to get the current Font and FontFamily from the ComboBox.SelecteItem :
(here, the ComboBox instance is named cboFontList )

private void cboFontList_SelectionChangeCommitted(object sender, EventArgs e)
{
    Font font = cboFontList.GetSelectedFont(this.Font.SizeInPoints, FontStyle.Regular);
    FontFamily family = cboFontList.GetSelectedFontFamily();
    string fontDetails = (cboFontList.SelectedItem as FontListCombo.FontObject).ToString();
}

The FontObject class stores some important details of the FontFamily, such as the Cell Ascent , the Cell Descent , the EM Size and the Line Spacing .
Some details on how to use these features are described here:

Properly draw text using Graphics Path
Fonts and text metrics

This is how it works:

自定义组合框字体选择器


FontListCombo Custom Control:

using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

[DesignerCategory("Code")]
public class FontListCombo : ComboBox
{
    private List<FontObject> fontList = null;

    public FontListCombo() {
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.DrawMode = DrawMode.OwnerDrawVariable;
    }

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        if (!DesignMode) GetFontFamilies();
    }

    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        if ((Items.Count == 0) || e.Index < 0) return;
        e.DrawBackground();
        var flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
        using (var family = new FontFamily(this.GetItemText(Items[e.Index])))
        using (var font = new Font(family, 10F, FontStyle.Regular, GraphicsUnit.Point)) {
            TextRenderer.DrawText(e.Graphics, family.Name, font, e.Bounds, this.ForeColor, flags);
        }
        e.DrawFocusRectangle();
        base.OnDrawItem(e);
    }

    protected override void OnMeasureItem(MeasureItemEventArgs e) {
        base.OnMeasureItem(e);
        e.ItemHeight = this.Font.Height + 4;
    }

    private void GetFontFamilies()
    {
        this.fontList = new List<FontObject>();

        fontList.AddRange(FontFamily.Families
            .Where(f => f.IsStyleAvailable(FontStyle.Regular))
            .Select(f => new FontObject(f)).ToArray());
        this.DisplayMember = "FamilyName";
        this.ValueMember = "EmHeight";
        this.DataSource = fontList;
    }

    public FontFamily GetSelectedFontFamily()
    {
        if (this.SelectedIndex < 0) return null;
        return FontObject.GetSelectedFontFamily((FontObject)this.SelectedItem);
    }

    public Font GetSelectedFont(float sizeInPoints, FontStyle style)
    {
        if (this.SelectedIndex < 0) return null;
        return FontObject.GetSelectedFont((FontObject)this.SelectedItem, sizeInPoints, style);
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg) {
            case WM_FONTCHANGE:  // The System Font pool has changed
                GetFontFamilies();
                break;
        }
    }

    public class FontObject
    {
        public FontObject(FontFamily family) { GetFontFamilyInfo(family); }
        public string FamilyName { get; set; }
        public int EmHeight { get; set; }
        public int CellAscent { get; set; }
        public int CellDescent { get; set; }
        public int LineSpacing { get; set; }

        private void GetFontFamilyInfo(FontFamily family)
        {
            this.FamilyName = family.Name;
            this.EmHeight = family.GetEmHeight(FontStyle.Regular);
            this.CellAscent = family.GetCellAscent(FontStyle.Regular);
            this.CellDescent = family.GetCellDescent(FontStyle.Regular);
            this.LineSpacing = family.GetLineSpacing(FontStyle.Regular);
        }
        internal static FontFamily GetSelectedFontFamily(FontObject fobj) 
            => new FontFamily(fobj.FamilyName);

        internal static Font GetSelectedFont(FontObject fobj, float sizeInPoints, FontStyle style) 
            => new Font(GetSelectedFontFamily(fobj), sizeInPoints, style);

        public override string ToString()
        {
            var sb = new StringBuilder();
            sb.AppendLine(this.FamilyName);
            sb.AppendLine($"Em Height: {this.EmHeight}");
            sb.AppendLine($"Cell Ascent: {this.CellAscent}");
            sb.AppendLine($"Cell Descent: {this.CellDescent}");
            sb.AppendLine($"Line Spacing: {this.LineSpacing}");
            return sb.ToString();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM