字体不支持符号时如何在WPF文本框中插入版权、商标、服务标记等

Posted

技术标签:

【中文标题】字体不支持符号时如何在WPF文本框中插入版权、商标、服务标记等【英文标题】:How to insert copyright, trademark, service mark, etc. into WPF textbox when font doesn't support symbols 【发布时间】:2011-01-10 12:32:47 【问题描述】:

我们有一个 WPF 应用程序,它显示包含各种公司符号的文本;例如商标、注册商标、版权和服务标志。

数据库有一些包含标准公司符号的字段。最初,数据标记如下:

Example Corp(TM) or Example Plan (SM)

我们可以轻松地将占位符更改为它们各自的 Unicode 等价物;并且实际上在大多数情况下都有。

我们遇到的问题是应用程序使用的字体不支持服务标记符号(它只是一个上标SM)。我们可能无法替换字体或对其进行编辑。

这些字段可以是一个简单的产品名称,末尾带有符号,也可以是一个包含 0 次或多次符号的长描述。我们将 TextBoxes 或 Labels 直接绑定到 ViewModel 和/或业务对象(通常通过 DataTemplates)。应用程序中的所有数据都是只读的。

那么,假设我们必须通过代码(在 C# 和 WPF 中)解决这个问题,我的选择是什么?

【问题讨论】:

你能告诉我们更多关于这种字体(名称?内部还是商业?)以及为什么你不能使用不同的字体? 这是一种自定义的内部字体;不能使用其他的,因为它必须经过漫长的审批过程。客户非常大,我认为我们甚至无法追踪是谁创建的。 【参考方案1】:

编辑:我在另一个答案中发现TextBlock 也有一个Inlines 集合,可以添加Runs。 Anvaka's answer 巧妙地将附加属性用作一种转换器。


我对如何解决这个问题有几个想法。只有一个人会正确处理自动换行并处理不同字体的字符运行。

使用FlowDocumentScrollViewer 并使用ValueConverter 将字符串绑定到它,该ValueConverter 将字符串转换为FlowDocument

<FlowDocumentScrollViewer
    HorizontalScrollBarVisibility="Hidden"
    VerticalScrollBarVisibility="Hidden"
    Document="Binding MyString, Converter=StaticResource MyConverter" />

您可以在转换器上创建属性来设置常规字体属性,这些属性不能在FlowDocumentScrollViewer 上设置,而必须在转换器创建的FlowDocument 上设置。对于需要不同字体(可能还有不同大小)的异常子字符串,您可能还需要一些字体属性。另一种选择是在 FlowDocument 上为其中一些属性 (RelativeSource) 创建 Bindings。

下面是在代码中创建FlowDocument 的方法:

FlowDocument doc = new FlowDocument();
doc.FontFamily = new FontFamily( "Our Own Font" );
Paragraph par = new Paragraph();
doc.Blocks.Add( par );

然后您需要在特殊子字符串上拆分传入字符串,同时保持这些子字符串完整。您必须滚动自己的拆分器,并在转换器中拥有一组子字符串或提供给转换器。

在段落中添加一个普通的子字符串:

Run r = new Run( substring );
par.Inlines.Add( r );

为段落添加一个特殊的子字符串,使用不同的字体:

Run r = new Run( substring );
r.FontFamily = new FontFamily( "Arial" );
par.Inlines.Add( r );

以上只是小sn-ps。我不知道您想如何拆分字符串或迭代子字符串,因为我不熟悉数据,所以我没有提供我拼凑的方法只是为了看看我的想法是否可行。您还可以使用Dictionary 来检测一个子字符串并在输出中使用替换,例如检测"(SM)" 并将其替换为"℠"

如果您有任何问题或有什么我可以详细说明的,请告诉我。

(您说它是只读的很好。RichTextBox 不起作用,因为它的Document 属性不是DependencyProperty,因此不能成为Binding 的目标。虽然,也许可以使用Mode=OneWayToSource 来反转它,实现ConvertBack 而不是Convert。)


“我认为您遗漏的部分(迭代字符串并创建这些运行)是棘手的部分。”

关于在特殊子字符串上拆分字符串,我确实非常简短。当我说“我不知道你将如何拆分字符串”时,我并不是说我根本不知道该怎么做(我重新措辞了),而是说我不知道​​你会想要什么来处理它。我认为你不难弄清楚那部分,因为这是我会让潜在员工弄清楚的那种字符串操作问题。这样,您可能会在数据中发现需要更改处理方式的边缘情况。

我会用IndexOf()Substring()向你描述一个相对粗略的版本。

这就是问题中的问题:您有许多包含 0 个或多个特殊子字符串的字符串(例如,"Company Name(R), makers of Product(TM)")。这些子字符串很少且已知,输入字符串需要拆分成几个字符串,其中特殊子字符串和非特殊子字符串相互隔离(例如,"Company Name", "(R)", ", makers of Product", "(TM)")。

特殊子字符串很少且已知,因此您需要一个数组。您可以通过.IndexOf() 的返回值知道它是否找到了子字符串。循环遍历已知的特殊子字符串,您可以通过索引比较找到其中任何一个的第一个实例,同时保留子字符串的长度。

每次您在字符串 S(如果有)中找到最早的特殊子字符串时,都会生成派生字符串 ABCB 是特殊子串,AC 是前后。将AB 附加到ListC 成为新的S,然后你再做一次。除非您在 S 中找不到特殊的子字符串并且它不为空,在这种情况下您只需将其全部附加。

现在List 中的每个奇数索引字符串都是一个特殊的子字符串。当您找到的子字符串为"(SM)" 时,您可能想参考我在此答案的前一部分中提到的Dictionary 用作查找以添加"℠"Run

【讨论】:

将文本分成 Run 元素可能是要走的路;然而,这只是解决方案的一部分。我认为你遗漏的部分(迭代字符串并创建那些运行)是棘手的部分。 我将把它标记为正确答案,尽管我希望有更优雅的东西。实际上,这是 Anvaka 的答案和我最终实施的暴力字符串操作的结合。 听起来不错。在我看来,把琴弦分开总是不雅的。唯一可以整理它的方法 - 只需将复杂性从一个地方转移到另一个地方 - 就是用正则表达式分离字符串。【参考方案2】:

如果您的显示元素支持多种字体,例如 RichTextbox 控件,那么您可以在 (TM) 和 (SM) 符号周围使用适当的字体来格式化字符串。

【讨论】:

我想你只是重申了这个问题。【参考方案3】:

这样做有点难看,但一种选择可能是将包含预渲染符号的小图像放置在适当的位置。由于数据不是静态的(如果我理解正确的话),您需要动态计算图像的位置,除非字体是固定宽度,否则这将很困难。

【讨论】:

以上是关于字体不支持符号时如何在WPF文本框中插入版权、商标、服务标记等的主要内容,如果未能解决你的问题,请参考以下文章

使用 PHP 将注册商标符号/版权符号插入 MySQL

将插入符号/光标位置设置为字符串值 WPF 文本框的末尾

如何选择文本框中的文本,插入符号位于选择的开头?

MS UIAutomation 对文本无用吗?如何设置字体粗细、获取插入符号位置、插入文本等?

从 Windows 10 虚拟键盘输入到 WPF 文本框中的快乐表情符号未在文本框的 PreviewTextInput 事件中捕获

在富文本框中定位列表框