挂钩到wpf xmlns名称空间导入

时间:2015-05-19 14:12:53

标签: c# wpf xaml dynamic xml-namespaces

是否可以在WPF中定义将加载xmlns-packages(名称空间)的自定义区域。我的目标是定义类似的东西:

xmlns:customUI="db-ui:IronPythonControl"

这样我就可以从数据库加载我的控件并在xaml中使用它,如下所示:

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

谢谢!

1 个答案:

答案 0 :(得分:3)

我相信你可以使用MarkupExtension很好地解决这个问题。

我根据this guide生成了以下psueocode。

最终目标将是这样的。

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

我们将标记扩展定义为:

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*/}

    }
}

这里的关键部分是我们扩展MarkupExtension,并覆盖ProvideValue。在ProvideValue中,我们验证状态信息,并加载控件。

在LoadControl中,您可以放置​​代码从数据库加载XAML,将其从Xaml转换为对象,然后最终将RootObject作为结果传回。

您最终可能会使用比FrameworkElement更高或更低的控制级别。

现在我们已经定义了我们的加载器扩展,我们可以在项目中使用它

<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>

虽然这不是挂钩到命名空间声明,但它允许您动态加载和注入数据驱动的控件。

一种非常相似但另类的方法是定义一个CustomControl并将其基于ContentControl,在本例中我们将使用.NET 4.0 x:Arguments directive来简化代码:

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*/}
}

在使用中我们的结果如下:

<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>

另一种好方法与此CustomControl非常相似。但是,我们可以定义一个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*/}
}

此场景的XAML示例:     

    <ipc:IronPythonControl ControlName="IronTextBox"/>

</Window>

这些示例中的任何一个都应该提供一个很好的解决方案,它将在UI加载时加载XAML控件 请注意,基于Ctor的方法和MarkupExtension不是动态的,如果您不希望控件在运行时更改,这是很好的。

理论上,PropertyChanged方法允许您更改控件名称并根据新名称动态重新加载新内容。如果将ControlName绑定到TextBox并将其绑定UpdateSourceTrigger设置为PropertyChanged,则很容易失控。