Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MarkupExtension is not valid for Setter.Value. The only supported MarkupExtension types are DynamicResourceExtension and BindingBase or derived types

I have a custom markup extensions "ThemeExtension" to provide "SolidColorBrush" from my DefaultTheme.xaml ResourceDictionary.

Calling example: BorderBrush="{extensions:Theme Key= FooKeyValue}"

It is working without any problems on runtime, but SOMETIMES it starting to crash during design time and I can't develop anymore. The designer is crashed. Rebuild, Clean Solution, OS Restart is NOT helping anymore. If I change some value inside of the XAML code it is working for exactly for 1 drawing! And after that it crashes again!

Preview

preview

XAML Stacktrace

bei System.Windows.Setter.Seal()
bei System.Windows.SetterBaseCollection.Seal()
bei System.Windows.Style.Seal()
bei System.Windows.StyleHelper.UpdateStyleCache(FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle, Style& styleCache)
bei System.Windows.FrameworkElement.OnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
bei System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
bei System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
bei System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
bei System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
bei System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue)
bei System.Windows.FrameworkElement.UpdateStyleProperty()
bei System.Windows.FrameworkElement.OnInitialized(EventArgs e)
bei System.Windows.Controls.Primitives.Selector.OnInitialized(EventArgs e)
bei System.Windows.FrameworkElement.TryFireInitialized()
bei System.Windows.FrameworkElement.EndInit()
bei System.Windows.Controls.ItemsControl.EndInit()
bei MS.Internal.Xaml.Runtime.ClrObjectRuntime.InitializationGuard(XamlType xamlType, Object obj, Boolean begin)

ThemeExtension.cs

[MarkupExtensionReturnType(typeof(Color))]
public class ThemeColorExtension : ThemeExtension
{
    internal override object ModifyThemeValue(object value)
    {
        if (value is SolidColorBrush solidColorBrush)
            return solidColorBrush.Color;
        return value;
    }
}

[MarkupExtensionReturnType(typeof(SolidColorBrush))]
public class ThemeExtension : MarkupExtension
{
    // ##############################################################################################################################
    // Properties
    // ##############################################################################################################################

    #region Properties

    // ##########################################################################################
    // Public Properties
    // ##########################################################################################

    /// <summary>
    /// The Key in the Resource Theme file
    /// </summary>
    public string Key { get; set; }

    // ##########################################################################################
    // Private Properties
    // ##########################################################################################

    private static readonly List<ThemeExtension> _Cache = new List<ThemeExtension>();
    private static readonly ResourceDictionary _DefaultTheme;
    private static ResourceDictionary _CurrentTheme;

    private PropertyInfo _Property { get; set; }
    private DependencyProperty _DependencyProperty { get; set; }
    private WeakReference _TargetReference { get; set; }

    #endregion

    // ##############################################################################################################################
    // Constructor
    // ##############################################################################################################################

    #region Constructor

    static ThemeExtension()
    {
        _DefaultTheme = new ResourceDictionary
        {
            Source = new Uri("/HtPlcFramework;component/Themes/DefaultTheme.xaml", UriKind.Relative)
        };
        _CurrentTheme = _DefaultTheme;

        NavigationService.Navigated += _OnNavigated;
    }

    public ThemeExtension() { }

    #endregion

    // ##############################################################################################################################
    // public methods
    // ##############################################################################################################################

    #region public methods

    /// <summary>
    /// https://social.msdn.microsoft.com/Forums/vstudio/en-US/931d7bff-90b6-4a70-bb0b-3a097e1301a1/net-40-breaking-change-using-a-markup-extension-as-value-of-property-setter-in-xaml-style?forum=wpf
    /// </summary>
    /// <param name="serviceProvider"></param>
    /// <returns></returns>
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        if (target == null)
            return this;

        if (target.TargetObject != null && target.TargetProperty != null)
        {
            _TargetReference = new WeakReference(target.TargetObject);
            if (target.TargetProperty.GetType() == typeof(PropertyInfo))
            {
                _Property = (PropertyInfo)target.TargetProperty;
            }
            else if (target.TargetProperty is DependencyProperty)
            {
                _DependencyProperty = (DependencyProperty)target.TargetProperty;
            }
        }

        if (!_Cache.Contains(this))
            _Cache.Add(this);

        return ModifyThemeValue(_ReadThemeKey(Key));
    }

    /// <summary>
    /// Change the Theme set
    /// </summary>
    /// <param name="themeUri">Default is: new Uri("/HtPlcFramework;component/Themes/DefaultTheme.xaml", UriKind.Relative)</param>
    public static void ChangeTheme(Uri themeUri)
    {
        _CurrentTheme = new ResourceDictionary { Source = themeUri };

        foreach (ThemeExtension reference in _Cache)
        {
            reference._UpdateTheme();
        }
    }

    /// <summary>
    /// Get the current theme entry. Can be null!
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public static object ReadThemeKey(string key) => _ReadThemeKey(key);

    internal virtual object ModifyThemeValue(object value)
    {
        return value;
    }

    #endregion

    // ##############################################################################################################################
    // private methods
    // ##############################################################################################################################

    #region private methods

    private static void _OnNavigated(object sender, string layer)
    {
        _Cache.RemoveAll(ti => !ti._TargetReference.IsAlive);
    }

    private static object _ReadThemeKey(string key)
    {
        try
        {
            return _CurrentTheme[key] ?? _DefaultTheme[key];
        }
        catch (Exception)
        {
            Trace.WriteLine($"The key '{key}' was not found in {_CurrentTheme.Source}!");
            return null;
        }
    }

    private void _UpdateTheme()
    {
        if (_TargetReference.IsAlive)
        {
            if (_Property != null)
                _Property.GetSetMethod().Invoke(_TargetReference.Target, new object[] { _ReadThemeKey(Key) });
            else if (_DependencyProperty != null)
            {
                DependencyObject dependencyObject = _TargetReference.Target as DependencyObject;
                dependencyObject?.SetValue(_DependencyProperty, _ReadThemeKey(Key));
            }
        }
        else
        {
            _Cache.Remove(this);
        }
    }

    #endregion

} 

Related VStudio Developer Community post

https://developercommunity.visualstudio.com/content/problem/364029/foomarkupextension-is-not-valid-for-settervalue-th.html

Related post with no solution

{0} is not valid for Setter.Value. The only supported MarkupExtension types are DynamicResourceExtension and BindingBase or derived types

VS2010 Custom MarkupExtension


On the following website is an example of a "Markup Extension":

https://dzone.com/articles/extend-wpf-add-your-own-keywor (Download project example http://raasiel.typepad.com/MyXamlExtensions.zip)

If I run this example, I get also this annyoing exception! So maybe it is a problem with VisualStudio.

like image 838
Dominic Jonas Avatar asked Oct 23 '18 10:10

Dominic Jonas


1 Answers

Thanks to Michael because he pointed me in the right direction 👍

As you can see (https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Setter.cs,123) only DynamicResourceExtension are allowed in Setter as MarkupExtension.

So to get arround this ***** error message:

  1. Change base class MarkupExtension into DynamicResourceExtension
  2. provide a foo resource key (just to get arround the ArgumentNullException; see https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/DynamicResourceExtension.cs,43)
[MarkupExtensionReturnType(typeof(SolidColorBrush))]
public class ThemeExtension : DynamicResourceExtension
{

...

    public ThemeExtension() : base("foo")
    {
    }

...

}
like image 57
Dominic Jonas Avatar answered Nov 02 '22 18:11

Dominic Jonas