dotnet/wpf · error · ArgumentException

PropertyOfClassCannotBeNull (Typeface, TextRunProperties)

Error message

PropertyOfClassCannotBeNull (Typeface, TextRunProperties)

What it means

FetchTextRun finally checks that properties.Typeface is non-null and throws ArgumentException(SR.PropertyOfClassCannotBeNull with Typeface/TextRunProperties). The typeface drives glyph selection and run shaping, so it is mandatory per-run.

Solutions

  1. Set Typeface on the run's TextRunProperties (e.g. new Typeface("Segoe UI"))
  2. Resolve fonts via FontFamily/Typeface combination rather than leaving null
  3. Validate each run's properties in your TextSource before returning it

Example fix

// before
public override Typeface Typeface => null;
// after
public override Typeface Typeface => new Typeface("Segoe UI");
Defensive patterns

Strategy: type-guard

Validate before calling

if (run.Properties?.Typeface == null) run.Properties.Typeface = new Typeface("Segoe UI");

Type guard

bool HasTypeface(TextRun r) => r?.Properties?.Typeface != null;

Try / catch

try { formatter.FormatLine(...); } catch (ArgumentException) { /* set typeface and retry */ }

Prevention

When it happens

Trigger: A TextRun whose TextRunProperties.Typeface is null is fetched by the run cache during line formatting.

Common situations: Custom TextRunProperties not initializing Typeface; fallback font resolution failing silently and leaving null; copy constructors omitting the typeface field.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/584fcb8dd1e7fb0a. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/TextRunCacheImp.cs:131

                    if (properties == null)
                        throw new ArgumentException(SR.TextRunPropertiesCannotBeNull);

                    if (properties.FontRenderingEmSize <= 0)
                        throw new ArgumentException(SR.Format(SR.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties"));

                    double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm;

                    if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize)
                        throw new ArgumentException(SR.Format(SR.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize));

                    CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo);

                    if (culture == null)
                        throw new ArgumentException(SR.Format(SR.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties"));

                    if (properties.Typeface == null)
                        throw new ArgumentException(SR.Format(SR.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties"));
                }


                //
                // TextRun is specifial to SpanVector because TextRun also encodes position which needs to be 
                // consistent with the positions encoded by SpanVector. In run cache, the begining of a span 
                // should always correspond to the begining of a cached text run. If the end of the currently fetched 
                // run overlaps with the begining of an already cached run, the begining of the cached run needs to be 
                // adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range 
                // so we'll simply remove the overlapped runs here.
                //                

                // Move the rider to the end of the current run
                textRunSpanRider.At(cpFetch + textRun.Length - 1);
                _latestPosition = textRunSpanRider.SpanPosition;

                if (textRunSpanRider.CurrentElement != _textRunVector.Default)
                {

View on GitHub (pinned to 81131a70a4)