简体   繁体   中英

Get value from DataContext to MarkupExtension

I'm using MV-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?

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; 


    }

You could wire an event to the Loaded or Initialized event on the Page from your markupextensions, perhaps. Or perhaps you could put your markup extension in the XAML file after the Foo.Document is mentioned.

Thanks, Rob Relyea WPF/XAML Team my blog

Not the best way of doing it. But, this guy shows how to get DataContext using reflection:

[WPF] Using InputBindings with the MVVM pattern

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM