none
What happened to Control.InitializeFromXaml?

    Question

  • Ack!  I just upgraded my Silverlight 2 application from Beta1 to Beta2, and it seems that the method Control.InitializeFromXaml has been removed!  Is there a replacement?  I desperately need this functionality.

     Thanks,

    Rajeev

    Sunday, June 08, 2008 1:26 AM

All replies

  • Hi Rajeev, 

    The quick way to fix this is to replace it with XamlReader.Load(string) method. However, this will not work if :

    • The control XAML includes event handlers. (XamlReader.Load() does not hook up events.)
    • The control XAML includes names. (XamlReader.Load() does not create a new namescope.)

     The recommended way to do this is:

     

    BEFORE:

        public class MyControl : Control

        {

            public MyControl() : base()

            {

                System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("MyNamespace.UserControl1.xaml");

                FrameworkElement content = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());

                this.namedElement = (Rectangle) content.FindName("namedElement");

            }

            private Rectangle namedElement; // xaml contains x:Name="namedElement"

        }

     

    <Grid   

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

          <Rectangle x:Name="namedElement"/>

          <Ellipse />

    </Grid>

     

     

    AFTER:

        public class MyControl : UserControl

        {

            public MyControl() : base()

            {

                // if building outside VS

                Application.LoadComponent(this, new Uri("MyApp;component/MyControl.xaml", UriKind.Relative));

                FrameworkElement content = this.Content;

                this.namedElement = (Rectangle) content.FindName("namedElement");

            }

            private Rectangle namedElement; // xaml contains x:Name="namedElement"

        }

     

    <UserControl x:Class="Namespace.MyControl"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        >

          <Grid>

                <Rectangle x:Name="namedElement"/>

                <Ellipse />

          </Grid>

    </UserControl>

     

    If you use VS to compile, it will generate a partial class for you so that all you need to do is call InitializeComponent:

     

        public partial class MyControl : UserControl

        {

            public MyControl() : base()

            {

                InitializeComponent();

            }

        }

    Hope this helps.

    Sunday, June 08, 2008 3:43 AM
  • I appreciate the quick response, but unfortunately this doesn't help.  I was previously using InitializeFromXaml precisely because my XAML contains event handlers and names.  (Frankly, any XAML that doesn't contain event handlers and names is a pretty useless piece of XAML.)  So I can't use XamlReader.Load.  I also can't use Application.LoadComponent because my XAML is not in a URI-addressable location or resource.  My XAML is in a local string variable. 

    I would really like to know the reason for removing the InitializeFromXaml API, and the possibilities around putting it back in.  I'm disappointed that the Silverlight team has chosen to remove important functionality, seemingly without consideration of the scenarios that it enabled.  I'm further frustrated that the removal of this API was not mentioned anywhere in any of the Beta2 documentation (http://msdn.microsoft.com/en-us/library/cc189007(vs.95).aspx), despite the fact that the docs enumerate in great detail every other major and minor change that was made between Beta1 and Beta2.  This leads me to wonder whether the decision to remove this important API was perhaps made by someone under the radar, carelessly, and without proper review.

     --Rajeev

     

    Sunday, June 08, 2008 1:49 PM
  • Application.LoadComponent works fine with event handlers. The generated InitializeComponent the VS makes when you create a new UserControl calls it.

    It is XamlReader that does not work with event handlers.

    Monday, June 09, 2008 12:43 AM
  • Hi, 

    I have a control inherited from Canvas that loads XAML as strings through a WCF service from the server. The XAML contains named elements but no event handlers.

    In Beta 1 I used XamlReader.Load to load the Xaml into the plugin and had no problems. In beta 2 the xaml gets loaded but all names are stripped. From the discussion above it seems such a thing is not possible anymore ?

    So tell me, how am I supposed to load a XAML string containing names into the Canvas ?? LoadComponent only works with uris.

    Tim

    Monday, June 09, 2008 4:49 AM
  • After reading this http://msdn.microsoft.com/en-us/library/cc189026(VS.95).aspx I understood the namescope issue and things work now.

    Tim

    Monday, June 09, 2008 7:12 AM
  • Hey Tim,  thank you so much for the link to the article.

    Would you mind sharing the code you used to go around the issue?

    I have in SL 2.0 Beta 1:

    FrameworkElement _rootElement = this.InitializeFromXaml(xamlString);

    where this is public abstract class ControlBase : Control

    and xamlString is a variable containing XAML with names (it's NOT a file accessible from a Uri).

    How can I reproduce the above in Beta 2 ???

    Thursday, June 12, 2008 3:11 AM
  • Hi,

    What I did was to keep a list of all root elements that I have loaded through XamlReader.Load and then loop through all roots in the list and do a FindName on all of them thus searching all namespaces. Not very elegant or efficient but it works. Microsoft suggested a workaround that I haven't tried yet.

    Check out my answer here: http://silverlight.net/forums/p/17951/60325.aspx#60325

    Tim

    Thursday, June 12, 2008 5:24 AM
  • I just wanted to point out that none of the workarounds or suggestions are fruitful when your XAML contains x:Name attributes and event handlers.  In Beta1, the InitializeFromXaml method handled this nicely, but that no longer exists in Beta2.  Some MSFTies have suggested using Application.LoadComponent, but that doesn't work either because it absolutely requires that the XAML be embedded as a resource in the same .DLL as the calling code.  (This is contrary to the documentation which states that LoadComponent would be able to find the XAML if it were a loose file at the application site of origin, i.e., on the web server.)  All this amounts to meaning that the XAML can't be changed or dynamically generated at run-time, which really sucks for me.

    It would cost MSFT nearly nothing to add an overload of LoadComponent that takes a string parameter for the input XAML directly (instead of a URI pointing at a resource), or to bring back the InitializeFromXaml method.  If I knew who to lobby to make that happen, I would.

    If it makes you feel better, you can go ahead and tell me that what I'm trying to do is not the recommended practice, but the fact is I need this functionality for the application I'm building.  Earlier this week, after I upgraded to Beta2, my blood was boiling when I came to realization that there was no way to build my app with the features I wanted anymore.  I would have to throw away 2 months of hard work, all because of this one tiny API that disappeared.  But now I've managed to calm myself down and use this experience as a valuable lesson about the nature of Microsoft Beta software.  I'm also finally starting to understand why most startups (even those founded by ex-Microsofties) tend to gravite toward non-Microsoft platforms.  They just don't have the time or resources to deal with this kind of flakiness and churn in the platform.

    --Rajeev

     

    Thursday, June 12, 2008 1:51 PM
  • Are you generating the XAML on the client-side or the server side? If it was the server-side, I could understand because you need some kind of representational transfer mechanism. On the client side, it seems inefficient to construct XAML (as a string) and then re-parse that to produce the object representation. Why not just construct the object tree directly, hooking up all the event handlers as you go?

    I would have to throw away 2 months of hard work, all because of this one tiny API that disappeared.  But now I've managed to calm myself down and use this experience as a valuable lesson about the nature of Microsoft Beta software.  I'm also finally starting to understand why most startups (even those founded by ex-Microsofties) tend to gravite toward non-Microsoft platforms.  They just don't have the time or resources to deal with this kind of flakiness and churn in the platform.

    I don't know why MS got rid of that function, and using my server-side comment above, if you want to have a server generate XAML and deliver it to the client it looks like you are now screwed - which is not a good thing from a functionality perspective (are you listening, Microsoft).

    BUT, basically you're crapped off because you placed a large dependency on a piece of software that in Beta form was well known could change from one release to the next. Everyone has come onto the platform with that prospect in mind, not just you. If you wanted certainty you should have waited for the 2.0 RTM release perhaps.

    I don't think Microsoft is right in eliminating a piece of functionality that looks like it would have proved useful, but I don't think you can necessarily chastise them for it to the degree you are because you knew what to expect.

    As for people gravitating to other platforms, my impression is that "other platforms" in Beta probably don't have the exposure that Microsoft has with Silverlight and therefore there will be less uptake and less likelihood of being "snookered" only because there are fewer developers. Please don't try intimate that by some miracle the Open Source community (for example) as a whole magically guarantees that their Beta software doesn't change APIs.

    Thursday, June 12, 2008 9:21 PM
  • Hi Rajeev,

    I am discussing this offline with some internal folks. No promises on the outcome of the discussion.

    "none of the workarounds or suggestions are fruitful when your XAML contains x:Name attributes and event handlers."

    For x:Name if you want to add it to the existing namescope, just strip off the root from the tree returned from xamlreader.load and add it the rest to the existing tree.

    eg.

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

    UIElement realRoot = root.Children[0];

    root.Children.Remove(realRoot);

    //add realRoot to your existing tree.

    For event handlers, you can hook them up in code once you find your named elements. Basically, for event handlers, you have to do the work that initializefromxaml did for you.

    If this is not working for you, please send me a small repro of your project (adrianmaATmicrosoftDOTcom) and I can make some suggestions that would save your 2 months of hard work.

    Thursday, June 12, 2008 9:40 PM
  • The Kevmeister is right in implying that I was too harsh.  Sorry about that.  I let my frustration get the better of me.

    Adrian, I appreciate the response and the suggestion.  To do what you're suggesting, I'd have to:

    1.) Parse the XAML myself as an XML document.

    2.) Figure out which XML attributes represent event handlers, and strip them out (because otherwise I get an exception saying "XamlReader.Load() does not accept event handlers.").

    3.) Call XamlReader.Load on the resulting stripped-down XML.

    4.) Strip off the root from the tree returned from XamlReader.Load, in order to get it added to the existing namescope.

    5.) Referring back to the original untouched XAML, use reflection to now hook up the event handlers.

    I suppose it all could be done.  But it sure requires me to know a whole lot about parsing XAML in order to correctly assess which XML attributes represent event handlers and which are just normal public properties.  I'd have to understand how the XML elements map to actual classes in the control library, and that includes dealing with any XML namespaces that may be in play.  There's a ton of room here for me to get things wrong, or at least deviate in subtle ways from the way Microsoft loads the XAML.  And despite all this effort, none of my "Loaded" event handlers would be invoked, because the Load happened in step 4 before the Loaded event handlers were hooked up in step 5.

    --Rajeev

     

    Friday, June 13, 2008 5:27 AM
  • I would like to vent some frustration too....

    I've been working on an application where I had created a custom control with different skins using embedded xaml resources. By using InitializeFromXaml, I was able to have one common control file that handled all my events, but could create different skins by passing in a string name of the embedded xaml during the constructor to be hooked dynamically for each instance of my custom control. The LoadComponent does not allow this and none of your alternative solutions work!!! Am I missing something or did Microsoft truly take away a powerful feature of Silverlight!

    If I have to spend all of this energy re-wiring all the xaml names and event handlers, what's the point???? I might as well create a custom user control for each skin or wrapper.

    Friday, June 13, 2008 2:23 PM
  • InitializeFromXaml() was a very powerful method.  Without it, there's no clean way to store a control's XAML in a database or in memory.

    Can you guys at Microsoft put that method back in the next release or patch, please? 

    An overload of Control.LoadComponent() as in Control.LoadComponent(string myActualXamlString) would work fine.

    Again, I have to refactor (but have no idea how) an entire application that was working really well using that function.

    Thanks!

     

    Saturday, June 14, 2008 11:12 AM
  • To give some context -- when I introduced InitializeFromXaml in the 1.1 alpha, I meant it as a placeholder mechanism until we could figure out the right way to load xaml for user controls. For beta one, we created UserControl/InitializeComponent, and control templates, and between those covered all the scenarios we had originally created InitializeFromXaml for. Its original purpose gone, I decided to remove it -- in Silverlight, the motto is when in doubt remove it, because that's how we keep download size small. (As well as small documentation, small test costs, small number of bugs, etc.) We meant to pull InitializeFromXaml for beta 1, but ran out of time before MIX. We should have marked it obsolete, but we didn't -- I apologize.

    Rajeev, c007 -- could you help me understand the scenario you're trying to build with InitializeFromXaml? Are you generating the xaml on the server? What kinds of events are you hooking up, what methods are you looking them to come and what elements are you doing this on? Is the xaml generation under your control, or is it third-party xaml you're merely storing away? Is the xaml trusted, or could it be compromised by an attacker? Does the xaml have to be editable by Blend?

    Tracie -- do control templates work for your skinning scenario?

    Again, I'd love to know more about what you're trying to build. If e-mail or phone works better for you, please contact me at nkramer@Microsoft.com. Thanks.

    -Nick Kramer
    Senior Program Manager, Silverlight

    Tuesday, June 17, 2008 6:37 PM
  • Some thoughts about a workaround -- if you have a little bit of flexibility about what the xaml looks like, you can stuff event handling information into the Tag property, and do a tree walk after XamlReader.Load() to hook up the events.  E.g.:

    <UserControl x:Class="XamlReaderWithEvents.Page"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="400" Height="300">
        <Grid x:Name="LayoutRoot" Background="White">
      <Button x:Name="b" Tag="MouseEnter=Page_MouseEnter;Click=Page_Click"/>
     </Grid>
    </UserControl>

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Controls.Primitives;
    using System.Diagnostics;
    using System.Reflection;

    namespace XamlReaderWithEvents {
        public partial class Page : UserControl {
            public Page() {
                InitializeComponent();
                WalkTree(LayoutRoot, HookUpEventsFromTag);
            }

            private void HookUpEventsFromTag(FrameworkElement element) {
                Object objectWithHandlers = this;
                Type typeWithHandlers = objectWithHandlers.GetType();

                if (element.Tag != null && element.Tag is string) {
                    var tag = (string)element.Tag;
                    var parts = tag.Split(new char[] { ';' });
                    foreach (string part in parts) {
                        var subparts = part.Split(new char[] { '=' });
                        Debug.Assert(subparts.Length == 2);
                        var eventname = subparts[0];
                        var methodname = subparts[1];

                        var eventInfo = element.GetType().GetEvent(eventname, BindingFlags.Instance | BindingFlags.Public);
                        Debug.Assert(eventInfo != null);
                       
                        MethodInfo methodInfo = typeWithHandlers.GetMethod(methodname, BindingFlags.Instance | BindingFlags.Public);
                        Debug.Assert(methodInfo != null);

                        var methodDelegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, objectWithHandlers, methodInfo);
                        eventInfo.AddEventHandler(element, methodDelegate);
                    }
                }
            }

            private static void WalkTree(FrameworkElement element, Action<FrameworkElement> action) {
                action(element);
                if (element is Panel) {
                    foreach (var child in ((Panel)element).Children) {
                        WalkTree((FrameworkElement)child, action);
                    }
                } else if (element is ContentControl) {
                    var contentcontrol = (ContentControl)element;
                    if (contentcontrol.Content is FrameworkElement) {
                        WalkTree((FrameworkElement)contentcontrol.Content, action);
                    }
                } else if (element is ItemsControl) {
                    var itemscontrol = (ItemsControl)element;
                    foreach (object child in itemscontrol.Items) {
                        if (child is FrameworkElement) {
                            WalkTree((FrameworkElement)child, action);
                        }
                    }
                } else if (element is Border) {
                    var border = (Border)element;
                    WalkTree((FrameworkElement)border.Child, action);
                } else if (element is Popup) {
                    var popup = (Popup)element;
                    WalkTree((FrameworkElement)popup.Child, action);
                }
            }

            public void Page_MouseEnter(object sender, MouseEventArgs e) {
            }

            public void Page_Click(object sender, RoutedEventArgs e) {
            }

        }
    }

    -Nick Kramer
    Senior Program Manager, Silverlight

    Tuesday, June 17, 2008 6:42 PM
  • Nick,

    I'll try to give as much info as I can about what I need for my scenario, but I'd rather not reveal the nature of the app I'm building on the forum.  In my case, I have a class that derives from UserControl that has already been compiled into a DLL, and this DLL is part of my Silverlight application.  This class contains event handlers.  The class has a special constructor that takes a string parameter as input, which is the xaml itself.  The constructor then calls "this.InitializeFromXaml(string)" (in Beta 1), passing in that same string.  This constructor does not call InitializeComponent; in fact, no such method exists in my case.  The XAML string that is passed into the constructor is authored by a human (i.e., it is not computer-generated), but not necessarily a trusted human.  The XAML will contain event hookups that refer to the event handler methods that are already part of the class.  I need to instantiante several instances of this class using different XAML content each time.  If the XAML refers to an event handler that doesn't exist on the class, it's an error.

    If this is not enough info, we can perhaps talk offline.

    --Rajeev

     

    Tuesday, June 17, 2008 7:06 PM
  • Nick,

    Rajeev is describing exactly how I am using the InitializeFromXaml() method. In my app, I have many individual xaml files in a folder (as my wrappers) that were marked as embedded resource strings, each with the appropriate event hookups, etc. Then, I had one common control source file with all the event handlers that loaded a string as dynamic xaml during the contructor. This was so powerful! I was able to create different xaml icons for navigation and hook unique animations and outlines of the different icons based on mouse events, all controled with one nice source file with all the events.

    The problem I have with your workaround above is that you still have only one xaml file hooked to each class definition. I have tried many alternative solutions, but I keep getting parser errors when I call LoadComponent() on anything other than loading the xaml defined with that class name.

    I will be glad to put together a sample app if needed.

    Tracie

    Wednesday, June 18, 2008 8:32 AM
  • Nick,

    I found the link to the financial services demo that gave us the examples of loading dynamic xaml strings in this manner. Your group posted this example using Silverlight 2 (Beta 1).

    http://www.financialdevelopers.com

    Download the Silverlight2 (Beta 1) Retail Financial Services Demonstrator located half-way down on this page. If you look at the Aspect.cs class in the Controls directory under BankingDemo, you can see an example of how I am using the InitializeFromXaml. They use this class to load embedded resources under the Aspects folder.

    Tracie

    Wednesday, June 18, 2008 9:30 AM
  • Nick,

    First of all, thank you so much for your answers and support!

    I've been using InitializeFromXaml() in the exact same way as Rajeev and Tracie.

    Furthermore, I'm mostly interested in creating an instance of a control from a XAML string and then manipulate the instance as I would when creating any UserControl or object entirely from C#.  In this way, I add an arbitrary "object" to the container (canvas, grid, etc) instead of just a mere Panel (or whatever the root element is cast as) to a tree.

    With the workarounds, I solved the namescope issues and the event handling wiring.  My problem is that I lost the ability to manipulate the dynamically created control because the instance is disconnected from the XAML object that was injected into the tree.

    I'm missing something?  Please let me know.

    Thanks!

     

    Wednesday, June 18, 2008 12:35 PM
  • Nick,

    I found the link to the financial services demo that gave us the examples of loading dynamic xaml strings in this manner. Your group posted this example using Silverlight 2 (Beta 1).

    http://www.financialdevelopers.com

    Download the Silverlight2 (Beta 1) Retail Financial Services Demonstrator located half-way down on this page. If you look at the Aspect.cs class in the Controls directory under BankingDemo, you can see an example of how I am using the InitializeFromXaml. They use this class to load embedded resources under the Aspects folder.

    Tracie

    They have converted that application to Beta 2, but have not released the source code.

    You can see it here - http://www.cookingwithxaml.com/meals/financials/default.html

    It will be interesting to see how they got around the loss of InitializeFromXaml.

    Sam...

    Saturday, June 21, 2008 8:01 PM
  • Is release of  the source code of that financial application working in Silverlight 2 Beta 2 supposed to be announced somewhere ? Or the only way to check  http://www.financialdevelopers.com from time to time ?  It is interesting, if it works at http://www.cookingwithxaml.com/meals/financials/default.html was new version just a hack which can't be released as an example of proper replacement InitializeFromXaml or it was just forgotten.

    Wednesday, July 02, 2008 2:53 AM
  • Hi,

    I have a scenario where I want to load a Datatemplate dynamically.

    In Silverlight 2 Beta 1 I used the InitializeFromXaml method to load the Datatemple. The reason for doing this is because the item inside the template had an event and the binding path is variable. In the runtime I am constructing the string that represents the Datatemplate. In this string the Binding path can change.

    string dynamicTemplate = "<DataTemplate x:Key=\"newTemplate\">" +"<TextBox xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"" +"Text=\"test\" TextChanged=\"TextBox_TextChanged\" Text={Binding Name}/>" +"</DataTemplate>";

    In Silverlight 2 Beta 2 how can I do the same or can I load a datatemplate dynamicaly in the C# code.

    Regards

    Ragu

      
    Tuesday, July 08, 2008 10:46 AM
  • Can someone please explain to me how Application.LoadComponent API work?

     I have Page.xaml.cs class and in its constructor  i'm trying to load another Xaml (say MyControl.xaml) that i have in the same project. I was using XamlReader.Load to load MyControl.xaml. Then  i use the returned root element and add it as child of one of the Canvas defined in my Page.xaml.

    Well life is good so far... it displays the content. However i want to do a bit more complicated stuff - i.e add event handlers on a Button control defined in the MyControl.xaml. Yes the button has x:Name defined.

    Well i got the parser error at runtime, thats when i hit upon this post.

     So i go back to my constructor and change

    ScrollViewer pScroll = (ScrollViewer)XamlReader.Load(uriString);   to

    Application.LoadComponent(this, new Uri(fileName, UriKind.Relative));

    Grid content = (Grid)this.Content;

     Here the fileName is the uri to MyControl.xaml and "this" refers to Page.xaml?

    this.Content; gives the root of the Page.xaml? How can i get hold of the root of  MyControl.xaml?

    I want to get hold of the root element in MyControl.xaml so that i can add it as child of a Canvas in the main page (Page.xaml).

    i got null when i did

    Scrollbar myScroll = (Canvas)content.FindName("myScrollBar");

     

    So can someone tell me what is going on here...

    Does  Application.LoadComponent(this, new Uri(fileName, UriKind.Relative));  create a tree structure of  the main page without the nodes from loaded xaml?

     

    Similarly what does 

    Application.LoadComponent(url) do?

     

    Thanks for your patience.

     

     

     

     

    Thursday, August 07, 2008 8:48 PM
  • This is real setback we definitely need this function back. In my scenario I need users to drag and drop 3rd party controls on a designer that can be loaded dynamically. So no tweaking would work Sad

    Monday, August 18, 2008 5:53 PM
  • In Silverlight RC0 I want to create DataTemplate for a control generated dynamically and register some events to it. LoadComponent is bad solution because I need a lot of instances of this control and would like to cache the XAML string (not to read from resource file each time). Therefore I think Microsoft should provide an overload for LoadComponent that will accept string as argument or return back the InitializeFromXAML method in the final release.

    Thursday, October 09, 2008 10:59 AM
  • I have only just started learning SilverLight and already I find the need to be able to create a control with dynamic xaml. 

    So Microsoft.

    How do we create a control where the XAML is coming from the server and contains names and event handlers?

    What is the solution...

    (Based on the number of views for this thread I guess I'm not alone!)

    Sunday, October 12, 2008 7:06 AM