Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get value from DataContext to MarkupExtension

I'm using M-V-VM pattern

In my VM I have code like

public class ViewModel {
    public XmlDocument Document { ... }
    ....
}

I have a markup extension from which I would like to use said document

  public override object ProvideValue(IServiceProvider serviceProvider) {
        IProvideValueTarget valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        if (valueProvider != null) {
            DependencyObject target = valueProvider.TargetObject as DependencyObject;
            XmlDocument doc = Foo.GetDocument(target);
            if (doc != null) {
                var n = doc.SelectSingleNode("/.../text()");
                if (n != null) return n.Value;
            }
        }
        return "«" + ObjectProperty + "»";
    }

I have created attached property Foo.Document, and attached it to my Page (the DataContext of the page is set to an instance of my ViewModel class

<Page ... lc:Foo.Document="{Binding Document}">
  ...
</Page>

(in order to not having to type it as a parameter each and every time I use the markup extension)

Now, in my markup extension when I try to read the Document attached property I always get a null document. By debugging the binding it seeems like a timing issue in that attached property gets proper value after markup extension has been run.

Is is possible to get this to work somehow?

like image 496
mostlytech Avatar asked Jun 03 '09 10:06

mostlytech


1 Answers

The ProvideValue method gets called twice, once when the XAML is evaluated by the parser and once when the values are loaded. On this first call the the targetObject is simply a sort of dummy object called SharedDP and not the object that the markupextension is applied to. You need to skip this first call and only deal with the second call. This is code works in our app.

   public override object ProvideValue(IServiceProvider serviceProvider){                                
                var pvt = serviceProvider as IProvideValueTarget;
                if (pvt == null)
                {
                    return null;
                }


                var frameworkElement = pvt.TargetObject as FrameworkElement;
                if (frameworkElement == null)
                {
                    return this;
                }
//.... Code will run once the markup is correctly loaded
 var dataContext = frameworkElement.DataContext; 


    }
like image 166
Rob Avatar answered Sep 22 '22 00:09

Rob