none
Data Binding between non FrameworkElements - can it be done in XAML?

    Question

  • Hello All,

    Suppose I have two classes that are not Framework elements:

    public class MyClass1
    {
        public string Foo { get{...} set{...} }
        ...
    }

    public class MyClass2 : DependencyObject
    {
        public string Bar { ... }
        public static DependencyProperty BarProperty = DependencyProperty.Register( "Bar", ... );
        ...
    }

    I am able to create instances of these classes as resources in a Xaml document and I can bind GUI elements to the MyClass1.Foo and MyClass2.Bar properties as expected.

    In code I can bind MyClass2.Bar (target) up to MyClass1.Foo (source) using the System.Windows.Data.Binding class.

    How can I perform a similar binding in XAML?  For example, I would like to be doing the following:

    <SomeElement.Resources>
        <z:SomeContainer x:Key="A">
            ...
                ...
                    <z:MyClass1 x:Name="FindMe" Foo="Hello World">
                ...
            ...
        </z:SomeContainer>
        <z:SomeOtherContainer x:Key="B">
            ...
                ...
                    <z:MyClass2 Bar={Binding ElementName=FindMe, Path=Foo}>
                ...
            ...

        </z:SomeOtherContainer>

    </SomeElement.Resources>

    The binding fails because MyClass2 is not a subclass of FrameworkElement or FrameworkContentElement.  It appears that the registration of "FindMe" happens in the correct NameScope, but the MyClass2 instance can not determine which namescope to resolve the name "FindMe" in.

    The error shown in the log window is:

            System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element.

    I do not require forward references, so I can write a MarkupExtention that resolves the name while the Xaml is being parsed rather than delaying the binding as the Binding class does.  The problem I face with this approach is determining the "current" namescope to perform the query on.  I can not find anything in the MSDN documentation nor hooks in the XamlParser class that will help me out here.

    The only "solutions" I have found tell me to subclass FrameworkElement, which is completely inappropriate:

        http://forums.msdn.microsoft.com/en-US/wpf/thread/aff23943-5483-40b2-816b-4ce687bc6bf8/

    Nick's post here points to what sounds like many point solutions to the problem (and worries me about the coupling going in within WPF), while a simple "current namescope" in thread local storage would probably solve the problem once and for all:

        http://blogs.msdn.com/nickkramer/archive/2006/08/18/705116.aspx

    Any suggestions?


    Cheers,

    Dan


    • Changed type Marco Zhou Tuesday, September 23, 2008 3:39 AM OP doesn't revert back
    • Changed type Marco Zhou Thursday, October 09, 2008 2:52 AM back to question
    Tuesday, September 09, 2008 4:19 AM

All replies

  • ElementName data binding will traverse the logical tree to find the named element given a specified namescope which means that ElementName binding cannot be used within Resources, because if the resource is not referenced, it will not be part of the logical tree. And, in order to make data binding work in the way you want, you might try something like the following:

    <z:MyClass2 Bar="{Binding Source={StaticResource FindName}, Path=Foo}"/>

    Note that "FindMe" here is a resource key rather than Name.
    Thursday, September 11, 2008 5:59 AM
  • Hello Marco,

    Thank you for your reply.

    I currently have two workarounds for this scenario, one of which maps to your suggestion:

    Workaround 1: reference the static resource and make a complecated path for the binding.  This is really poor as the binding path is very fragile - any structural change to the data will break the path - it is exactly this problem that reference by name overcomes:

    <SomeElement.Resources>
        <z:SomeContainer x:Key="A">
            ...
                ...
                    <z:MyClass1 x:Name="FindMe" Foo="Hello World">
                ...
            ...
        </z:SomeContainer>
        <z:SomeOtherContainer x:Key="B">
            ...
                ...
                    <z:MyClass2 Bar={Binding Source={StaticResource A}, Path=Some.Long.Property["Path"].To.Foo}>
                ...
            ...

        </z:SomeOtherContainer>

    </SomeElement.Resources>


    Workaround2: Promote the "FindMe" element to being a resource and mount it into it's original location using a StaticResource markup extension.  MyClass2 can then simply bind to the resource.  This is much nicer as the binding is no longer fragile.

    <SomeElement.Resources>
        <z:MyClass1 x:Key="FindMe" Foo="Hello World">
        <z:SomeContainer x:Key="A">
            ...
                ...
                    <StaticResource ResourceKey="FindMe" />
                ...
            ...
        </z:SomeContainer>
        <z:SomeOtherContainer x:Key="B">
            ...
                ...
                    <z:MyClass2 Bar={Binding Source={StaticResource FindMe}, Path=Foo}>
                ...
            ...

        </z:SomeOtherContainer>

    </SomeElement.Resources>


    Now imagine another scenario - I am actuallly using XAML to assemble and wireup non-WPF applications, so I do not use the FrameworkElement and friends.  My assoication with WFP ends with the DependencyObject.  I know that a namescope is created at the root of the XAML document by the parser to collect together elements named using x:Name - how can a non-FrameworkElement class refer to this root namescope during XAML parsing?  So far it seems impossible...

    I may resort to creating a special node high up  in my douments that implement INameScope and see how that goes, but I feel that I am experimenting rather than reasoning about how XAML work - something that makes me uncomfortable.

    It seems a great shame that the Binding class can behave so well in C# code with used with non-WPF objects, but it seems very limited in XAML due to poor design regarding element name resolution when parsing the XAML document.




    Thursday, September 11, 2008 9:52 AM
  • -> It seems a great shame that the Binding class can behave so well in C# code with used with non-WPF objects, but it seems very limited in XAML due to poor design regarding element name resolution when parsing the XAML document.

    In Code, you always know which binding source you could use, because you have the reference at hand, but in XAML, there is no concept of variable references etc, you need to invent a way to specify the binding source, that's why ElementName comes into play, so in order to find the correct ElementName, you need to use a data structure to keep those elements, that's why logical tree comes into play. And because elements resides at different level of the logical tree might end up having the same name, so in order to prevent naming conflict, name scope comes into play.

    So it's fairly possible to enable data binding in XAML if you are working with non-WPF XAML elements/objects, you need to hook up the correct content model to construct the logical tree, and you also need to implement the INameScope to support name scoping, that's pretty well all you need to do here.

    In order to hook up the right content model, you might find the following post useful:
    http://shevaspace.blogspot.com/2007/02/visual-level-programming-vs-logical.html

    Hope this helps

    Friday, September 12, 2008 5:40 AM
  • Hi Marco,

    From my experiments it seems that only FrameworkElements and FrameworkContentElements can be part of the LogicalTree, even though the interface to the LogicalTreeHelper would suggest otherwise as it deals with DependencyObjects as parents and children.

    Let me draw your attention to the implementation of LogicalTreeHelper.GetParent( DependencyObject ) as decompiled by reflector:
    public static DependencyObject GetParent(DependencyObject current)
    {
        if (current == null)
        {
            throw new ArgumentNullException("current");
        }
        FrameworkElement element = current as FrameworkElement;
        if (element != null)
        {
            return element.Parent;
        }
        FrameworkContentElement element2 = current as FrameworkContentElement;
        if (element2 != null)
        {
            return element2.Parent;
        }
        return null;
    }
    Quite clearly this confirms my suspicions that the logical tree is indeed composed of only FrameworkElements (FE) and FrameworkContentElements (FCE).

    Your comment:

    "So it's fairly possible to enable data binding in XAML if you are working with non-WPF XAML elements/objects, you need to hook up the correct content model to construct the logical tree, and you also need to implement the INameScope to support name scoping, that's pretty well all you need to do here."

    Is not correct.  To form the logical tree that is searched by the Binding class, one MUST use the FE and FCE.

    If we turn our attention to the fact that we can place the x:Name attribute on any element and it will register with the "nearest" namescope, one would expect to find a stack of Namescopes being kept during parsing of the XAML file.  Sure enough, if you look at BamlRecordReader.DoRegisterName(), we see elements that are non-WPF objects being registered with the namescope on top of the stack that is stored in the ParserContext.

    Now if the MarkupExtension were given access to this stack, or even the top of this stack, my problem would be solved - the markup extension woud simply look for the object specified by the "ElementName" in the current namescope.

    It's really starting to look like XAML and WPF are coupled in ways that limits the usefulness of XAML in non-WPF applications.  Perhaps I should restate that - there seems to be a lot of useful, generic, reusable stuff in WPF (like the Binding class) that can not work inXAML outside of the WFP framework.

    I feel I have two options here:

    1) Somehow get a handle to the stack of namescopes - I do not even know if this is possible.  Help anyone?

    2) Invent my own element naming scheme (I think attached properties will help me here as I want to attach a name to objects that do not know they are named) and augment the Binding class to be aware of my naming scheme.


    Cheers,

    Dan

    Friday, September 12, 2008 12:52 PM
  • Dan, you are right, I thought AddLogicalChild() is available for DependencyObjects, but it turns out this is not the case, I need to fix my memory right now:)

    -> Somehow get a handle to the stack of namescopes - I do not even know if this is possible.  Help anyone?

    I don't think you can, even if you can, it only works for XAML parsing, but for runtime name registration, and the WPF's data binding still relies on the existence of element tree to make ElementName data binding work.

    -> Invent my own element naming scheme (I think attached properties will help me here as I want to attach a name to objects that do not know they are named) and augment the Binding class to be aware of my naming scheme.

    This might be an option, create your own concept of "element tree" neither logical nor visual, introduces your own element naming scheme, and element name lookup.

    Hope this helps
    • Edited by Marco Zhou Thursday, October 09, 2008 2:46 AM bad typo
    Tuesday, September 16, 2008 5:20 AM
  • We are changing the issue type to “Comment” because you have not followed up with the necessary information. If you have more time to look at the issue and provide more information, please feel free to change the issue type back to “Question” by clicking the "Options" link at the top of your post, and selecting "Change Type" menu item from the pop menu. If the issue is resolved, we will appreciate it if you can share the solution so that the answer can be found and used by other community members having similar questions.

     

    Thank you!

    Tuesday, September 23, 2008 3:39 AM
  • Hello Marco,

    I do apologize - I should have posted back here sooner.

    In the end I implemented my own namespace and namespace searching methods using an interface similar to INamescope and some static utility methods.  This approach also let me implement namespace search in a manner that better suits my needs, such as using the hiding rule for nested namespaces.

    As I did not require forward references in the XAML document to resolve, the implementation is simple.  This solution is specific to my object model, not a general solution at the document/parser layer, which is a shame as I expect to hit this issue time and time again if I continue to use XAML.

    While going through all this, it bugged me the StaticResourceExtension was able to resolve names in parts of the XAML document where there is no Logical/Visual tree - which is exactly what I was trying to do.  So, I did some poking around with reflector, which is something I don't like to do, but WPF is not a very well behaved black box.

    The StaticResourceExtension contains code like the following:

        bamlReader = serviceProvider.GetService(typeof(IBamlReader)) as IBamlReader;

    The IBamlReader interface is exactly what I need as you can get to the stack of namescopes maintained by the XAML parser from the IBamlReader interface.  The IBamlReader interface, however, is marked internal, so I can not access it.  It seems very strange to me that Microsoft would keep in interface like this private - if it is required to implement classes like the StaticResourceExtension, then surely people extending XAML/WPF will need the interface also.  It seems to me that the "X" in XAML is dubious, even as a marketing construct.

    The underlying principal for building extensible frameworks is the Open/Closed Principal as coined by Bertrand Meyer.  For those unfamiliar with this concept, see http://en.wikipedia.org/wiki/Open/closed_principle.  However, this principal is clearly not followed though WPF.  The example sighted in an earlier post is a classic example where an abstract object is downcasted to two known concrete types in order to navigate the logical tree, precluding extension of the logical tree with other types.  Seriously, someone in a back room at MS must know that code like this is just plain wrong:

    FrameworkElement element;
    FrameworkContentElement element2;
    DependencyObject objectData = (DependencyObject) context.ObjectData;
    Helper.DowncastToFEorFCE(objectData, out element, out element2, false);
    if (element != null)
    {
        unsetValue = element.FindResourceOnSelf(resourceNameObject,
            allowDeferredResourceReference, mustReturnDeferredResourceReference);
    }
    else if (element2 != null)
    {
        unsetValue = element2.FindResourceOnSelf(resourceNameObject,
            allowDeferredResourceReference, mustReturnDeferredResourceReference);
    }

    And it is everywhere!  The really, really alarming bit is code like this:

    internal static void SpecialDowncastToFEorFCE(DependencyObject d,
        out FrameworkElement fe, out FrameworkContentElement fce, bool throwIfNeither)
    {
        if (FrameworkElement.DType.IsInstanceOfType(d))
        {
            fe = (FrameworkElement) d;
            fce = null;
        }
        else if (FrameworkContentElement.DType.IsInstanceOfType(d))
        {
            fe = null;
            fce = (FrameworkContentElement) d;
        }
        else
        {
            if (throwIfNeither && !(d is Visual3D))
            {
                throw new InvalidOperationException(System.Windows.SR.Get(
                    System.Windows.SRID.MustBeFrameworkDerived, new object[] { d.GetType() }));
            }
            fe = null;
            fce = null;
        }
    }

    Let me paraphrase - someone wanted to extend WPF by introducing the Visual3D concept and hit all kinds of problems to do with all these downcasts to FE and FCE.  So, they added a "Special" downcast to avoid the problem rather than fix the design to allow for such extension.  Tell you what, I wouldn't want to be passing "throwIfNeither = true" into that function...

    My understanding was that WPF is a layer on top of XAML.  This is not the case, they are tightly coupled.  This makes the utility of XAML for non-WPF objects very limited.

    Sorry if I sound negative with this post, but I started off trying to do something that should be perfectly reasonable - obtain a reference to an element in a document during parsing.  In DOM you call it getElementById() - where is the XAML equivalent?  The premise was reasonable, the discoveries were shocking, hence my tone.  I just hope that Microsoft appreciates the feedback.

    Cheers,

    Dan
    Wednesday, October 08, 2008 3:21 PM
  • If you look at the GridView  syntax in xaml it looks like:

    <GridView>
     <GridViewColumn DisplayMemberBinding="{Binding UserName}" />
    </GridView>
    
    
    

    GridViewColumn is not FrameWorkelement and it doest not have datacontext and it is not in Logical neither VisualTree.

    A made my own control DetailsView, with very similar usage, but I made my own DetailsViewRow with DisplayMemberBinding dependency prop instead of GridViewColumn. And the error ' Cannot find governing FrameworkElement ..bla bla...' occured.

    hmm, how msguys did it in GridViewColumn. I looked at GridViewColumn definition and  couldn't find DisplayMemberBindingProperty. hmm where it is? Usually I'm not very pleased when I can't find something but this time I was :)

    GridViewColumn.DisplayMemberBinding is not DependencyProperty but the CLR property.
    It causes that the 'DisplayMemberBinding="{Binding UserName}" ' peace of code do not set the binding, but makes new instance of Binding class.

    well, the point is:

    In you own classes you can write property of type BindingBase. It allows you to write bindings from not FrameworkElements in xaml and parse it without error. Then you have to use the binding at the right place in code. in my case it is something like that:

    DetailsViewRow row = ...
    var
    
     cell = (FrameworkElement)row.CellTemplate.LoadContent();
    cell.SetBinding(ContentControl.DataContextProperty, field.DisplayMemberBinding);
    ...
    myContentControl.Children.Add(cell);
    

    It does not cover all scenarious that causes the problem, but for some of them (e.g mine :) works nicely.

    Tuesday, February 01, 2011 2:58 PM