diff --git a/Perspex.sln b/Perspex.sln
index 3c1892f48d..a5940bb913 100644
--- a/Perspex.sln
+++ b/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
diff --git a/src/Windows/Perspex.Designer/App.config b/src/Windows/Perspex.Designer/App.config
new file mode 100644
index 0000000000..d1428ad712
--- /dev/null
+++ b/src/Windows/Perspex.Designer/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/Windows/Perspex.Designer/App.xaml b/src/Windows/Perspex.Designer/App.xaml
new file mode 100644
index 0000000000..2c853d351e
--- /dev/null
+++ b/src/Windows/Perspex.Designer/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/Windows/Perspex.Designer/App.xaml.cs b/src/Windows/Perspex.Designer/App.xaml.cs
new file mode 100644
index 0000000000..929a440192
--- /dev/null
+++ b/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
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ internal partial class App
+ {
+ public App()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs b/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs
new file mode 100644
index 0000000000..88861628a6
--- /dev/null
+++ b/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 _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;
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Designer/AppHost/WindowHost.cs b/src/Windows/Perspex.Designer/AppHost/WindowHost.cs
new file mode 100644
index 0000000000..90daca18e4
--- /dev/null
+++ b/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);
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Designer/Comm/CommChannel.cs b/src/Windows/Perspex.Designer/Comm/CommChannel.cs
new file mode 100644
index 0000000000..a408298db2
--- /dev/null
+++ b/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 _terminating = new TaskCompletionSource();
+ private BlockingCollection _outputQueue = new BlockingCollection();
+ public event Action