【问题标题】:Loading XAML at runtime?在运行时加载 XAML?
【发布时间】:2009-05-26 13:53:54
【问题描述】:

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

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

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

谢谢!

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

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    正如 Jakob Christensen 所说,您可以使用 XamlReader.Load 加载您想要的任何 XAML。这不仅适用于样式,还适用于UIElements。您只需像这样加载 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>
    

    您可以在grid 中添加rootElement

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

    您自然还必须在代码隐藏中手动连接rootElement 内元素的任何事件。例如,假设您的rootElement 包含一个Canvas 和一堆Paths,您可以像这样分配Paths' MouseLeftButtonDown 事件:

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

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

    【讨论】:

    • 嗨,TomiJ!这似乎是关于我正在寻找的东西。我唯一缺少的是如何处理作为解决方案一部分的 .xaml 文件。我将如何去加载它们?
    • 在我正在处理的应用程序中,我只是将 XAML 文件保留在解决方案中并将其构建操作设置为“无”,并让 Visual Studio 将文件复制到输出目录(如果更新)。然后将使用上面的第一个代码 sn-p 来加载它们。
    • 这就是我一直在寻找的,将 xaml 作为资源加载 msdn.microsoft.com/en-us/library/aa970494.aspx
    • 你知道这种方法是否应该支持绑定吗?我已经尝试过了,甚至无法让它抛出异常,所以我怀疑在我的案例中没有任何东西可以让绑定真正发生。
    • @NeilBarnwell,我怀疑它不支持绑定,因此您必须在加载 XAML 后手动创建它们。 (免责声明:只是我的猜测。)
    【解决方案2】:

    我认为这对于 XamlReader 来说相当简单,试一试,我自己没有尝试过,但我认为它应该可以工作。

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

    【讨论】:

    【解决方案3】:

    我做了简单的标记扩展,它加载 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】:

      您可以使用 XamlReader.Load 加载您想要的任何 XAML。

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

      【讨论】:

        【解决方案5】:

        我已经在运行时加载了 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>";
        

        【讨论】:

          【解决方案6】:

          正如其他答案中已经提到的,您可以使用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);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-19
            相关资源
            最近更新 更多