在运行时加载XAML?

时间:2009-05-26 13:53:55

标签: c# wpf xaml

首先是一些背景知识:我正在开发一个应用程序,而我正在尝试遵循编写它的MVVM约定。我想做的一件事是能够为应用程序提供不同的“皮肤”。相同的应用程序,但为一个客户端显示一个“皮肤”,为另一个客户端显示不同的“皮肤”。

所以我的问题是:
1.是否可以在运行时加载xaml文件并将其“分配”给我的应用程序?
2. xaml文件可以是驻留在不同文件夹中的外部文件吗?
3.应用程序是否可以轻松地或仅在启动时切换到另一个xaml文件?

那么我应该从哪里开始寻找有关此信息?哪些WPF方法(如果存在)处理此功能?

谢谢!

编辑:我想做的“皮肤”类型不仅仅是改变控件的外观。这个想法是有一个完全不同的用户界面。不同的按钮,不同的布局。有点像应用程序的一个版本将如何为专家全面展示,另一个版本将为初学者简化。

7 个答案:

答案 0 :(得分:36)

正如Jakob Christensen所说,你可以使用XamlReader.Load加载你想要的任何XAML。这不仅适用于样式,也适用于UIElement。你只需加载XAML:

UIElement rootElement;
FileStream s = new FileStream(fileName, FileMode.Open);
rootElement = (UIElement)XamlReader.Load(s);
s.Close();

然后您可以将其设置为合适元素的内容,例如对

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Foo Bar">
    <Grid x:Name="layoutGrid">
        <!-- any static elements you might have -->
    </Grid>
</Window>

您可以在rootElement中添加grid

layoutGrid.Children.Add(rootElement);
layoutGrid.SetColumn(rootElement, COLUMN);
layoutGrid.SetRow(rootElement, ROW);

您自然也必须在代码隐藏中手动连接rootElement内的元素的任何事件。例如,假设您的rootElement包含CanvasPathPath,您可以像这样分配MouseLeftButtonDown s'Canvas canvas = (Canvas)LogicalTreeHelper.FindLogicalNode(rootElement, "canvas1"); foreach (UIElement ui in LogicalTreeHelper.GetChildren(canvas)) { System.Windows.Shapes.Path path = ui as System.Windows.Shapes.Path; if (path != null) { path.MouseLeftButtonDown += this.LeftButtonDown; } } 事件:< / p>

{{1}}

我没有尝试动态切换XAML文件,所以我不能说这是否真的有效。

答案 1 :(得分:17)

我认为这对XamlReader来说相当简单,给它一个镜头,没有自己尝试,但我认为它应该有效。

http://blogs.msdn.com/ashish/archive/2007/08/14/dynamically-loading-xaml.aspx

答案 2 :(得分:5)

您可以使用XamlReader.Load加载所需的任何XAML。

如果在应用程序中设置所有控件的样式并在应用程序资源字典中定义这些样式,则可以使用XamlReader.Load在其他位置加载在XAML中定义的新样式,并用加载的XAML替换部分资源字典。您的控件将相应地更改外观。

答案 3 :(得分:5)

我做了简单的标记扩展,它加载了xaml:

public class DynamicXamlLoader : MarkupExtension
{
    public DynamicXamlLoader() { }

    public DynamicXamlLoader(string xamlFileName)
    {
        XamlFileName = xamlFileName;
    }

    public string XamlFileName { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var provideValue = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        if (provideValue == null || provideValue.TargetObject == null) return null;

        // get target
        var targetObject = provideValue.TargetObject as UIElement;
        if (targetObject == null) return null;

        // get xaml file
        var xamlFile = new DirectoryInfo(Directory.GetCurrentDirectory())
            .GetFiles(XamlFileName ?? GenerateXamlName(targetObject), SearchOption.AllDirectories)
            .FirstOrDefault();

        if (xamlFile == null) return null;

        // load xaml
        using (var reader = new StreamReader(xamlFile.FullName))
            return XamlReader.Load(reader.BaseStream) as UIElement;
    }

    private static string GenerateXamlName(UIElement targetObject)
    {
        return string.Concat(targetObject.GetType().Name, ".xaml");
    }
}

用法:

此查找并加载MyFirstView.xaml文件

<ContentControl Content="{wpf:DynamicXamlLoader XamlFileName=MyFirstView.xaml}" />

这将填满整个UserControl(查找并加载MySecondView.xaml文件)

<UserControl x:Class="MySecondView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         Content="{wpf:DynamicXamlLoader}" />

答案 4 :(得分:4)

我已经在运行时加载了XAML,这是一个简短的例子

Grid grd = new Grid();
var grdEncoding = new ASCIIEncoding();
var grdBytes = grdEncoding.GetBytes(myXAML);
grd = (Grid)XamlReader.Load(new MemoryStream(grdBytes));
Grid.SetColumn(grd, 0);
Grid.SetRow(grd, 0);
parentGrid.Children.Add(grd);

private String myXAML = @" <Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' Margin='30 10 30 65' VerticalAlignment='Bottom'>" +
                    "<Label Content='Date: 1-Feb-2013' FontFamily='Arial' FontSize='12' Foreground='#666666' HorizontalAlignment='Left'/>" +
                    "<Label Content='4'  FontFamily='Arial' FontSize='12' Foreground='#666666' HorizontalAlignment='Center'/>" +
                    "<Label Content='Hello World'  FontFamily='Arial' FontSize='12' Foreground='#666666' HorizontalAlignment='Right'/>" +
                "</Grid>";

答案 5 :(得分:0)

查看http://www.codeproject.com/Articles/19782/Creating-a-Skinned-User-Interface-in-WPF - Josh Smith撰写了一篇关于如何在WPF中进行蒙皮的精彩文章。

答案 6 :(得分:0)

正如其他答案中已经提到的那样,您可以使用XamlReader.Load

如果您正在寻找一个更简单的示例,那么以下示例显示了如何轻松地从包含XAML的字符串变量中创建控件:

public T LoadXaml<T>(string xaml)
{
    using (var stringReader = new System.IO.StringReader(xaml))
    using (var xmlReader = System.Xml.XmlReader.Create(stringReader))
        return (T)System.Windows.Markup.XamlReader.Load(xmlReader);
}

以及用法:

var xaml = "<TextBox xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation\'>" +
            "Lorm ipsum dolor sit amet." +
            "</TextBox>";
var textBox = LoadXaml<System.Windows.Controls.TextBox>(xaml);