Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting TextBox FontFamily property from Style causes XamlObjectWriterException #2194

Closed
wieslawsoltes opened this issue Dec 26, 2018 · 20 comments
Assignees
Labels

Comments

@wieslawsoltes
Copy link
Contributor

<Style Selector="TextBox.export">
    <Setter Property="FontFamily" Value="Consolas"/>
</Style>

version: 0.7.1-build989-beta

Portable.Xaml.XamlObjectWriterException
  HResult=0x80131500
  Message=Set value of member '{clr-namespace:Avalonia.Styling;assembly=Avalonia.Styling}Setter.Value' threw an exception
  Source=Avalonia.Markup.Xaml
  StackTrace:
   at Portable.Xaml.XamlObjectWriterInternal.SetValue(XamlMember member, Object target, Object value)
   at Portable.Xaml.XamlObjectWriterInternal.SetValue(XamlMember member, Object value)
   at Portable.Xaml.XamlObjectWriterInternal.OnWriteEndMember()
   at Portable.Xaml.XamlWriterInternalBase.WriteEndMember()
   at Portable.Xaml.XamlServices.Transform(XamlReader xamlReader, XamlWriter xamlWriter, Boolean closeWriter)
   at Avalonia.Markup.Xaml.AvaloniaXamlLoader.LoadFromReader(XamlReader reader, AvaloniaXamlContext context, IAmbientProvider parentAmbientProvider)
   at Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(Stream stream, Assembly localAssembly, Object rootInstance, Uri uri)
   at Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(Type type, Object rootInstance)
   at Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(Object obj)
   at ThemeEditor.App.Initialize() in C:\DOWNLOADS\GitHub\ThemeEditor\src\ThemeEditor\App.xaml.cs:line 10
   at Avalonia.Controls.AppBuilderBase`1.Setup()
   at Avalonia.Controls.AppBuilderBase`1.Start[TMainWindow](Func`1 dataContextProvider)
   at ThemeEditor.Program.Main(String[] args) in C:\DOWNLOADS\GitHub\ThemeEditor\src\ThemeEditor\Program.cs:line 19

Inner Exception 1:
NullReferenceException: Object reference not set to an instance of an object.
@ksigne
Copy link

ksigne commented Dec 31, 2018

Solution is ridiculously simple.

In file \src\Markup\Avalonia.Markup.Xaml\Converters\FontFamilyTypeConverter.cs replace
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { var s = (string)value; return FontFamily.Parse(s, context.GetBaseUri()); }
to
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { var s = (string)value; return FontFamily.Parse(s, context**?**.GetBaseUri()); }

@Gillibald
Copy link
Contributor

Gillibald commented Jan 3, 2019

@ksigne The real issue here is the non present context. Your "solution" would just hide it in my opinion.

This issue is not about FontFamily. Uris in general fail to work with avares scheme inside a style. This needs to be fixed.

@ksigne
Copy link

ksigne commented Jan 3, 2019

I don't see a big trouble with non present context. I've checked it and GetBaseUri() is usually null so it is valid value. It makes some limitation on how uris are written but not blocking some converters totally at least.

@Gillibald
Copy link
Contributor

Without a present context relative uris would never work. That would still produce an exception when you expect it to work. Uris defined in styles should work in the same way as uris defined directly on controls.

@ksigne
Copy link

ksigne commented Jan 3, 2019

Well, I was able to pull baseuri into setter converter.
What I did I moved

 if (target is Setter &&
                    member.Name == nameof(Setter.Value) &&
                    value is string)
            {
                (target as Setter).Value = SetterValueTypeConverter.ConvertSetterValue(
                                        new TypeDescriptorImpersonator(this.Settings),
                                        member.DeclaringType.SchemaContext,
                                        CultureInfo.InvariantCulture,
                                        target as Setter,
                                        value);
                return true;
            }

from AvaloniaXamlType which is typewise singleton and out of serialization context to AvaloniaXamlObjectWriter which is instanced each time you load xaml.
Key change is:

protected internal override bool OnSetValue(object target, XamlMember member, object value)
        {
            if (_delayedValuesHelper.TryAdd(target, member, value))
            {
                return true;
            }

            if (target is Setter &&
                    member.Name == nameof(Setter.Value) &&
                    value is string)
            {
                (target as Setter).Value = SetterValueTypeConverter.ConvertSetterValue(
                                        new TypeDescriptorImpersonator(this.Settings),
                                        member.DeclaringType.SchemaContext,
                                        CultureInfo.InvariantCulture,
                                        target as Setter,
                                        value);
                return true;
            }

            return base.OnSetValue(target, member, value);
        }

The rest is TypeDescriptorImpersonator. You cannot access actual Service Resolver as it is internal. But the only service is actually located is through this method:

public static Uri GetBaseUri(this ITypeDescriptorContext ctx)
        {
            return ctx.GetWriterSettings()?.Context?.BaseUri;
        }

so you implement ITypeDescriptor which resolves only IXamlObjectWriterFactory which can only return GetParentSettings.

Other way is to add public property on XamlObjectWriter inside Portable.Xaml with following getter:
public IXamlObjectWriterFactory WriterFactoryService => this.intl. So then you return this one for impersonator.

@x2bool
Copy link

x2bool commented Jan 6, 2019

Do temp workarounds exist for this issue?

@Gillibald
Copy link
Contributor

I guess you could define the font family in code by defining a static property and reference it in your style.

@pr8x
Copy link
Contributor

pr8x commented Jan 21, 2019

Any plans to fix this in the near future 😅 ?

@grokys
Copy link
Member

grokys commented Feb 22, 2019

@kekekeks will this be fixed by the XAML compiler?

@kekekeks
Copy link
Member

I've added

      <StackPanel.Styles>
          <Style Selector="TextBox">
              <Setter Property="FontFamily" Value="Consolas"/>
          </Style>
      </StackPanel.Styles>    

To TextBoxPage.xaml and it produced the following (decompiled):

Styles styles = ((StyledElement)stackPanel5).Styles;
Style style;
Style item = style = new Style();
_AvaloniaXamlIlContext.ParentsStack.Add(style);
Style style2 = style;
style2.Selector = ((Selector)null).OfType(typeof(TextBox));
IList<ISetter> setters = style2.Setters;
Setter setter;
Setter item2 = setter = new Setter();
_AvaloniaXamlIlContext.ParentsStack.Add(setter);
Setter setter2 = setter;
setter2.Property = TemplatedControl.FontFamilyProperty;
setter2.Value = (object)(FontFamily)new FontFamilyTypeConverter()
    .ConvertFrom(_AvaloniaXamlIlContext, CultureInfo.InvariantCulture, "Consolas");
_AvaloniaXamlIlContext.ParentsStack.RemoveAt(_AvaloniaXamlIlContext.ParentsStack.Count - 1);
setters.Add(item2);
_AvaloniaXamlIlContext.ParentsStack.RemoveAt(_AvaloniaXamlIlContext.ParentsStack.Count - 1);
styles.Add(item);

which seems to work.

@jmacato jmacato closed this as completed Feb 23, 2019
@jmacato jmacato reopened this Feb 23, 2019
@kekekeks
Copy link
Member

@jmacato XAML compiler will be probably merged after 0.8 release.

@x2bool
Copy link

x2bool commented Feb 23, 2019

@kekekeks Does that mean AvaloniaXamlLoader will be gone after 0.8?

@jmacato
Copy link
Member

jmacato commented Feb 23, 2019

@kekekeks sorry i misclicked the close issue button, i was trying to reply if we could modify/fix the TypeConverter for FontFamily to avoid ParentStack

@Gillibald
Copy link
Contributor

The issue is not the converter the issue is within Portable.Xaml

@kekekeks
Copy link
Member

@x2bool AvaloniaXamlLoader.Load(this) will still be there. After-build task will replace this call by compiled IL invocation. Other high-level APIs for loading XAML from string will use compiler's System.Reflection.Emit backend. This is a huge set of changes, so we want to postpone them after 0.8 release which will happen soon, probably right after VS2019 release.

@grokys grokys added the bug label Mar 1, 2019
@techtim
Copy link

techtim commented Mar 21, 2019

I guess you could define the font family in code by defining a static property and reference it in your style.

Can you give an example how to do it?
After update to 0.7.1-cibuild0001679-beta , got same error on
<Setter Property="FontFamily" Value="resm:EMU.Avalonia.Assets.Fonts.FiraCode-Medium.ttf?assembly=EMU.Avalonia#Fira Code" />

@kekekeks kekekeks self-assigned this May 22, 2019
@kekekeks
Copy link
Member

Compiler is now merged, so it should work.
#2194 (comment)

@MiKaMaru
Copy link

MiKaMaru commented Aug 1, 2019

Hello! I have similar problem, but with Button control, and now, at Avalonia 0.8.1, FontFamily from style causes XamlObjectWriterException

@MiKaMaru
Copy link

MiKaMaru commented Aug 1, 2019

Hello! I have similar problem, but with Button control, and now, at Avalonia 0.8.1, FontFamily from style causes XamlObjectWriterException

I've tried it on Avalonia 0.8.999-cibuild0003136-beta. Problem exist

@arun-kat
Copy link

arun-kat commented Sep 5, 2019

I also see the same issue as MiKaMaru with the Button control.

I'm able to set the other properties using the syntax below, but I get the error: "NullReferenceException: Object reference not set to an instance of an object." for the FontFamily line.

<Style Selector="Button">
  <Setter Property="Background" Value="LightGray"/>
  <Setter Property="FontSize" Value= "12"/>
  <Setter Property="BorderThickness" Value= "1"/>
  <Setter Property="BorderBrush" Value= "#6C6F70"/>
  <Setter Property="FontFamily" Value ="Arial"/>
</Style>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests