none
.NET 4.0 Breaking Change - Using a Markup Extension as Value of Property Setter in XAML Style

    Question

  • In .NET 3.5SP1 I had a style with the following property setting. 

     <Setter Property="ToolTip" Value="{resource:Resource Group=Text, Key=BTN_CLOSE}"/>
    

    The markup extension will access our satellite assemblies and pull in the proper resource text for the tooltip. The reason this is done this way is our application is customizable. Using the markup extension allows our clients to use a key combination to see the Key for the text and then go modify the text value if they choose to do so.

    After upgrading to .NET 4.0 the error below occurs when the above xaml to define the tooltip on my button style is defined. Is there a different way to do this?

    System.Windows.Markup.XamlParseException: A 'Binding' cannot be set on the 'Value' property of type 'Setter'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

    at System.Windows.Markup.XamlReader.RewrapException(Exception e, Uri baseUri)

    at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)

    at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)

    at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)

    at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)

    at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)

    at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)

    at System.Windows.FrameworkElement.ApplyTemplate()

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at System.Windows.Controls.Canvas.MeasureOverride(Size constraint)

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)

    at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)

    at System.Windows.Controls.Grid.MeasureOverride(Size constraint)

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)

    at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint)

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at System.Windows.Controls.Border.MeasureOverride(Size constraint)

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at System.Windows.Window.MeasureOverrideHelper(Size constraint)

    at System.Windows.Window.MeasureOverride(Size availableSize)

    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

    at System.Windows.UIElement.Measure(Size availableSize)

    at System.Windows.Interop.HwndSource.Process_WM_SIZE(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam)

    at System.Windows.Interop.HwndSource.LayoutFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

    at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

    at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)

    at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)

    at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

    Monday, January 10, 2011 7:27 PM

All replies

  • I wrote the following code which failed to reproduce the error you mentioned. It shows a button with text "Hello" as expected.

      public class HelloExtension : MarkupExtension
      {
        public HelloExtension()
        {
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
          return "Hello";
        }
      }
    
      <Window.Resources>
        <Style TargetType="Button">
          <Setter Property="Content" Value="{local:Hello}" />
        </Style>
      </Window.Resources>
      <Grid>
        <Button></Button>
      </Grid>
    
    Could you provide a simpified version of your code which shows the error?
    Monday, January 10, 2011 7:54 PM
  • I guess I left out a key element. In order to allow the swapping between text and the keys we have a ResourceCache class which stores the values. In the provide value the part that is breaking the tooltip binding is the fact that we are returning binding.ProvideValue(serviceProvider). This is the same as we had it in .NET 3.5SP1.

     public override object ProvideValue(IServiceProvider serviceProvider)
        {
          ResourceCache resourceCache = ResourceCache.Instance;
          resourceCache["cacheKey"] = "hello";
    
          Binding binding;
    
          binding = new Binding();
          binding.Source = resourceCache;
          binding.Path = new PropertyPath("[" + "cacheKey" + "]");
          binding.Mode = BindingMode.OneWay;
    
          return binding.ProvideValue(serviceProvider);
    
        }
    Monday, January 10, 2011 9:54 PM
  • Hi Tammy,

    I can reproduce this issue, I compare the serviceProvider variable in .Net 3.5 and 4:

    .Net 4 redesigns the serviceProvider, and uses to an internal class instead of the Markup.ProvideValueServiceProvider in default at run-time. It may be a issue of .Net 4, that the serviceProvider dose not get the correct target property. I will send the consult internal, and any feedback I will update here. Meanwhile, youc ould submit a feedback on the Microsoft Connect site, our developers will aware of this issue. Thanks.

    Below is my simple test code, it can work in .Net 3.5, but not in .Net 4: http://cid-51b2fdd068799d15.office.live.com/self.aspx/.Public/TestCase/20110111%5E_CustomMarkupExtensionIssue%5E_TestCase.zip

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, January 11, 2011 3:12 AM
  • I'm having this problem as well,  and it will stop us from upgrading.

     

    Here is a link to Tammy's Connect submission

    Friday, January 21, 2011 6:30 PM
  • I've found the following workaround when facing a similar problem. Try and see if the target object is a setter and if it is just return the binder instead of the provided value.

     

    Not the cleanest solution, but works till a fix comes.

    So according to your example this fix would work:

    ResourceCache resourceCache = ResourceCache.Instance;
              resourceCache["cacheKey"] = "hello";
    
              Binding binding;
              binding = new Binding();
              binding.Source = resourceCache;
              binding.Path = new PropertyPath("[" + "cacheKey" + "]");
              binding.Mode = BindingMode.OneWay;
    
              IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    
              if (target != null)
              {
                if (target.TargetObject is Setter)
                {
                  return binding;
                }
              }
    
              return binding.ProvideValue(serviceProvider);

    • Edited by SimonLanthier Saturday, February 12, 2011 1:15 AM Code error
    • Proposed as answer by SimonLanthier Saturday, February 12, 2011 1:21 AM
    Saturday, February 12, 2011 1:12 AM
  • This worked for me. Thanks!
    Monday, February 14, 2011 4:47 PM
  • That is greate, thanks Simon for sharing thi s workaround!
    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, February 14, 2011 4:50 PM
  • Thank you so much Simon.

    I was very alarmed to see this is STILL an issue with .NET 4 over a year later, and was extremely grateful to find your workaround.

    Friday, May 11, 2012 11:46 AM