我正在尝试显示类似于Blend中的字体选择器列表:
Blend Font Picker http://img691.imageshack.us/img691/60/blendfontpicker.png
与Blend一样,我在FontCache中未加载FontFamilies时会看到性能问题。
我付出的代价似乎是实际渲染给定FontSize的FontFamily并将其保存到FontCache中所需的时间。一旦渲染的字体在缓存中,问题就会消失。
我尝试在后台线程上迭代Fonts.SystemFontFamilies集合并调度对UI的调用,导致隐藏的TextBlock更新(这应该导致字体呈现)。
当然,由于调度调用是连续发生的,所以只需对UI进行调整,并获得阻塞UI的相同结果,直到所有字体都被渲染并加载到FontCache中。
有没有人能很好地解决这个问题?他们似乎没有在Blend找到它的解决方案所以我认为没有一个好的解决方案。
答案 0 :(得分:3)
为您提出一些想法
想法1:完全在后台线程上获取字体。
字体实际上被加载到系统字体缓存中(进程间),然后部分字体信息被复制到特定于线程的字体缓存中。填充系统字体缓存可能会导致速度的充分提高。这可以通过低优先级后台线程来完成,该线程开始运行应用程序启动的瞬间。因此,当用户下拉字体列表时,系统字体缓存应该完全填充。
创意2:自己缓存渲染的字体几何
不使用TextBlocks,而是使用ComboBox的DataTemplate中的ContentPresenter对象以及绑定到PriorityBinding的内容。较低优先级将使用默认字体生成TextBlock,较高优先级将是IsAsync绑定,该绑定将使用适当的参数创建GlyphRun,在其上调用BuildGeometry(),并在Path对象内返回Geometry。创建的Geometry对象可以被缓存并再次返回,以便将来访问相同的字体。
结果将是项目最初将以默认字体显示,并在加载字体并创建其几何图形后立即呈现为样式字体。请注意,这可以与在单独的线程中预填充缓存的代码结合使用。
Idea 2的代码如下所示:
<ComboBox ItemsSource="{Binding MyFontObjects}">
<ComboBox.ItemTemplate>
<ContentPresenter>
<ContentPresenter.Content>
<PriorityBinding>
<Binding IsAsync="True" Path="BuildStyledFontName" />
<Binding Path="BuildTextBlock" />
</PriorityBinding>
... close all tags ...
MyFontObjets将是IEnumerable对象,如下所示:
public class MyFontObject
{
public FontFamily Font { get; set; }
public object BuildTextBlock
{
get { return new TextBlock { Text = GetFamilyName(Font) } }
}
public object BuildStyledFontName
{
get
{
return new Path { Data = GetStyledFontGeometryUsingCache() };
}
}
private Geometry GetStyledFontGeometryUsingCache()
{
Geometry geo;
lock(_fontGeometryCache)
if(_fontGeometryCache.TryGetValue(Font, out geo) return geo;
lock(_fontGeometryBuildLock)
{
lock(_fontGeometryCache)
if(_fontGeometryCache.TryGetValue(Font, out geo) return geo;
geo = BuildStyledFontGeometry();
lock(_fontGeometryCache)
_fontGeometryCache[Font] = geo;
}
}
static object _fontGeometryCache = new Dictionary<FontFamily, Geometry>();
static object _fontGeometryBuildLock = new object();
private Geometry BuildStyledFontGeometry()
{
var run = new GlyphRun
{
Characters = GetFamilyName(Font),
GlyphTypeface = GetGlyphTypeface(Font),
}
return run.BuildGeometry();
}
... GetFamilyName ...
... GetGlyphTypeface ...
// Call from low priority background thread spawned at app startup
publc static void PrefillCache()
{
foreach(FontFamily font in Fonts.SystemFontFamilies)
new MyFontObject { Font = font }.GetStyledFontGeometryUsingCache();
}
}
请注意,可以将缓存中的Geometry对象保存到磁盘,方法是将它们转换为PathGeometry,然后将其转换为PathGeometry迷你语言中的字符串。这将允许使用单个文件读取和放大来填充字体几何缓存。解析,所以你唯一一次看到任何延迟就是你第一次运行应用程序时,或者当你使用大量新字体运行它时。
答案 1 :(得分:0)
我的解决方案是不提前渲染所有字体,通过使用虚拟化面板显示字体列表,您只会加载适合屏幕的字体,它将首次减慢滚动速度,但它几乎不会引起注意用户。
查看http://www.bennedik.de/2007/10/wpf-fast-font-drop-down-list.html
顺便说一句,如果您使用VirtualizingStackPanel的组合,则必须在DataTemplate中设置TextBlock元素的宽度,否则在滚动期间下拉宽度将会改变。