0

Is it possible in WPF, to define a custom area from where xmlns-packages (namespaces) will be loaded. My aim is to define something like:

xmlns:customUI="db-ui:IronPythonControl"

so that i can load my control from the database and use it in xaml like this:

<IronPythonControl x:Name="..." ... />

Thank you!

pnuts
  • 58,317
  • 11
  • 87
  • 139
BendEg
  • 20,098
  • 17
  • 57
  • 131
  • Is your goal to dynamically load and display XAML code? – SomeInternetGuy May 19 '15 at 14:29
  • I already load and display XAML code with ironpython dynamically. Now i want to load dynamic controls on other dynamic controls. For example, i create a custom Textbox with XAML and IronPython and want to use it in a custom Window with XAML and ironPython. – BendEg May 19 '15 at 14:48

1 Answers1

3

I believe you could solve this nicely using a MarkupExtension.

I generated the following psueocode based upon this guide.

The end goal will be something like this.

<ContentControl Content="{ipc:IronPythonControl IronTextBox}"/>

We define our Markup Extension as so:

namespace IronPythonControlsExample {
    public class IronPythonControl : MarkupExtension
    {
        public string Name { get; private set;}
        public IronPythonControl(string name)
        {
            Name = name;   
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (!string.IsNullOrEmpty(Name))
            {
              return LoadControl(Name);
            }
        }

        public FrameworkElement LoadControl(string name){/*Load XAML Control from database here*/}

    }
}

The key parts here are that we extend MarkupExtension, and override ProvideValue. In ProvideValue we validate our state information, and load the control.

In LoadControl you would place your code to load the XAML from the database, convert it from Xaml into objects and then finally pass the RootObject back as the result.

You may end up using a higher or lower level of control than FrameworkElement.

Now that we have defined our loader extension we can use it in our project

<Window x:Class="IronPythonControlsExample.IronPythonWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ipc="clr-namespace:IronPythonControlsExample"
    Title="IronWindow" Height="300" Width="300">

    <ContentControl Content="{ipc:IronPythonControl IronTextBox}"/>

</Window>

While this isn't hooking into the namespace declaration, it will allow you to dynamically load and inject data driven controls.

A very similar but alternative approach would be to define a CustomControl and base it off of ContentControl, in this example we will use .NET 4.0 x:Arguments directive to simplify the code:

public class IronPythonControl : ContentControl 
{
    public IronPythonControl(string name)
    {
        Debug.Assert(!string.IsNullOrEmpty(name), "No name provided for control");
        Content = LoadControl(name);
    }

    public FrameworkElement LoadControl(string name){/*Load XAML Control from database here*/}
}

In usage our results would look like this:

<Window x:Class="IronPythonControlsExample.IronPythonWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ipc="clr-namespace:IronPythonControlsExample"
    Title="IronWindow" Height="300" Width="300">

    <ipc:IronPythonControl>
        <x:Arguments>
            <x:String>IronTextBox</x:String>
        </x:Arguments>
    </ipc:IronPythonControl>

</Window>

Yet another good approach would be very similar to this CustomControl. However instead of taking a param in the constructor, we can define a PropertyChangedCallback.

public class IronPythonControl : ContentControl 
{
    public static readonly DependencyProperty ControlNameProperty =
                           DependencyProperty.Register("ControlName", typeof(string), typeof(IronPythonControl), new FrameworkPropertyMetadata("IronTextBlock", ControlNameChangedCallback));

    private static void ControlNameChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        Content = LoadControl(ControlName);
    }

    public string ControlName
    {
        get { return (string)GetValue(ControlNameProperty); }
        set { SetValue(ControlNameProperty, value); }
    }

    public FrameworkElement LoadControl(string name){/*Load XAML Control from database here*/}
}

And a XAML example for this scenario:

    <ipc:IronPythonControl ControlName="IronTextBox"/>

</Window>

Any one of these examples should provide a good solution that will load XAML controls at UI Load time.
Note that the Ctor based approach and the MarkupExtension are not dynamic, this is good if you don't expect controls to change at run time.

In Theory, the PropertyChanged approach will allow you to change the control name and dynamically reload new content based upon the new name. This could easily get out of control if you bound ControlName to a TextBox with its bind UpdateSourceTrigger set to PropertyChanged.

SomeInternetGuy
  • 1,007
  • 12
  • 19
  • You can also reference these SO articles for more details: http://stackoverflow.com/questions/910814/loading-xaml-at-runtime – SomeInternetGuy May 19 '15 at 15:30
  • I think your last solution fits into my concept. I will try it. Thank you very musch for your detailed answer! – BendEg May 20 '15 at 06:45