This is a known WPF issue but still affects applications created with Material Design XAML Toolkit since it relies on the Roboto font. See https://stackoverflow.com/a/31452979. The answer at StackOverflow has a broken link to a issue at Microsoft Connect. I have not been able to find out if this bug is going to be fixed. I have not found any official comments about this issue.
According to my observations the leaked object UnmanagedMemoryStream is very small in size, about 32 bytes. This makes it almost invisible to the memory footprint of many applications. I observed this leak in an application with many bindings which are updated frequently. The application is not expected to be restarted often. These traits would lead to huge amounts of UnmanagedMemoryStream being created over time.
You need Visual Studio Diagnostics Tool, dotMemory or another memory profiler to observer the memory leak. I have used both Visual Studio Diagnostics Tool and dotMemory to track down the leak and confirm it has to do with font loading.
UnmanagedMemoryStreamUnmanagedMemoryStreamUnmanagedMemoryStreamThe workaround suggested by Israel Altar is to use file based fonts instead of resource based fonts. I have confirmed that this workaround "_works_ _on_ _my_ _machine_".
FontFamily instance:public class MaterialDesignFonts
{
public static FontFamily Roboto { get; }
static MaterialDesignFonts()
{
var fontPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Roboto\");
Roboto = new FontFamily(new Uri($"file:///{fontPath}"), "./#Roboto");
}
}
Add the fonts with "Build Action" None and "Copy to Output Directory" Copy if newer
<None Include="Resources\Roboto\Roboto-Black.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-BlackItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-Bold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-BoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-Italic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-Light.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-LightItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-Medium.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-MediumItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-Thin.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\Roboto-ThinItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\RobotoCondensed-Bold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\RobotoCondensed-BoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\RobotoCondensed-Italic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\RobotoCondensed-Light.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\RobotoCondensed-LightItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Roboto\RobotoCondensed-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
FontFamily attributes on your Window types. This is the only place I use the Roboto font in my application:FontFamily="{x:Static local:MaterialDesignFonts.Roboto}"
TextElement.FontFamily="{x:Static local:MaterialDesignFonts.Roboto}"
How do we handle this issue in Material Design XAML Toolkit? Possible actions below but I let you guys weigh in on the way forward.
MaterialDesignFonts type that takes care of writing temporary font files to disk before loading{StaticResource MaterialDesignFont} or {x:Static materialDesign:MaterialDesignFonts.Roboto}Personally I think it would be nice if I could use the material design fonts without memory leakage out of the box :)
@ButchersBoy I know you've got a lot on your plate but if you think this is a good idea I can provide a PR:
- Keep the font as a resource in the library
- Add a MaterialDesignFonts type that takes care of writing temporary font files to disk before loading
- The consumer of the library can decide if they want to use {StaticResource MaterialDesignFont} or {x:Static materialDesign:MaterialDesignFonts.Roboto}
I would also add some info about this behaviour in the wiki. Or I could just provide the info in the wiki.
Yes, good idea, so long as we keep all the existing stuff in tact as you have said.
I have put in a "fix" for this that I am looking to get feedback on. It is an opt-in style feature.
Inside of your project file you can set <IncludeMaterialDesignFont>True</IncludeMaterialDesignFont>. This should be placed inside of your default PropertyGroup (the one without any Condition attribute). This will cause the font files to be added to your output directory (ie. bin\Debug) under Resources\Roboto. You can then make use of these font files by using the new static property MaterialDesignFonts.Roboto. Like this:
TextElement.FontFamily="{x:Static wpf:MaterialDesignFonts.Roboto}"
Now there are a couple things worth considering here:
MaterialDesignFonts.Roboto property. This matches the default behavior when you typo a FontFamily name. Is this intuitive?Nice work! I like it. It is simple and we can expand on it if we have to. Keep it!
Have you seen this different solution: using a markup extension to fix memory leak
With this:
FontFamily="{local:FontExplorer Key=Roboto}">
@quicoli I have not seen that. I think I like it better, though I think i am going to leave in the option for people to include the font files in their project if they want. I dropped the MaterialDesignFonts static class in favor of the MaterialDesignFont markup extension.
I'll create a package with this resource implementation so who can't update
to the future version can at least have a fix.
See you
On Sun, Dec 8, 2019, 07:33 Kevin B notifications@github.com wrote:
@quicoli https://github.com/quicoli I have not seen that. I think I
like it better, though I think i am going to leave in the option for people
to include the font files in their project if they want. I dropped the
MaterialDesignFonts static class in favor of the MaterialDesignFont
markup extension.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/746?email_source=notifications&email_token=AB775D4OYM2HEUJRIHZMBPTQXSPLVA5CNFSM4DSVHSMKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGGX2AA#issuecomment-562920704,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AB775D5HB6672TGTMBKPBCDQXSPLVANCNFSM4DSVHSMA
.
I just published a nuget package for this.
You can the source
Most helpful comment
@quicoli I have not seen that. I think I like it better, though I think i am going to leave in the option for people to include the font files in their project if they want. I dropped the
MaterialDesignFontsstatic class in favor of theMaterialDesignFontmarkup extension.