如何设置用作字体选择器的 ComboBox 的 SelectedValue?
How to set the SelectedValue of a ComboBox used as Font selector?
我有一个组合框,我在里面填充了字体。我使用的方法是 link。我将在这里分享那个问题的答案。
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);
}
现在我只对这段代码做了 1 处更改,那就是:
cmbFonts.DrawMode = DrawMode.OwnerDrawFixed;
加载字体没有问题,它可以正常工作,但我尝试在我的表单加载时设置 selectedvalue。例如,我尝试设置名为“arial”的字体。为此,我使用了这个:
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;
现在当我这样做时我得到 System.ArgumentOutOfRangeException
错误,因为我没有添加任何项目到 Combobox
我绑定了 DataSource
因此当我尝试设置 SelectedIndex
我得到这个错误,我知道,我也试过这个:
cmbFonts.SelectedValue = StaticVariables.FontName;
但是当我 运行 我的代码在 Visual studio 中带有断点时,我发现我的 SelectedValue
从未改变。在执行该行之前我看到了 null,在执行之后我仍然在 SelectedValue
中看到了 null,我检查了我的 StaticVariables.FontName 变量并且字体显示在那里。
我也尝试过使用 combobox.Text
属性 但没有运气,就像 SelectedValue
,之前是空字符串,在我用断点跳过它之后仍然是一样的。
长话短说:
我尝试 select 在我填充了 DataSource
的组合框中加载表单上的项目
这是一个自定义的所有者绘制的 ComboBox class(此处命名为 FontListCombo
)显示 compatible系统字体系列,代表每个 ComboBox Item 使用 FontFamily 名称作为 Item Text 和相应的字体来绘制 Item 的文本(实际上是 classic ComboBox Font 选择器)。
自定义控件会在 运行 时自动填充系统中的可用字体列表。它还对 WM_FONTCHANGE
消息做出反应(当系统字体池更改时广播;例如,将字体添加到 Fonts
文件夹或从中删除),以更新字体列表并反映更改(也避免尝试使用不再存在的字体)。
ComboBox Items 的文本是使用 TextRenderer.DrawText() instead of Graphics.DrawString() 绘制的,因为前者在此上下文中提供更清晰的结果。
ComboBox.Items
集合由 FontObject
class 个对象的集合表示,publicclass 它存储了每个 FontFamily 的一些属性,并且还公开了一些内部使用的静态方法 return Font 对象或 FontFamily 对象,调用自定义 ComboBox 的相应 public 方法:
GetSelectedFont(SizeInPoints, FontStyle)
方法 return 来自当前 ComboBox.SelectedItem
的 Font 对象。
GetSelectedFontFamily()
return 来自当前 ComboBox.SelectedItem
的 FontFamily 对象。
它还会覆盖 ToString()
,成为 return 其属性值的摘要。
这种对象容器更适合这里:将 FontFamily 对象存储为 ConboBox 项肯定会产生不同类型的问题,最明显和最悲惨的是一些 FontFamily 对象随着时间的推移甚至变得无效在它们被存储之后。这些对象一开始并不是要永久存储的,所以这并不奇怪。
如示例所示,从ComboBox.SelecteItem
获取当前的Font和FontFamily:
(这里,ComboBox 实例被命名为 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();
}
FontObject class 存储了 FontFamily 的一些重要细节,例如 Cell Ascent, the Cell Descent, the EM Size and the Line Spacing.
此处描述了有关如何使用这些功能的一些详细信息:
它是这样工作的:
FontListCombo 自定义控件:
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();
}
}
}
我有一个组合框,我在里面填充了字体。我使用的方法是
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);
}
现在我只对这段代码做了 1 处更改,那就是:
cmbFonts.DrawMode = DrawMode.OwnerDrawFixed;
加载字体没有问题,它可以正常工作,但我尝试在我的表单加载时设置 selectedvalue。例如,我尝试设置名为“arial”的字体。为此,我使用了这个:
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;
现在当我这样做时我得到 System.ArgumentOutOfRangeException
错误,因为我没有添加任何项目到 Combobox
我绑定了 DataSource
因此当我尝试设置 SelectedIndex
我得到这个错误,我知道,我也试过这个:
cmbFonts.SelectedValue = StaticVariables.FontName;
但是当我 运行 我的代码在 Visual studio 中带有断点时,我发现我的 SelectedValue
从未改变。在执行该行之前我看到了 null,在执行之后我仍然在 SelectedValue
中看到了 null,我检查了我的 StaticVariables.FontName 变量并且字体显示在那里。
我也尝试过使用 combobox.Text
属性 但没有运气,就像 SelectedValue
,之前是空字符串,在我用断点跳过它之后仍然是一样的。
长话短说:
我尝试 select 在我填充了 DataSource
这是一个自定义的所有者绘制的 ComboBox class(此处命名为 FontListCombo
)显示 compatible系统字体系列,代表每个 ComboBox Item 使用 FontFamily 名称作为 Item Text 和相应的字体来绘制 Item 的文本(实际上是 classic ComboBox Font 选择器)。
自定义控件会在 运行 时自动填充系统中的可用字体列表。它还对 WM_FONTCHANGE
消息做出反应(当系统字体池更改时广播;例如,将字体添加到 Fonts
文件夹或从中删除),以更新字体列表并反映更改(也避免尝试使用不再存在的字体)。
ComboBox Items 的文本是使用 TextRenderer.DrawText() instead of Graphics.DrawString() 绘制的,因为前者在此上下文中提供更清晰的结果。
ComboBox.Items
集合由 FontObject
class 个对象的集合表示,publicclass 它存储了每个 FontFamily 的一些属性,并且还公开了一些内部使用的静态方法 return Font 对象或 FontFamily 对象,调用自定义 ComboBox 的相应 public 方法:
GetSelectedFont(SizeInPoints, FontStyle)
方法 return 来自当前ComboBox.SelectedItem
的 Font 对象。GetSelectedFontFamily()
return 来自当前ComboBox.SelectedItem
的 FontFamily 对象。
它还会覆盖 ToString()
,成为 return 其属性值的摘要。
这种对象容器更适合这里:将 FontFamily 对象存储为 ConboBox 项肯定会产生不同类型的问题,最明显和最悲惨的是一些 FontFamily 对象随着时间的推移甚至变得无效在它们被存储之后。这些对象一开始并不是要永久存储的,所以这并不奇怪。
如示例所示,从ComboBox.SelecteItem
获取当前的Font和FontFamily:
(这里,ComboBox 实例被命名为 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();
}
FontObject class 存储了 FontFamily 的一些重要细节,例如 Cell Ascent, the Cell Descent, the EM Size and the Line Spacing.
此处描述了有关如何使用这些功能的一些详细信息:
它是这样工作的:
FontListCombo 自定义控件:
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();
}
}
}