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

1

6

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.

Accelerator answered 23/10, 2018 at 10:23 Comment(4)
Is it really valid to return this from ProvideValue? I'm not sure if it's the cause of the problem, but it seems really odd for a markup extension to return its self rather than an instance of some other object that it translated. – Strander
I have added a Stacktrace for more details. Return this was a solution approach on a msdn forum. And it helped for some weeks. But know the problems starts again. Return null instead of this gives the same error. Btw. I have tried to debug the XAML exception (https://mcmap.net/q/1913593/-how-can-i-debug-xaml-designer-issues) and the target was always != null. – Accelerator
@DominicJonas Did you find any solution to this, because I have nearly exact the problem. I have read the Setter() .Net Framework source and there you can read, that Setter() only accepts DynamicResourceExtension or BindingBase as value. Custom MarkupExtensions are denied. See the method Seal() in referencesource.microsoft.com/#PresentationFramework/src/… – Featureless
Awesome! You've shown me the solution with this! The trick is to use the base class DynamicResourceExtension! πŸ˜ƒπŸ‘πŸ‘πŸŽˆπŸŽ‰ – Accelerator
A
4

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")
    {
    }

...

}
Accelerator answered 28/5, 2020 at 8:11 Comment(7)
I know, it is bit early, but have you already implemented it in your code, tested it and can confirm that your error is gone? – Featureless
Yes, the error is gone and I can use the Designer as I expect that. – Accelerator
Ok, great! In my case it was a different error and it is even weirder. Before you mentioned to use DynamicResource as a base class, I had the same idea yesterday and I ran into my error again. And the solution to my error is really weird and in my opinion a bug in the Setter class. The explanation is too long for a comment. Eventually I will write a longer answer, that may be interesting for the community. – Featureless
Dominic, please have a look into the chat. There may or may not be a problem. Once you inherit from DynamicResourceExtension, the ProvideValue function is never be called in my test. This might work with your solution because as you said, you are just only using one style at this point. But the switching between styles could be broken. – Featureless
I get the same behavior as Michael. If my markup extension is derived from DynamicResourceExtension and if it is used in a setter in a style definition, then the ProvideValue method is not called. This solution does not work for me. – Salto
What if you declare ProvideValue() with new instead of override? Like public new object ProvideValue(IServiceProvider serviceProvider) { ... – Latoria
@gt still method not called. – Giffard

© 2022 - 2024 β€” McMap. All rights reserved.