Загрузка XAML во время выполнения? - PullRequest
46 голосов
/ 26 мая 2009

Сначала немного предыстории: я работаю над приложением и пытаюсь следовать соглашениям MVVM при его написании. Одна вещь, которую я хотел бы сделать, это иметь возможность придавать приложению различные «скины» для моего приложения. То же приложение, но для одного клиента показывается один «скин», а для другого - «скин».

И вот мои вопросы:
1. Можно ли загрузить файл xaml во время выполнения и «назначить» его моему приложению?
2. Может ли файл xaml быть внешним файлом, находящимся в другой папке?
3. Может ли приложение легко переключаться на другой файл xaml или только во время запуска?

Так с чего мне начать искать информацию об этом? Какие методы WPF, если они существуют, обрабатывают эту функцию?

Спасибо!

Edit: тип "скининга", который я хочу сделать, это больше, чем просто изменение внешнего вида моих элементов управления. Идея в том, чтобы иметь совершенно другой интерфейс. Разные кнопки, разные раскладки. Вроде как, как одна версия приложения будет полностью доступна для экспертов, а другая версия будет упрощена для начинающих.

Ответы [ 7 ]

36 голосов
/ 26 мая 2009

Как заметил Якоб Кристенсен, вы можете загрузить любой XAML по вашему желанию, используя XamlReader.Load. Это относится не только к стилям, но и к 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 содержит Canvas с кучей Path s, вы можете назначить событие Path s '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 на лету, поэтому я не могу сказать, сработает ли это на самом деле.

17 голосов
/ 26 мая 2009

Я думаю, что это довольно просто с XamlReader, попробуйте, я не пробовал сам, но я думаю, что это должно работать.

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

5 голосов
/ 30 августа 2013

Я сделал простое расширение разметки, которое загружает 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}" />
5 голосов
/ 26 мая 2009

Вы можете загрузить любой желаемый XAML, используя XamlReader.Load .

Если вы стилизуете все свои элементы управления в приложении и определяете эти стили в словаре ресурсов вашего приложения, вы можете загрузить новые стили, определенные в XAML, где-то еще, используя XamlReader.Load, и заменить части вашего словаря ресурсов на загруженный XAML. Ваше управление изменит внешний вид соответственно.

4 голосов
/ 30 мая 2013

Я выполнил загрузку 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>";
0 голосов
/ 03 августа 2018

Как уже упоминалось в других ответах, вы можете использовать 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);
0 голосов
/ 26 мая 2009

Проверьте http://www.codeproject.com/Articles/19782/Creating-a-Skinned-User-Interface-in-WPF - Джош Смит написал отличную статью о том, как делать скины в WPF.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...