Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wpf 在后台线程上渲染字体并将其加载到FontCache?_Wpf_Fonts_Backgroundworker - Fatal编程技术网

Wpf 在后台线程上渲染字体并将其加载到FontCache?

Wpf 在后台线程上渲染字体并将其加载到FontCache?,wpf,fonts,backgroundworker,Wpf,Fonts,Backgroundworker,我试图显示一个字体选择器列表,与Blend中的列表类似: 与Blend一样,当FontFamilies未加载到FontCache中时,我会看到性能问题 似乎我所付出的代价是实际渲染给定FontSize的FontFamily并将其保存到FontCache所需的时间。一旦渲染字体在缓存中,问题就会消失 我尝试在后台线程上迭代Fonts.SystemFontFamilies集合,并向UI发送一个调用,该调用会导致隐藏的文本块更新(这会导致字体呈现) 当然,由于分派调用是连续发生的,它只会冲击UI,我

我试图显示一个字体选择器列表,与Blend中的列表类似:

与Blend一样,当FontFamilies未加载到FontCache中时,我会看到性能问题

似乎我所付出的代价是实际渲染给定FontSize的FontFamily并将其保存到FontCache所需的时间。一旦渲染字体在缓存中,问题就会消失

我尝试在后台线程上迭代Fonts.SystemFontFamilies集合,并向UI发送一个调用,该调用会导致隐藏的文本块更新(这会导致字体呈现)

当然,由于分派调用是连续发生的,它只会冲击UI,我会得到阻塞UI的相同净结果,直到所有字体都呈现并加载到FontCache


有人能很好地解决这个问题吗?他们似乎没有在Blend中找到解决方案,所以我认为这不是一个好的解决方案。

给你一些想法

想法1:完全在后台线程上提取字体。

字体实际上被加载到系统字体缓存(进程间),然后部分字体信息被复制到特定于线程的字体缓存中。填充系统字体缓存可能会带来足够好的速度提升。这可以通过一个低优先级后台线程来完成,该线程在应用程序启动时立即开始运行。因此,当用户下拉字体列表时,系统字体缓存应该已完全填充

Idea 2:自己缓存渲染的字体几何体

不要使用文本块,而是在组合框的数据模板中使用ContentPresenter对象,并将内容绑定到PriorityBinding。较低的优先级将使用默认字体生成文本块,较高的优先级将是IsAsync绑定,该绑定将使用适当的参数创建GlyphRun,对其调用BuildGeometry(),并返回路径对象内的几何体。创建的几何体对象可以缓存并再次返回,以便将来访问同一字体

这样做的结果是,项目最初将以默认字体显示,并在加载字体并创建其几何体后立即呈现为样式化字体。请注意,这可以与在单独的线程中预先填充缓存的代码结合使用

Idea 2的代码如下所示:

<ComboBox ItemsSource="{Binding MyFontObjects}">
  <ComboBox.ItemTemplate>
    <ContentPresenter>
      <ContentPresenter.Content>
        <PriorityBinding>
          <Binding IsAsync="True" Path="BuildStyledFontName" />
          <Binding Path="BuildTextBlock" />
        </PriorityBinding>
        ... close all tags ...
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();
  }
}

... 关闭所有标签。。。
其中MyFontObjets是一个IEnumerable对象,类似于:

<ComboBox ItemsSource="{Binding MyFontObjects}">
  <ComboBox.ItemTemplate>
    <ContentPresenter>
      <ContentPresenter.Content>
        <PriorityBinding>
          <Binding IsAsync="True" Path="BuildStyledFontName" />
          <Binding Path="BuildTextBlock" />
        </PriorityBinding>
        ... close all tags ...
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();
  }
}
公共类MyFontObject
{
公共字体系列字体{get;set;}
公共对象构建文本块
{
获取{返回新文本块{Text=GetFamilyName(字体)}
}
公共对象BuildStyledFontName
{
得到
{
返回新路径{Data=GetStyledFontGeometryUsingCache()};
}
}
私有几何图形GetStyledFontGeometryUsingCache()
{      
几何地理;
锁(_fontGeometryCache)
if(_fontGeometryCache.TryGetValue(Font,out geo)返回geo;
锁(_fontgometrybuildlock)
{
锁(_fontGeometryCache)
if(_fontGeometryCache.TryGetValue(Font,out geo)返回geo;
geo=BuildStyledFontGeometry();
锁(_fontGeometryCache)
_fontGeometryCache[Font]=geo;
}
}
静态对象_fontGeometryCache=新字典();
静态对象_fontgometrybuildlock=新对象();
私有几何图形BuildStyledFontGeometry()
{
var run=新的GlyphRun
{
Characters=GetFamilyName(字体),
GlyphTypeface=GetGlyphTypeface(字体),
}
返回run.BuildGeometry();
}
…GetFamilyName。。。
…字体。。。
//从应用程序启动时生成的低优先级后台线程调用
publc static void PrefillCache()
{
foreach(Fonts.SystemFontFamilies中的FontFamily字体)
新的MyFontObject{Font=Font}.GetStyledFontGeometryUsingCache();
}
}

请注意,缓存中的几何体对象可以通过将其转换为PathGeometry,然后再转换为PathGeometry mini语言中的字符串来保存到磁盘。这将允许使用单个文件读取和解析来填充字体几何体缓存,因此您唯一看到延迟的时间是首次运行应用程序时,或者使用大量新字体。

给你一些想法

想法1:完全在后台线程上提取字体。

字体实际加载到系统字体缓存中(进程间)然后,部分字体信息被复制到特定于线程的字体缓存中。填充系统字体缓存可能会带来足够好的速度提升。这可以通过一个低优先级的后台线程来完成,该线程在应用程序启动时就开始运行。因此,当用户下拉字体列表时应完全填充系统字体缓存

Idea 2:自己缓存渲染的字体几何体

不要使用TextBlock,而是在组合框的DataTemplate中使用ContentPresenter对象,将内容绑定到PriorityBinding。较低的优先级将使用默认字体生成TextBlock,较高的优先级将是IsAsync绑定,该绑定将使用适当的参数创建GlyphRun,调用BuildGeometry()然后返回路径对象中的几何体。创建的几何体对象可以缓存并再次返回,以便将来访问同一字体

这样做的结果是,项目最初将以默认字体显示,并在加载字体并创建其几何体后立即呈现为样式化字体。请注意,这可以与在单独线程中预先填充缓存的代码结合使用

Idea 2的代码如下所示:

<ComboBox ItemsSource="{Binding MyFontObjects}">
  <ComboBox.ItemTemplate>
    <ContentPresenter>
      <ContentPresenter.Content>
        <PriorityBinding>
          <Binding IsAsync="True" Path="BuildStyledFontName" />
          <Binding Path="BuildTextBlock" />
        </PriorityBinding>
        ... close all tags ...
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();
  }
}