WPF Автоматическая сборка и перезагрузка при изменении исходного кода - PullRequest
0 голосов
/ 25 сентября 2019

Я вижу ответ на этот вопрос, предлагаю использовать функцию живого юнит-тестирования.Так что у меня нет Microsoft Enterprise, и поэтому я не могу использовать его функцию Live Unit Testing.Попытка создать простое приложение для компиляции и перезагрузки в окно контейнера WPF.

скелетный код выглядит следующим образом:

public void RecompileAndReloadPrj ()
{
   Grid.Content = null;
   ReleaseAsm()
   RunMsBuild(targetProject);
   Grid.Content = LoadComponetFromAsm(targetASM);
}

К сожалению, заставить его работать немного сложнее ... Есть ли у кого-нибудь готовый код, который он может опубликовать, советы и т. Д., Илипредоставить ссылку?

1 Ответ

1 голос
/ 25 сентября 2019

Да, до пары лет я написал код для 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();
        }
    }
}

Комментарии:

  1. Приведенный выше код работает только с классом UserControl.Вы можете расширить код для поддержки также с Windows и Panel.

  2. Приведенный выше код перестраивает только тогда, когда окно нашего приложения активировано (фокус).Вы можете расширить его, чтобы сохранить файл ответа.При использовании с FileSystemWatcher.Обратите внимание, событие запуска наблюдателя для каждого файла.Таким образом, вам нужно дождаться завершения всех событий (с разбивкой по таймеру), а также Предложить настроить Visual Studio, чтобы всегда делать SaveALL для комбинации клавиш Ctr + S.

  3. 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». '

  4. Dotnet не может освободить сборку после ее динамической загрузки.Работать вокруг не стоит усилий.Я проверяю приведенный выше код и запускаю его с циклом, потребление оперативной памяти лучше, чем я предполагал.И еще, я понимаю, что у Core 3.0 есть решение для этого.Так что, если вам нравится этот код, я советую попробовать выполнить его миграцию на ядро ​​3.0

  5. Производительность.Я не пробую на реальном проекте.Но если у вас есть запасная оперативная память и сильный процессор, я считаю, что хорошо работает для небольшого проекта и не имеет задержки больше половины секунды.Я сравниваю это с началом отладки после изменения кода.Visual Studio имеет длительную задержку для входа в режим отладки и выхода.Так что вы можете получить значительное улучшение.

  6. Кстати, если вы скопируете файл отладочной информации (.pdb), а также файл lib, VS откроет исходный файл при возникновении ошибки времени выполнения в целевых проектах.Но этот файл получит Lock.И следующая перезагрузка Fail (это странно, но, по моим проверкам, этого не случится, если целевой проект - VB).

  7. Если вы действительно хотите разрабатывать с этим подходом.Вам нужно создать целевое приложение в виде коллекции небольшого проекта.Вы можете встроить приведенный ниже код в целевой проект и открыть окно контейнера, когда вы находитесь в режиме разработки.Вы можете добавить систему кэширования для всех данных, считываемых из файлов или внешних ресурсов, таких как базы данных.И построить систему, которая позволяет перезагружать перезагрузку непосредственно в какую-то точку интерфейса.

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