locked
NameScope issue (I think) RRS feed

  • Question

  • While converting 1 of my project to beta 2, I encountered some errors with dynamically loaded objects created via XamlReader.Load(rawxml). In 1 case rawxml contains storyboards, which when they ran, I got an error that they could not find the TargetName. I ended up using the following crude workaround: 

    //creation 

    Storyboard m_Storyboard = (Storyboard)XamlReader.Load(AnimationXaml);

    //addition to DOM

    ((Canvas)Content).Resources.Add((string)m_Storyboard.GetValue(FrameworkElement.NameProperty), m_Storyboard);

    //the workaround

    foreach(Timeline t in m_Storyboard.Children)

    {

    if ("rectangle" == (string)t.GetValue(Storyboard.TargetNameProperty))

    {

    Storyboard.SetTarget(t, rectangle);     //target rectangle

    }

    else

    {

    Storyboard.SetTarget(t, rectangle1);    //otherwise target rectangle1

    }

    }

    Now this all ran fine in beta 1(without the workaround.) I also had some other dynamically loaded Xaml where my user control's FindName ceased to work and I had to call FindName on the root of the dynamically created content. So my question is, is my dynamic content not getting the right NameScope, if so, how do I fix it, OR am I completely off base and need to change something else. Thanks in advance.

    Sunday, June 8, 2008 6:58 PM

Answers

  • Hello, actually XamlReader.Load DOES introduce a new name scope. That's why your Storyboard can't find the target to animate. Because the Storyboard and the Rectangle are in different name scopes. But why do you want to load XAML content dynamically? Are you converting a Silverlight 1.0 application to Silverlight 2? Loading XAML dynamically is generally speaking not a good practice in Silverlight 2. To create a Storyboard in code, you can use the object model to construct one. Something like:

    Storyboard sb = new Storyboard();

    DoubleAnimation da = new DoubleAnimation();

    sb.Children.Add(da);

    sb.Begin();

     

    Note in Beta 2, you don't have to add a Storyboard to Resources to begin it.

    To create a Storyboard in a UserControl's XAML, just put it in the XAML file and begin it in code behind. Most times there's indeed no need to load XAML dynamically... If you're creating a Custom Control, you should follow the VisualStateManager pattern. Please refer to http://msdn.microsoft.com/en-us/library/cc278064(VS.95).aspx.

    Monday, June 9, 2008 12:16 AM

All replies

  • Hi Jack,

    XamlReader.Load does not create a  new namescope. You should use LoadComponent for the user control.

    I have added info in another post http://silverlight.net/forums/p/17513/58338.aspx#58338

     

    Sunday, June 8, 2008 8:14 PM
  • Hello, actually XamlReader.Load DOES introduce a new name scope. That's why your Storyboard can't find the target to animate. Because the Storyboard and the Rectangle are in different name scopes. But why do you want to load XAML content dynamically? Are you converting a Silverlight 1.0 application to Silverlight 2? Loading XAML dynamically is generally speaking not a good practice in Silverlight 2. To create a Storyboard in code, you can use the object model to construct one. Something like:

    Storyboard sb = new Storyboard();

    DoubleAnimation da = new DoubleAnimation();

    sb.Children.Add(da);

    sb.Begin();

     

    Note in Beta 2, you don't have to add a Storyboard to Resources to begin it.

    To create a Storyboard in a UserControl's XAML, just put it in the XAML file and begin it in code behind. Most times there's indeed no need to load XAML dynamically... If you're creating a Custom Control, you should follow the VisualStateManager pattern. Please refer to http://msdn.microsoft.com/en-us/library/cc278064(VS.95).aspx.

    Monday, June 9, 2008 12:16 AM
  • A few things. First off, this seems to be a breaking change, which doesn't seem to be documented(oh well, no biggie.)

    To create a Storyboard in code, you can use the object model to construct one.

    Sure, but Xaml is declarative after all, so why build the DOM in code? Your mentioning that this was not a best practice was the first time I had heard anything along those lines. I can easily re-work my existing code to accomodate that, but I can think of a lot of situations where dynamical Xaml would be very useful. Unfortunately this may be very difficult now with the new name scope restrictions. Would it be possible to provide an overload to XamlReader.Load() that accepted a NameScope (if that even makes sense.) At this point, it looks like its more than just a "best practice", if you can't load working content into a an existing tree, then well, might as well get rid of XamlReader all together.

    Thanks for confirming what I suspected.

    Monday, June 9, 2008 5:08 AM
  • Unfortunately even on WPF XamlReader.Load doesn't preserve name scope... Normally, you use XamlReader.Load only for deserialization purpose. Let's say you have a UserControl, and you want to save its state to a file. On WPF, you can use XamlWriter to serialize the UserControl, and later use XamlReader to deserialize it. At this point, you'll get an instance of your UserControl, and you can add it to the element tree. You don't care names because you already have the reference of the UserControl in your code. Dynamically creating XAML in code is quite rare. One important reason we use XAML is we want to separate markup from code, and separate design from developer. Creating markup in code will break this rule. In your case, I think you indeed should put the Storyboard in a UserControl's markup.

    Monday, June 9, 2008 5:34 AM
  • Dynamically creating XAML in code is quite rare.

    Hi,

    When I read posts like yours I get the feeling that you guys at MS dont have a clue of what we developers out here work with. Since dec 2006 the createFromXaml supported common namescoping, in beta 1 XamlReader.Load( ...,false) did the same thing. And now in beta 2 you just leave out this feature because you think its rare to mix code and markup ! For me this has caused heavy trouble and it seems I'm not the only one.

    So here is one of those rare cases where code and markup is mixed: My frontend relies on XAML generated server side, every now and then small pieces of XAML are requested from the server and returned in string format with x:Name set to make them findable. The xaml is loaded into the model using XamlReader.Load. So now I end up with a large number of separate namescopes that I have to keep track of and have to search them all in order to find my objects.

    The Beta 1 approach where you let the developer himself decide what he needed was perfect, the beta 2 approach where MS just knows what we need better than us selves just stinks !

    Tim

     

    Tuesday, June 10, 2008 5:38 AM
  • I have forwarded your feedback to some internal folks.

    To ease your pain with keeping track of large number of separate namescopes, here is a suggestion.

    XamlReader.Load always creates a new name scope.  So if you want those named items to join an existing name scope, strip out the root: 

     

    Panel root = (Panel) XamlReader.Load(...);

    UIElement realRoot = root.Children[0];

    root.Children.Remove(realRoot);

    then add realRoot to your tree. This will not create a new namescope.

     

     

     

    Wednesday, June 11, 2008 3:48 AM
  • Hi and thanks for your answer.

    I temporarily solved the problems by internally keeping track of the separate namescopes but your solution seems smarter.

    But in your example above why do I have to do  root.Children.Remove(realRoot); ?

    Tim

    Wednesday, June 11, 2008 4:18 AM
  • But in your example above why do I have to do  root.Children.Remove(realRoot); ?

    So that it has only one parent.

    Wednesday, June 11, 2008 4:58 AM
  • The Beta 1 approach where you let the developer himself decide what he needed was perfect, the beta 2 approach where MS just knows what we need better than us selves just stinks !

    Well, it's still Beta-time, so we don't know how the final release will look like. But if I look back on my 20 years history as a developer, I must agree to it. I can't count the painfull unpaid(!) hours (thousands!) any more which I spent with developing "fancy workarounds" just to make code working as expected. My experience is that many times Microsoft's and other market leaders desicions rely on marketing strategies and NOT on developer needs. Eat - or die! Of course, over the years things have changed, and since Silverlight is on the road, our big "Mother" seems to be more close to her children (namely us!) than ever before, and even supplies us (partially) with Open Source. What a big change since 1985!!!

    I think we (the developers) should take this "communication offer" and cooperate with the MS-developer guys who try their best to supply us with good tools. No reason for complains. A much better approach is to give precise feedback what we really need - as early as possible, before releases go into production.

    Because I believe in this, I suggest Microsoft NOT trying to separate Designer needs from Developer needs (e.g. Design has to be done in XAML while functionality has to be supplied by the Developer). This "hard line" seperation never will work in practice!! And - for example from a small developers view, what about the thousands developers who can not afford a sophisticated designer for their projects? They are pretty good in their own stuff, and often want to "code" the design, at least partially. You want an example? Imagine a CMS (Content Management System) - based on Silverlight, where xaml has frequently to be created on the fly...

    By the way, the largest group from which developers, designers and even Microsoft are really dependend, are the CONSUMERS! They don't care how we reach the target, they just want accurate functionality and a good UI in order to do their job with our applications - without "blue screens" and other crashes. We should always keep this in our mind when we talk about needs...

    Regards,

    Simvouli
    PS: Sorry for my bad english, I am native german...

    Monday, June 16, 2008 11:44 AM
  • I'm trying to get my mind wrapped around this thread, but first, a thought.

    I wonder if this was taken out of Beta 2 simply to remove some code and keep the size of the download small.  Since the main controls were moved into the system, I'm sure the knife was out to try to find other things to cut.  Just a thought.

    Still, I'm trying to figure out a few things.

    1. Does this mean that instead of generating xaml code pieces, you always must generate a UserControl instead?
    2. In light of item 1, does that mean that to convert current projects, you need to make those xaml snippets into user controls?

    If I'm seeing this right, that would mean that you would no longer be able to assign events in the xaml to methods outside of the user control, but would have to manage that in code(maybe).

    So, is my framing of this problem correct?  Is it the distinction of generating xaml pieces as opposed to generating xaml user controls?

    Sam...

    Monday, June 16, 2008 4:30 PM
    1. Does this mean that instead of generating xaml code pieces, you always must generate a UserControl instead?
    2. In light of item 1, does that mean that to convert current projects, you need to make those xaml snippets into user controls?

    I'll give it a try, although it may not hit exactly your questions.
     
    As Yi-Lun Luo stated, obj = XamlReader.Load(...) always introduces a new name scope.
    Actually obj results in a disconnected object tree with its own root. FindName() works only inside obj, but when I add obj to the main object tree, it fails. Seems to be strange, but as I understand what is going on behind the scenes, it really makes sense. It prevents us from having problems with duplicate names by generating unique id's internally (similar to clientID in ASP.NET). FindName() is a convenient method to get a reference to any object using our given name, but internally it searches for the previous generated id, what means FindName() is "dedicated" to the root objects scope.
     
    Now - we have 3 choices to get out of this "problem":
     
    a) walk up or down the tree from the main object tree and use the distinct FindName() method of each child
    b) keep the reference to the obj that was returned from XamlReader as a global variable and then add it to the main object tree. Use the obj's FindName() method.
    c) detach the children of the obj and add them to the application object tree.
     
    - Personally a) is not my favourite, because it's time- and code-consuming. And because children might have sub children the whole procedure must walk even through sub collections.
    - In b) you can use obj.FindName() within the discrete namescope.
    - Solution c) is a clear and easy way (see Adrians posting above). FindName works fine, because the snippet child(ren) now belong to the live tree. It has only one drawback: if a name already exists in the main object tree, an error will be thrown. 
     
    Conclusion: If my theory is correct, Sam, it is not necessary to convert your Beta1 code into UserControls, when using one of the 3 methods mentioned above.
     
    hope this helps...
     
    Simvouli
     
     
     
     
    Monday, June 16, 2008 8:53 PM
  • I have forwarded your feedback to some internal folks.

    To ease your pain with keeping track of large number of separate namescopes, here is a suggestion.

    XamlReader.Load always creates a new name scope.  So if you want those named items to join an existing name scope, strip out the root: 

     

    Panel root = (Panel) XamlReader.Load(...);

    UIElement realRoot = root.Children[0];

    root.Children.Remove(realRoot);

    then add realRoot to your tree. This will not create a new namescope.

     

    I created a very similar helper function to add the namespace for XAMLReader and then strip it off after... which is similar in context to what you have above... I found this very helpful, although I'm now running into issues where FindName is coming back null for me on this newly loaded object, UNLESS I add it to my page DOM [which is really not useful since I am using the FindName to fix up any namespace conflicts I may have with this new object.

     public static UIElement LoadXAMLString(string xamlStr)
            {
                // Add namespace wrapper for XamlReader.Load.
                var wrapStr = "<Canvas xmlns=\"http://schemas.microsoft.com/client/2007\"";
                wrapStr += " xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">";
                wrapStr += xamlStr;
                wrapStr += "</Canvas>";

                var loadedXAMLWithWrapper = XamlReader.Load(wrapStr) as Canvas;
                var loadedXAML = loadedXAMLWithWrapper.Children[0];
                loadedXAMLWithWrapper.Children.Remove(loadedXAML);

                // Pull off namespace wrapper and return the object.
                return loadedXAML;
            }

    Friday, November 7, 2008 6:43 PM