c# - Compilar/ejecutar XAML durante el tiempo de ejecución del programa
wpf runtime (2)
Me gustaría crear una aplicación WPF que recupere el código XAML de una base de datos y muestre el código recuperado.
Digamos que la base de datos devuelve el siguiente código:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="mainGrid">
<Button Content="test case 1"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
Width="100"
Click="TestCase1_OnClick"
Height="29"/>
</Grid>
</Window>
¿Cómo ejecuto este código (o tal vez solo el contenido de mainGrid) durante el tiempo de ejecución?
Así es como resolví el problema:
Tengo 3 archivos MainWindow.xaml, MainWindow.xaml.cs y wpftest.csproj.
MainWindow.xaml:
<Window x:Class="wpftest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="122,75,0,0" VerticalAlignment="Top" Width="75" Click="ButtonBase_OnClick"/>
</Grid>
</Window>
MainWindow.xaml.cs
namespace wpftest
{
using System.Windows;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello world", "does it work?");
}
}
}
wpftest.csproj (Nota: este archivo es muy grande y la mayoría del material no es necesario. Puede crear una solución mucho más simplificada siguiendo esta guía: Tutorial: Crear un archivo de proyecto MSBuild desde cero )
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)/$(MSBuildToolsVersion)/Microsoft.Common.props" Condition="Exists(''$(MSBuildExtensionsPath)/$(MSBuildToolsVersion)/Microsoft.Common.props'')" />
<PropertyGroup>
<Configuration Condition=" ''$(Configuration)'' == '''' ">Debug</Configuration>
<Platform Condition=" ''$(Platform)'' == '''' ">AnyCPU</Platform>
<ProjectGuid>{E3FBAE41-AF7E-4C7E-A69E-ADAEAEE76FA2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>wpftest</RootNamespace>
<AssemblyName>wpftest</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" ''$(Configuration)|$(Platform)'' == ''Debug|AnyCPU'' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin/Debug/</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" ''$(Configuration)|$(Platform)'' == ''Release|AnyCPU'' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin/Release/</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)/Microsoft.CSharp.targets" />
</Project>
Solución:
El siguiente fragmento de código compilará el código, creará el archivo dll y usará el dll creado a través de la reflexión. Esto sucede (todo) durante el tiempo de ejecución de la aplicación.
Gracias especiales a Doug por esta pregunta
private void TestCase4_OnClick(object sender, RoutedEventArgs e)
{
var globalProperties = new Dictionary<string, string>();
var buildRequest = new BuildRequestData(@"C:/Users/jbu/wpftest/wpftest.csproj", globalProperties, null, new string[] { "Build" }, null);
var pc = new ProjectCollection();
var result = BuildManager.DefaultBuildManager.Build(new BuildParameters(pc), buildRequest);
Assembly assembly = Assembly.LoadFrom(@"C:/Users/jbu/wpftest/bin/Debug/wpftest.dll");
var instance = assembly.CreateInstance("wpftest.MainWindow") as Window;
if (instance != null)
{
instance.Show();
}
}
La clase XamlReader está diseñada para este propósito. Use su método Load para cargar el xaml dinámicamente.
EDITAR - Este enlace aquí podría ser de su interés.