簡體   English   中英

在運行時加載 XAML?

[英]Loading XAML at runtime?

首先是一些背景:我正在開發一個應用程序,我正在嘗試遵循編寫它的 MVVM 約定。 我想做的一件事是能夠為我的應用程序提供不同的“皮膚”。 相同的應用程序,但為一個客戶顯示一個“皮膚”,為另一個客戶顯示不同的“皮膚”。

所以我的問題是:
1. 是否可以在運行時加載 xaml 文件並將其“分配”到我的應用程序?
2. xaml 文件可以是駐留在不同文件夾中的外部文件嗎?
3. 應用程序是否可以輕松切換到另一個 xaml 文件,或者僅在啟動時切換?

那么我應該從哪里開始尋找這方面的信息呢? 哪些 WPF 方法(如果存在)處理此功能?

謝謝!

編輯:我想做的“蒙皮”類型不僅僅是改變控件的外觀。 這個想法是擁有一個完全不同的用戶界面。 不同的按鈕,不同的布局。 有點像應用程序的一個版本如何為專家提供完整的功能,而另一個版本如何為初學者簡化。

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

您可以在grid添加rootElement

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

您自然也必須在代碼隱藏中手動連接rootElement內的元素的任何事件。 例如,假設您的rootElement包含帶有一堆PathCanvas ,您可以像這樣分配Path的' 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文件,所以我不能說這是否真的有效。

我認為這對於XamlReader來說相當簡單,給它一個鏡頭,沒有自己嘗試,但我認為它應該有效。

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

我做了簡單的標記擴展,它加載了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}" />

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

如果在應用程序中設置所有控件的樣式並在應用程序資源字典中定義這些樣式,則可以使用XamlReader.Load在其他位置加載在XAML中定義的新樣式,並使用加載的XAML替換部分資源字典。 您的控件將相應地更改外觀。

我已經在運行時加載了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>";

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM