Browse Source

Designer: Initial out-of-process renderer implementation

pull/148/head
Nikita Tsukanov 11 years ago
parent
commit
eee5da30a0
  1. 7
      Perspex.sln
  2. 6
      src/Windows/Perspex.Designer/App.config
  3. 8
      src/Windows/Perspex.Designer/App.xaml
  4. 28
      src/Windows/Perspex.Designer/App.xaml.cs
  5. 120
      src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs
  6. 44
      src/Windows/Perspex.Designer/AppHost/WindowHost.cs
  7. 108
      src/Windows/Perspex.Designer/Comm/CommChannel.cs
  8. 18
      src/Windows/Perspex.Designer/Comm/InitMessage.cs
  9. 152
      src/Windows/Perspex.Designer/Comm/ProcessHost.cs
  10. 19
      src/Windows/Perspex.Designer/Comm/StateMessage.cs
  11. 19
      src/Windows/Perspex.Designer/Comm/UpdateXamlMessage.cs
  12. 19
      src/Windows/Perspex.Designer/Comm/WindowCreatedMessage.cs
  13. 23
      src/Windows/Perspex.Designer/DemoWindow.xaml
  14. 34
      src/Windows/Perspex.Designer/DemoWindow.xaml.cs
  15. 13
      src/Windows/Perspex.Designer/DesignerAppHost.cs
  16. 109
      src/Windows/Perspex.Designer/Perspex.Designer.csproj
  17. 14
      src/Windows/Perspex.Designer/PerspexDesigner.xaml
  18. 98
      src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs
  19. 51
      src/Windows/Perspex.Designer/Program.cs
  20. 36
      src/Windows/Perspex.Designer/Properties/AssemblyInfo.cs
  21. 3
      src/Windows/Perspex.Designer/README.txt
  22. 17
      src/Windows/Perspex.Designer/WinApi.cs

7
Perspex.sln

@ -90,6 +90,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Cairo", "src\Gtk\Pe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.ReactiveUI", "src\Perspex.ReactiveUI\Perspex.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Designer", "src\Windows\Perspex.Designer\Perspex.Designer.csproj", "{EC42600F-049B-43FF-AED1-8314D61B2749}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -212,6 +214,10 @@ Global
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.Build.0 = Release|Any CPU
{EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EC42600F-049B-43FF-AED1-8314D61B2749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EC42600F-049B-43FF-AED1-8314D61B2749}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -234,5 +240,6 @@ Global
{78CAFE33-DBEB-4132-8A28-81CFE8A4933C} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{54F237D5-A70A-4752-9656-0C70B1A7B047} = {B9894058-278A-46B5-B6ED-AD613FCC03B3}
{FB05AC90-89BA-4F2F-A924-F37875FB547C} = {B9894058-278A-46B5-B6ED-AD613FCC03B3}
{EC42600F-049B-43FF-AED1-8314D61B2749} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
EndGlobalSection
EndGlobal

6
src/Windows/Perspex.Designer/App.config

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>

8
src/Windows/Perspex.Designer/App.xaml

@ -0,0 +1,8 @@
<Application x:Class="Perspex.Designer.App" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" >
</Application>

28
src/Windows/Perspex.Designer/App.xaml.cs

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Perspex.Designer
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
internal partial class App
{
public App()
{
InitializeComponent();
}
}
}

120
src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using Perspex.Designer.Comm;
namespace Perspex.Designer.AppHost
{
class PerspexAppHost
{
private string _appDir;
private CommChannel _comm;
private Func<Stream, object, object> _xamlReader;
private WindowHost _host;
public PerspexAppHost(CommChannel channel)
{
_comm = channel;
}
public void Start()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
_comm.OnMessage += Channel_OnMessage;
_comm.Start();
}
private void Channel_OnMessage(object obj)
{
var init = obj as InitMessage;
if (init != null)
{
Init(init.TargetExe);
}
var updateXaml = obj as UpdateXamlMessage;
if (updateXaml != null)
UpdateXaml(updateXaml.Xaml);
}
private void UpdateXaml(string xaml)
{
dynamic window = Activator.CreateInstance(LookupType("Perspex.Controls.Window"));
_xamlReader(new MemoryStream(Encoding.UTF8.GetBytes(xaml)), window);
var hWnd = (IntPtr) window.PlatformImpl.Handle;
_host.SetWindow(hWnd);
window.Show();
}
void UpdateState(string state)
{
_comm.SendMessage(new StateMessage(state));
}
Type LookupType(string name)
{
var asms = AppDomain.CurrentDomain.GetAssemblies();
foreach (var asm in asms)
{
var found = asm.GetType(name, false, true);
if (found != null)
return found;
}
throw new TypeLoadException("Unable to find type " + name);
}
private MethodInfo LookupStaticMethod(string typeName, string method)
{
var type = LookupType(typeName);
var methods = type.GetMethods();
return methods.First(m => m.Name == method);
}
private void Init(string targetExe)
{
_appDir = Path.GetFullPath(Path.GetDirectoryName(targetExe));
Directory.SetCurrentDirectory(_appDir);
UpdateState("Loading assemblies");
foreach(var asm in Directory.GetFiles(_appDir).Where(f=>f.ToLower().EndsWith(".dll")||f.ToLower().EndsWith(".exe")))
try
{
Assembly.LoadFrom(asm);
}
catch { }
UpdateState("Looking up Perspex types");
var app = Activator.CreateInstance(LookupType("Perspex.Application"));
app.GetType()
.GetMethod("RegisterServices", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(app, null);
app.GetType().GetProperty("Styles").GetSetMethod(true)
.Invoke(app, new[] {Activator.CreateInstance(LookupType("Perspex.Themes.Default.DefaultTheme"))});
LookupStaticMethod("Perspex.Direct2D1.Direct2D1Platform", "Initialize").Invoke(null, null);
LookupStaticMethod("Perspex.Win32.Win32Platform", "InitializeEmbedded").Invoke(null, null);
var xamlFactory = Activator.CreateInstance(LookupType("Perspex.Markup.Xaml.Context.PerspexParserFactory"));
dynamic xamlLoader =
LookupType("OmniXaml.XamlLoader").GetConstructors().First().Invoke(new object[] {xamlFactory});
_xamlReader = (stream, root) => xamlLoader.Load(stream, root);
_host = new WindowHost();
_comm.SendMessage(new WindowCreatedMessage(_host.Handle));
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyPath = Path.Combine(_appDir, new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(assemblyPath) == false) return null;
Assembly assembly = Assembly.LoadFrom(assemblyPath);
return assembly;
}
}
}

44
src/Windows/Perspex.Designer/AppHost/WindowHost.cs

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Perspex.Designer.AppHost
{
class WindowHost : Control
{
public WindowHost()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.AliceBlue, ClientRectangle);
}
private IntPtr _hWnd;
public void SetWindow(IntPtr hWnd)
{
_hWnd = hWnd;
WinApi.SetParent(hWnd, Handle);
FixWindow();
}
void FixWindow()
{
WinApi.MoveWindow(_hWnd, 0, 0, Width, Height, true);
}
protected override void OnResize(EventArgs e)
{
FixWindow();
base.OnResize(e);
}
}
}

108
src/Windows/Perspex.Designer/Comm/CommChannel.cs

@ -0,0 +1,108 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace Perspex.Designer.Comm
{
internal class CommChannel : IDisposable
{
private readonly BinaryReader _input;
private readonly BinaryWriter _output;
private SynchronizationContext _dispatcher;
TaskCompletionSource<bool> _terminating = new TaskCompletionSource<bool>();
private BlockingCollection<byte[]> _outputQueue = new BlockingCollection<byte[]>();
public event Action<object> OnMessage;
public event Action Disposed;
public CommChannel(Stream input, Stream output)
{
_input = new BinaryReader(input);
_output = new BinaryWriter(output);
_dispatcher = SynchronizationContext.Current;
}
public void Start()
{
new Thread(WriterThread) { IsBackground = true }.Start();
new Thread(ReaderThread) { IsBackground = true }.Start();
}
public void SendMessage(object message)
{
var fmt = new BinaryFormatter();
var ms = new MemoryStream();
fmt.Serialize(ms, message);
_outputQueue.Add(ms.ToArray());
}
void WriterThread()
{
while (!_terminating.Task.IsCompleted)
{
var item = _outputQueue.Take();
if(_terminating.Task.IsCompleted)
return;
try
{
_output.Write(item.Length);
_output.Write(item);
_output.Flush();
}
catch (Exception e)
{
Dispose();
}
}
}
private async Task<byte[]> ReadAsyncOrNull(int count)
{
var readTask = Task.Factory.StartNew(() => _input.ReadBytes(count));
await Task.WhenAny(readTask, _terminating.Task);
if (_terminating.Task.IsCompleted)
return null;
return await readTask;
}
async void ReaderThread()
{
var fmt = new BinaryFormatter();
while (!_terminating.Task.IsCompleted)
{
try
{
var lenb = await ReadAsyncOrNull(4);
if (lenb == null)
return;
var data = await ReadAsyncOrNull(BitConverter.ToInt32(lenb, 0));
if (data == null)
return;
var message = fmt.Deserialize(new MemoryStream(data));
_dispatcher.Post(_ => OnMessage?.Invoke(message), null);
}
catch
{
Dispose();
return;
}
}
}
public void Dispose()
{
if(_terminating.Task.IsCompleted)
return;
_terminating.TrySetResult(true);
_outputQueue.Add(null);
Disposed?.Invoke();
}
}
}

18
src/Windows/Perspex.Designer/Comm/InitMessage.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Designer.Comm
{
[Serializable]
class InitMessage : UpdateXamlMessage
{
public string TargetExe { get; private set; }
public InitMessage(string targetExe, string xaml) : base(xaml)
{
TargetExe = targetExe;
}
}
}

152
src/Windows/Perspex.Designer/Comm/ProcessHost.cs

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace Perspex.Designer.Comm
{
class ProcessHost : INotifyPropertyChanged
{
private CommChannel _comm;
private string _state;
public string State
{
get { return _state; }
private set
{
if (_state == value)
return;
_state = value;
OnPropertyChanged();
}
}
private bool _isAlive;
private readonly SynchronizationContext _dispatcher;
private Process _proc;
public bool IsAlive
{
get { return _isAlive; }
set
{
if (_isAlive == value)
return;
_isAlive = value;
OnPropertyChanged();
}
}
private IntPtr _windowHandle;
public IntPtr WindowHandle
{
get { return _windowHandle; }
set
{
if (_windowHandle == value)
return;
_windowHandle = value;
OnPropertyChanged();
}
}
public ProcessHost()
{
_dispatcher = SynchronizationContext.Current;
}
void OnExited(object sender, EventArgs eventArgs)
{
_comm.Dispose();
_dispatcher.Post(_ =>
{
IsAlive = false;
WindowHandle = IntPtr.Zero;
State = "Designer process crashed";
}, null);
}
public void Start(string targetExe, string initialXaml)
{
if (_proc != null)
{
_proc.Exited -= OnExited;
_proc.Kill();
OnExited(null, null);
State = "Restarting...";
}
var msg = new InitMessage(Path.GetFullPath(targetExe), initialXaml);
var exe = typeof (ProcessHost).Assembly.GetModules()[0].FullyQualifiedName;
_proc = new Process()
{
StartInfo = new ProcessStartInfo(exe)
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true
},
EnableRaisingEvents = true
};
_proc.Exited += OnExited;
try
{
_proc.Start();
State = "Launching designer process...";
_comm = new CommChannel(_proc.StandardOutput.BaseStream, _proc.StandardInput.BaseStream);
_comm.OnMessage += OnMessage;
_comm.Start();
_comm.SendMessage(msg);
return;
}
catch (Exception e)
{
State = e.ToString();
}
IsAlive = true;
}
public void UpdateXaml(string xaml)
{
_comm?.SendMessage(new UpdateXamlMessage(xaml));
}
private void OnMessage(object obj)
{
var stateMessage = obj as StateMessage;
if (stateMessage != null)
State = stateMessage.State;
var windowMessage = obj as WindowCreatedMessage;
if (windowMessage != null)
WindowHandle = windowMessage.Handle;
}
public void Kill()
{
try
{
_proc.Kill();
_proc = null;
}
catch
{
//
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

19
src/Windows/Perspex.Designer/Comm/StateMessage.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Designer.Comm
{
[Serializable]
class StateMessage
{
public StateMessage(string state)
{
State = state;
}
public string State { get; private set; }
}
}

19
src/Windows/Perspex.Designer/Comm/UpdateXamlMessage.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Designer.Comm
{
[Serializable]
class UpdateXamlMessage
{
public UpdateXamlMessage(string xaml)
{
Xaml = xaml;
}
public string Xaml { get; private set; }
}
}

19
src/Windows/Perspex.Designer/Comm/WindowCreatedMessage.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Designer.Comm
{
[Serializable]
class WindowCreatedMessage
{
public WindowCreatedMessage(IntPtr handle)
{
Handle = handle;
}
public IntPtr Handle { get; private set; }
}
}

23
src/Windows/Perspex.Designer/DemoWindow.xaml

@ -0,0 +1,23 @@
<Window x:Class="Perspex.Designer.DemoWindow" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Perspex.Designer"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<DockPanel LastChildFill="True">
<DockPanel Dock="Bottom" LastChildFill="True">
<TextBlock Margin="5,0">Target Exe: </TextBlock>
<Button DockPanel.Dock="Right">Load XAML</Button>
<Button Margin="5,0" DockPanel.Dock="Right">Select EXE</Button>
<TextBlock x:Name="TargetExe"/>
</DockPanel>
<UniformGrid Columns="1" Rows="2">
<local:PerspexDesigner
TargetExe="{Binding ElementName=TargetExe, Path=Text, Mode=OneWay}"
Xaml="{Binding ElementName=Xaml, Path=Text, Mode=OneWay}"/>
<TextBox x:Name="Xaml"/>
</UniformGrid>
</DockPanel>
</Window>

34
src/Windows/Perspex.Designer/DemoWindow.xaml.cs

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Perspex.Designer
{
internal partial class DemoWindow
{
public DemoWindow()
{
InitializeComponent();
}
public DemoWindow(string targetExe, string targetPath) : this()
{
TargetExe.Text = targetExe;
if (targetExe != null)
Xaml.Text = File.ReadAllText(targetPath);
}
}
}

13
src/Windows/Perspex.Designer/DesignerAppHost.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Designer
{
class DesignerAppHost
{
}
}

109
src/Windows/Perspex.Designer/Perspex.Designer.csproj

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.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>{EC42600F-049B-43FF-AED1-8314D61B2749}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex.Designer</RootNamespace>
<AssemblyName>Perspex.Designer</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</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>
<UseVSHostingProcess>true</UseVSHostingProcess>
</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="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="AppHost\PerspexAppHost.cs" />
<Compile Include="AppHost\WindowHost.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Comm\CommChannel.cs" />
<Compile Include="Comm\InitMessage.cs" />
<Compile Include="Comm\ProcessHost.cs" />
<Compile Include="Comm\StateMessage.cs" />
<Compile Include="Comm\UpdateXamlMessage.cs" />
<Compile Include="Comm\WindowCreatedMessage.cs" />
<Compile Include="DemoWindow.xaml.cs">
<DependentUpon>DemoWindow.xaml</DependentUpon>
</Compile>
<Compile Include="DesignerAppHost.cs" />
<Compile Include="PerspexDesigner.xaml.cs">
<DependentUpon>PerspexDesigner.xaml</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WinApi.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Page Include="App.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="DemoWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="PerspexDesigner.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Content Include="README.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

14
src/Windows/Perspex.Designer/PerspexDesigner.xaml

@ -0,0 +1,14 @@
<UserControl x:Class="Perspex.Designer.PerspexDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ContentControl x:Name="NativeContainer" Panel.ZIndex="1"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" Panel.ZIndex="0">
<TextBlock x:Name="State"/>
</ScrollViewer>
</Grid>
</UserControl>

98
src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms.Integration;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Perspex.Designer.AppHost;
using Perspex.Designer.Comm;
namespace Perspex.Designer
{
/// <summary>
/// Interaction logic for PerpexDesigner.xaml
/// </summary>
public partial class PerspexDesigner
{
public static readonly DependencyProperty TargetExeProperty = DependencyProperty.Register(
"TargetExe", typeof (string), typeof (PerspexDesigner), new FrameworkPropertyMetadata(TargetExeChanged));
private static void TargetExeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((PerspexDesigner) d).OnTargetExeChanged();
}
public string TargetExe
{
get { return (string) GetValue(TargetExeProperty); }
set { SetValue(TargetExeProperty, value); }
}
public static readonly DependencyProperty XamlProperty = DependencyProperty.Register(
"Xaml", typeof (string), typeof (PerspexDesigner), new FrameworkPropertyMetadata(XamlChanged));
private static void XamlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((PerspexDesigner) d).OnXamlChanged();
}
public string Xaml
{
get { return (string) GetValue(XamlProperty); }
set { SetValue(XamlProperty, value); }
}
private readonly ProcessHost _host = new ProcessHost();
public PerspexDesigner()
{
InitializeComponent();
BindingOperations.SetBinding(State, TextBlock.TextProperty,
new Binding(nameof(ProcessHost.State)) {Source = _host});
_host.PropertyChanged += _host_PropertyChanged;
}
private void _host_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ProcessHost.WindowHandle) && _host.WindowHandle != IntPtr.Zero)
{
var host = new WindowHost();
host.SetWindow(_host.WindowHandle);
NativeContainer.Content = new WindowsFormsHost() {Child = host};
}
}
public void KillProcess()
{
_host.Kill();
}
private void OnTargetExeChanged()
{
if(string.IsNullOrEmpty(Xaml))
return;
_host.Start(TargetExe, Xaml);
}
private void OnXamlChanged()
{
if (!_host.IsAlive)
_host.Start(TargetExe, Xaml);
else
_host.UpdateXaml(Xaml);
}
}
}

51
src/Windows/Perspex.Designer/Program.cs

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
using Perspex.Designer.AppHost;
using Perspex.Designer.Comm;
namespace Perspex.Designer
{
class Program
{
[STAThread]
static void Main(string[] args)
{
if (!Console.IsInputRedirected || !Console.IsOutputRedirected)
DemoMain(args);
else
HostedMain();
}
static void HostedMain()
{
//Initialize sync context
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
var comm = new CommChannel(Console.OpenStandardInput(), Console.OpenStandardOutput());
comm.Disposed += () => Process.GetCurrentProcess().Kill();
var service = new PerspexAppHost(comm);
service.Start();
Application.Run();
}
private static void DemoMain(string[] args)
{
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
var app = new App();
const string targetExe = "--exe=";
const string xaml = "--xaml=";
app.Run(new DemoWindow(
args.Where(a => a.StartsWith(targetExe)).Select(a => a.Substring(targetExe.Length)).FirstOrDefault(),
args.Where(a => a.StartsWith(xaml)).Select(a => a.Substring(xaml.Length)).FirstOrDefault()));
}
}
}

36
src/Windows/Perspex.Designer/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Perspex.Designer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Perspex.Designer")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ec42600f-049b-43ff-aed1-8314d61b2749")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

3
src/Windows/Perspex.Designer/README.txt

@ -0,0 +1,3 @@
To load data from XamlTestApplication run with:
--exe=..\..\..\..\..\samples\XamlTestApplication\bin\Debug --xaml=..\..\..\..\..\samples\XamlTestApplication\Views\MainWindow.xaml

17
src/Windows/Perspex.Designer/WinApi.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Designer
{
class WinApi
{
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);
}
}
Loading…
Cancel
Save