如何在标题栏中显示带有下标或上标的文本?
How to display text with subscripts or superscripts in the Title Bar?
我希望能够在 Windows 表单或 WPF Window 的标题栏中显示带下标的文本。这样做的原因是简单的。我们的项目组写了一个分子编辑器:
不只是显示它的名字,'ACME',我们想显示类似的东西:
ACME - Editing C6H12Cl
文本下标(也可能是上标)的位置以及控件是否显示在 Windows 表单或 WPF 主机中。
最初的问题是询问如何在表单的标题中插入 RichTextBox,以显示化学式。
当然可以做到:你可以看看 DwmExtendFrameIntoClientArea and the Docs here: Custom Window Frame Using DWM and a number of SO questions.
但这里有点过头了。有一个更简单的替代方法:利用现有的 Unicode 下标符号(它是一个 Unicode 类别)来重现公式。
这些是 Hindu-Arabic 数字的基本下标和上标 Unicode 代码点:
char[] subScriptNumbers = {
'\u2080', '\u2081', '\u2082', '\u2083', '\u2084',
'\u2085', '\u2086', '\u2087', '\u2088', '\u2089'
};
char[] superScriptNumbers = {
'\u2070', '\u00B9', '\u00B2', '\u00B3', '\u2074',
'\u2075', '\u2076', '\u2077', '\u2078', '\u2079'
};
如评论中提示,简单的公式:C6H12Cl
可以转化为C₆H₁₂Cl
,映射数字到 SubScript 范围内相应的 Unicode 值。例如:
this.Text = string.Concat("C6H12Cl".Select(c => char.IsDigit(c) ? subScriptNumbers[c-48] : c));
或者,由于下标代码点是连续的(上标代码点不是):
const int subScriptBase = 0x2080;
string chem = "C6H12Cl";
// Or this.Title, in WPF
this.Text = chem.Aggregate(new StringBuilder(), (sb, c) =>
sb.Append(char.IsDigit(c) ? (char)(subScriptBase + c - 48) : c)).ToString();
既然有人感兴趣,我提出一个稍微复杂一点的解析器(使用相同的逻辑)来生成不同类型的公式:
这里显示的 class 可以转换 SubScript/SuperScript 数字或字母的序列,使用简单的符号(类似于维基百科使用的标记符号):
SuperScript: [+:symbols] A[+:12] => A¹²
SubScript: [-:symbols] A[-:12] => A₁₂
Fraction: [f:symbols/symbols] A·[f:x/12] => A·ˣ⁄₁₂
例如:
string formula = "N[+:(x+2)] · H[+:3] · γLog[-:e] + δ· [f:n11/x]";
// Or this.Text, in WinForms
this.Title = UniSubSup.Parse(formula);
将打印:
N⁽ˣ⁺²⁾·H³·γLogₑ + δ·ⁿ¹¹⁄ₓ
注1:
当 标记 包含空格时,它会被跳过:因此 [+:(x+2)]
被解析,而 [+:(x + 2)]
不被解析(以防这些括号被忽略)。
注2:
我没有包含所有字母,因为并非所有字体都支持 SubScript 和 SuperScript 类别中的所有代码点。比较常见的下标n
(\u2099
)(例如Logn)在大多数字体类型中都不可用(因为sub/super 脚本是通过不同的方式生成的)。
一些(很少)字体确实有这些字形。 fileformat.info 等网站可以提供此信息。
public class UniSubSup
{
const char joiner = '\u200D';
const char nonJoiner = '\u200C';
const char fraction = '\u2044';
const char solidusShort = '\u0337';
const char solidusLong = '\u0338';
protected internal static Dictionary<string, Func<string, string>> actions =
new Dictionary<string, Func<string, string>>()
{
["-"] = (s) => sub(s),
["+"] = (s) => sup(s),
["f"] = (s) => fract(s),
};
internal static string sub(string s) =>
s.Aggregate(new StringBuilder(), (sb, c) => sb.Append(subScripts[c])).ToString();
internal static string sup(string s) =>
s.Aggregate(new StringBuilder(), (sb, c) => sb.Append(superScripts[c])).ToString();
internal static string fract(string str)
{
var sb = new StringBuilder();
var parts = str.Split('/');
parts[0].Aggregate(sb, (s, c) => sb.Append(superScripts[c]));
sb.Append(fraction);
parts[1].Aggregate(sb, (s, c) => sb.Append(subScripts[c]));
return sb.ToString();
}
static RegexOptions options = RegexOptions.Singleline | RegexOptions.Compiled;
public static string Parse(string input)
{
string pattern = @"\[(\D{1}):(\S\/?\S*?)\]";
var matches = Regex.Matches(input, pattern, options);
var result = new StringBuilder(input);
foreach (Match m in matches)
result = result.Replace(m.Value, actions[m.Groups[1].Value](m.Groups[2].Value));
}
return result.ToString();
}
internal static Dictionary<char, char> superScripts = new Dictionary<char, char>()
{
['0'] = '\u2070', ['1'] = '\u00B9', ['2'] = '\u00B2', ['3'] = '\u00B3',
['4'] = '\u2074', ['5'] = '\u2075', ['6'] = '\u2076', ['7'] = '\u2077',
['8'] = '\u2078', ['9'] = '\u2079',
['+'] = '\u207A', ['-'] = '\u207B', ['='] = '\u207C',
['('] = '\u207D', [')'] = '\u207E',
['e'] = '\u1D49', ['n'] = '\u207F', ['x'] = '\u02E3'
};
internal static Dictionary<char, char> subScripts = new Dictionary<char, char>()
{
['0'] = '\u2080', ['1'] = '\u2081', ['2'] = '\u2082', ['3'] = '\u2083',
['4'] = '\u2084', ['5'] = '\u2085', ['6'] = '\u2086', ['7'] = '\u2087',
['8'] = '\u2088', ['9'] = '\u2089',
['+'] = '\u208A', ['-'] = '\u208B', ['='] = '\u208C',
['('] = '\u208D', [')'] = '\u208E', ['/'] = '\u2044',
['e'] = '\u2091', ['n'] = '\u2099', ['x'] = '\u2093'
};
}
关于 OP 提出的问题的更新
我是这个项目的 "poor other sod",他使用上面的方法来实现它。
结果如下图,我们现在标题栏happy days有了下标字符。
这是与上面看到的数字到 unicode 转换的定义一起完成技巧的代码
this.Text = "ACME - Editing" + string.Concat("C4H9NO2".Select(c => char.IsDigit(c) ? subScriptNumbers[c -48]:c));
我希望能够在 Windows 表单或 WPF Window 的标题栏中显示带下标的文本。这样做的原因是简单的。我们的项目组写了一个分子编辑器:
不只是显示它的名字,'ACME',我们想显示类似的东西:
ACME - Editing C6H12Cl
文本下标(也可能是上标)的位置以及控件是否显示在 Windows 表单或 WPF 主机中。
最初的问题是询问如何在表单的标题中插入 RichTextBox,以显示化学式。
当然可以做到:你可以看看 DwmExtendFrameIntoClientArea and the Docs here: Custom Window Frame Using DWM and a number of SO questions.
但这里有点过头了。有一个更简单的替代方法:利用现有的 Unicode 下标符号(它是一个 Unicode 类别)来重现公式。
这些是 Hindu-Arabic 数字的基本下标和上标 Unicode 代码点:
char[] subScriptNumbers = {
'\u2080', '\u2081', '\u2082', '\u2083', '\u2084',
'\u2085', '\u2086', '\u2087', '\u2088', '\u2089'
};
char[] superScriptNumbers = {
'\u2070', '\u00B9', '\u00B2', '\u00B3', '\u2074',
'\u2075', '\u2076', '\u2077', '\u2078', '\u2079'
};
如评论中提示,简单的公式:C6H12Cl
可以转化为C₆H₁₂Cl
,映射数字到 SubScript 范围内相应的 Unicode 值。例如:
this.Text = string.Concat("C6H12Cl".Select(c => char.IsDigit(c) ? subScriptNumbers[c-48] : c));
或者,由于下标代码点是连续的(上标代码点不是):
const int subScriptBase = 0x2080;
string chem = "C6H12Cl";
// Or this.Title, in WPF
this.Text = chem.Aggregate(new StringBuilder(), (sb, c) =>
sb.Append(char.IsDigit(c) ? (char)(subScriptBase + c - 48) : c)).ToString();
既然有人感兴趣,我提出一个稍微复杂一点的解析器(使用相同的逻辑)来生成不同类型的公式:
这里显示的 class 可以转换 SubScript/SuperScript 数字或字母的序列,使用简单的符号(类似于维基百科使用的标记符号):
SuperScript: [+:symbols] A[+:12] => A¹²
SubScript: [-:symbols] A[-:12] => A₁₂
Fraction: [f:symbols/symbols] A·[f:x/12] => A·ˣ⁄₁₂
例如:
string formula = "N[+:(x+2)] · H[+:3] · γLog[-:e] + δ· [f:n11/x]";
// Or this.Text, in WinForms
this.Title = UniSubSup.Parse(formula);
将打印:
N⁽ˣ⁺²⁾·H³·γLogₑ + δ·ⁿ¹¹⁄ₓ
注1:
当 标记 包含空格时,它会被跳过:因此 [+:(x+2)]
被解析,而 [+:(x + 2)]
不被解析(以防这些括号被忽略)。
注2:
我没有包含所有字母,因为并非所有字体都支持 SubScript 和 SuperScript 类别中的所有代码点。比较常见的下标n
(\u2099
)(例如Logn)在大多数字体类型中都不可用(因为sub/super 脚本是通过不同的方式生成的)。
一些(很少)字体确实有这些字形。 fileformat.info 等网站可以提供此信息。
public class UniSubSup
{
const char joiner = '\u200D';
const char nonJoiner = '\u200C';
const char fraction = '\u2044';
const char solidusShort = '\u0337';
const char solidusLong = '\u0338';
protected internal static Dictionary<string, Func<string, string>> actions =
new Dictionary<string, Func<string, string>>()
{
["-"] = (s) => sub(s),
["+"] = (s) => sup(s),
["f"] = (s) => fract(s),
};
internal static string sub(string s) =>
s.Aggregate(new StringBuilder(), (sb, c) => sb.Append(subScripts[c])).ToString();
internal static string sup(string s) =>
s.Aggregate(new StringBuilder(), (sb, c) => sb.Append(superScripts[c])).ToString();
internal static string fract(string str)
{
var sb = new StringBuilder();
var parts = str.Split('/');
parts[0].Aggregate(sb, (s, c) => sb.Append(superScripts[c]));
sb.Append(fraction);
parts[1].Aggregate(sb, (s, c) => sb.Append(subScripts[c]));
return sb.ToString();
}
static RegexOptions options = RegexOptions.Singleline | RegexOptions.Compiled;
public static string Parse(string input)
{
string pattern = @"\[(\D{1}):(\S\/?\S*?)\]";
var matches = Regex.Matches(input, pattern, options);
var result = new StringBuilder(input);
foreach (Match m in matches)
result = result.Replace(m.Value, actions[m.Groups[1].Value](m.Groups[2].Value));
}
return result.ToString();
}
internal static Dictionary<char, char> superScripts = new Dictionary<char, char>()
{
['0'] = '\u2070', ['1'] = '\u00B9', ['2'] = '\u00B2', ['3'] = '\u00B3',
['4'] = '\u2074', ['5'] = '\u2075', ['6'] = '\u2076', ['7'] = '\u2077',
['8'] = '\u2078', ['9'] = '\u2079',
['+'] = '\u207A', ['-'] = '\u207B', ['='] = '\u207C',
['('] = '\u207D', [')'] = '\u207E',
['e'] = '\u1D49', ['n'] = '\u207F', ['x'] = '\u02E3'
};
internal static Dictionary<char, char> subScripts = new Dictionary<char, char>()
{
['0'] = '\u2080', ['1'] = '\u2081', ['2'] = '\u2082', ['3'] = '\u2083',
['4'] = '\u2084', ['5'] = '\u2085', ['6'] = '\u2086', ['7'] = '\u2087',
['8'] = '\u2088', ['9'] = '\u2089',
['+'] = '\u208A', ['-'] = '\u208B', ['='] = '\u208C',
['('] = '\u208D', [')'] = '\u208E', ['/'] = '\u2044',
['e'] = '\u2091', ['n'] = '\u2099', ['x'] = '\u2093'
};
}
关于 OP 提出的问题的更新
我是这个项目的 "poor other sod",他使用上面的方法来实现它。
结果如下图,我们现在标题栏happy days有了下标字符。
这是与上面看到的数字到 unicode 转换的定义一起完成技巧的代码
this.Text = "ACME - Editing" + string.Concat("C4H9NO2".Select(c => char.IsDigit(c) ? subScriptNumbers[c -48]:c));