Cargando XAML en tiempo de ejecución?

Primero algunos antecedentes: estoy trabajando en una aplicación y estoy tratando de seguir las convenciones de MVVM escribiéndola. Una cosa que me gustaría hacer es poder darle a la aplicación diferentes “máscaras” para mi aplicación. La misma aplicación, pero muestra una “piel” para un cliente y una “piel” diferente para otro.

Y entonces mis preguntas son:
1. ¿Es posible cargar un archivo xaml en tiempo de ejecución y “asignarlo” a mi aplicación?
2. ¿Puede el archivo xaml ser un archivo externo que reside en una carpeta diferente?
3. ¿Puede la aplicación cambiar a otro archivo xaml fácilmente, o solo en el momento del inicio?

Entonces, ¿dónde debería empezar a buscar información sobre esto? ¿Qué métodos de WPF, si existen, manejan esta funcionalidad?

¡Gracias!

Editar: el tipo de “skinning” que quiero hacer es más que simplemente cambiar el aspecto de mis controles. La idea es tener una interfaz de usuario completamente diferente. Diferentes botones, diferentes diseños. Algo así como cómo una versión de la aplicación estará completamente equipada para expertos y otra versión simplificada para principiantes.

Creo que esto es bastante simple con el XamlReader, dale una oportunidad, no lo intenté yo mismo, pero creo que debería funcionar.

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

Como señaló Jakob Christensen, puede cargar cualquier XAML que desee utilizando XamlReader.Load . Esto no aplica solo para estilos, sino también para UIElement . Usted solo carga el XAML como:

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

Luego puede configurarlo como el contenido del elemento adecuado, por ejemplo, para

      

podría agregar rootElement en la grid con:

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

Naturalmente, también tendrá que conectar cualquier evento para elementos dentro de rootElement manualmente en el código subyacente. Como ejemplo, suponiendo que su rootElement contiene un Canvas con un montón de rootElement , puede asignar el evento Path s ‘ MouseLeftButtonDown como este:

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

No he intentado cambiar los archivos XAML sobre la marcha, así que no puedo decir si realmente funcionará o no.

Hice la extensión de marcado simple, que carga 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"); } } 

Uso:

Este hallazgo y carga el archivo MyFirstView.xaml

  

Y esto completa UserControl completo (encuentra y carga el archivo MySecondView.xaml)

  

Puede cargar cualquier XAML que desee utilizando XamlReader.Load .

Si aplica estilo a todos sus controles en su aplicación y define esos estilos en el diccionario de recursos de su aplicación, puede cargar nuevos estilos definidos en XAML en otro lugar utilizando XamlReader.Load y reemplazar partes de su diccionario de recursos con el XAML cargado. Sus controles cambiarán la apariencia en consecuencia.

He terminado de cargar XAML en tiempo de ejecución, aquí hay un breve ejemplo

 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 = @" " + "" + "" + "" + ""; 

Visite http://www.codeproject.com/Articles/19782/Creating-a-Skinned-User-Interface-in-WPF – Josh Smith escribió un excelente artículo sobre cómo hacer skinning en WPF.

Como ya se mencionó en otras respuestas, puede usar XamlReader.Load .

Si está buscando un ejemplo más directo, aquí hay un ejemplo que muestra con qué facilidad puede crear un control a partir de una variable de cadena que contiene el XAML:

 public T LoadXaml(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); } 

Y como el uso:

 var xaml = "" + "Lorm ipsum dolor sit amet." + ""; var textBox = LoadXaml(xaml);