Да, до пары лет я написал код для WPF, как вы хотите.Но это очень просто и имеет много проблем.Но я советую вам проверить возможность редактирования XAML во время отладки.Это хорошо работает даже без точки останова.Просто запустите проект в режиме отладки и отредактируйте файл XAML, даже если ваш код загружает его по коду.Даже Сохранить не требуется.
Я перечисляю здесь код и некоторые комментарии к нему:
MainWindow.xaml
<Window x:Class="RebuildAndReloadWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RebuildAndReloadWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Activated="Window_Activated" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition />
</Grid.RowDefinitions>
<Border CornerRadius="6" BorderBrush="Gray" Background="LightGray" BorderThickness="2" >
<StackPanel x:Name="___No_Name_" Background="LightYellow" Orientation="Horizontal">
<Button Click="Button_Click" >Reload</Button>
<CheckBox x:Name="chkReloadOnFocus" VerticalContentAlignment="Center" VerticalAlignment="Center" Content="Reload on focus" Margin="10,0,0,0"/>
<TextBlock x:Name="txtIndicator" VerticalAlignment="Center" Margin="10,0,0,0"/>
<Border x:Name="elmJustNowIndicator" BorderThickness="1" BorderBrush="Black" Background="Orange" CornerRadius="5" Height="21" Width="76" Visibility="Hidden" Margin="10,0,0,0">
<TextBlock Text="Just now" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</Border>
<ScrollViewer x:Name="PlaceHolder" Grid.Row="1"/>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.Build.BuildEngine;
using System;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace RebuildAndReloadWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const string projectPath = @"C:\...\Some-Project.csproj";
const string libFileName = "Some-Lib.dll";
const string ClassName = "NameSpace.UserControlName";
private string projectFileName;
private string projectDirectoryPath;
private string projectBinPath;
private string logFilePath;
private string appDirectoryPath;
private DispatcherTimer indicatorTimer = new DispatcherTimer();
public MainWindow ()
{
projectFileName = Path.GetFileName(projectPath);
projectDirectoryPath = Path.GetDirectoryName(projectPath);
projectBinPath = projectDirectoryPath + @"\bin\Debug";
logFilePath = projectDirectoryPath + @"\bin\Debug\build.log";
appDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;
indicatorTimer = new DispatcherTimer();
indicatorTimer.Interval = TimeSpan.FromMilliseconds(4000);
indicatorTimer.Tick += ( sender, e ) =>
{
elmJustNowIndicator.Visibility = Visibility.Hidden;
indicatorTimer.IsEnabled = false;
};
InitializeComponent();
}
public void ReloadContainer ()
{
PlaceHolder.Content = null;
bool result = RunMsBuild();
if (!result)
{
txtIndicator.Text = "Compile error, see in log file. Compile fail at: " + DateTime.Now.ToLongTimeString();
txtIndicator.Foreground = Brushes.Red;
return;
}
else
{
txtIndicator.Text = "Last build at: " + DateTime.Now.ToLongTimeString();
txtIndicator.Foreground = Brushes.Green;
}
try
{
File.Copy(projectBinPath + @"\" + libFileName, appDirectoryPath + libFileName, true);
}
catch (Exception ex)
{
MessageBox.Show("Can't copy compiled lib file: " + ex.Message);
throw;
}
elmJustNowIndicator.Visibility = Visibility.Visible;
indicatorTimer.IsEnabled = false;
indicatorTimer.IsEnabled = true;
try
{
PlaceHolder.Content = AsmLoad();
}
catch (Exception ex)
{
MessageBox.Show("Laod assembly error" + ex.Message);
}
GC.Collect();
}
public bool RunMsBuild ()
{
Engine eng = new Engine();
FileLogger logger = new FileLogger();
logger.Parameters = "logfile=" + logFilePath;
eng.RegisterLogger(logger);
bool bb = eng.BuildProjectFile(projectPath);
eng.UnregisterAllLoggers();
return bb;
}
public FrameworkElement AsmLoad ()
{
byte[] assemblyFileBUffer = File.ReadAllBytes(appDirectoryPath + @"\" + libFileName);
Assembly asm = AppDomain.CurrentDomain.Load(assemblyFileBUffer);
ContentControl container = (ContentControl)asm.CreateInstance(ClassName);
return (FrameworkElement)container.Content;
}
private void Window_Activated ( object sender, EventArgs e )
{
if (chkReloadOnFocus.IsChecked == true)
ReloadContainer();
}
private void Button_Click ( object sender, RoutedEventArgs e )
{
ReloadContainer();
}
}
}
Комментарии:
Приведенный выше код работает только с классом UserControl.Вы можете расширить код для поддержки также с Windows и Panel.
Приведенный выше код перестраивает только тогда, когда окно нашего приложения активировано (фокус).Вы можете расширить его, чтобы сохранить файл ответа.При использовании с FileSystemWatcher.Обратите внимание, событие запуска наблюдателя для каждого файла.Таким образом, вам нужно дождаться завершения всех событий (с разбивкой по таймеру), а также Предложить настроить Visual Studio, чтобы всегда делать SaveALL для комбинации клавиш Ctr + S.
Microsoft заменяет Microsoft.Build.BuildEngine с новейшей сборкой и предлагаем использовать с новым Microsoft.Build.Я понимаю, что новейшие проблемы найти новые инструменты (MSBuild.exe), как версия 15.0.Вероятно, вы получите сообщение об ошибке, требующее обходного пути: Microsoft.Build.Exceptions.InvalidProjectFileException: «Версия инструментов« 15.0 »не распознана.Доступные версии инструментов: «12.0», «14.0», «2.0», «3.5», «4.0». '
Dotnet не может освободить сборку после ее динамической загрузки.Работать вокруг не стоит усилий.Я проверяю приведенный выше код и запускаю его с циклом, потребление оперативной памяти лучше, чем я предполагал.И еще, я понимаю, что у Core 3.0 есть решение для этого.Так что, если вам нравится этот код, я советую попробовать выполнить его миграцию на ядро 3.0
Производительность.Я не пробую на реальном проекте.Но если у вас есть запасная оперативная память и сильный процессор, я считаю, что хорошо работает для небольшого проекта и не имеет задержки больше половины секунды.Я сравниваю это с началом отладки после изменения кода.Visual Studio имеет длительную задержку для входа в режим отладки и выхода.Так что вы можете получить значительное улучшение.
Кстати, если вы скопируете файл отладочной информации (.pdb), а также файл lib, VS откроет исходный файл при возникновении ошибки времени выполнения в целевых проектах.Но этот файл получит Lock.И следующая перезагрузка Fail (это странно, но, по моим проверкам, этого не случится, если целевой проект - VB).
Если вы действительно хотите разрабатывать с этим подходом.Вам нужно создать целевое приложение в виде коллекции небольшого проекта.Вы можете встроить приведенный ниже код в целевой проект и открыть окно контейнера, когда вы находитесь в режиме разработки.Вы можете добавить систему кэширования для всех данных, считываемых из файлов или внешних ресурсов, таких как базы данных.И построить систему, которая позволяет перезагружать перезагрузку непосредственно в какую-то точку интерфейса.