diff --git a/Perspex.sln b/Perspex.sln
index 9cbaca445d..eec6adcbf5 100644
--- a/Perspex.sln
+++ b/Perspex.sln
@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Win32", "src\Window
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Direct2D1", "src\Windows\Perspex.Direct2D1\Perspex.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Designer", "src\Windows\Perspex.Designer\Perspex.Designer.csproj", "{EC42600F-049B-43FF-AED1-8314D61B2749}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Input", "src\Perspex.Input\Perspex.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Interactivity", "src\Perspex.Interactivity\Perspex.Interactivity.csproj", "{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}"
@@ -306,6 +308,30 @@ Global
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.Build.0 = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhone.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhoneSimulator.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}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhoneSimulator.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
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhone.Build.0 = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -1356,6 +1382,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
+ {EC42600F-049B-43FF-AED1-8314D61B2749} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{47ECDF59-DEF8-4C53-87B1-2098A3429059} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{76716382-3159-460E-BDA6-C5715CF606D7} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
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/HostedAppModel.cs b/src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs
new file mode 100644
index 0000000000..31fe2beeeb
--- /dev/null
+++ b/src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+using JetBrains.Annotations;
+
+namespace Perspex.Designer.AppHost
+{
+ public class HostedAppModel : INotifyPropertyChanged
+ {
+ private readonly PerspexAppHost _host;
+ private IntPtr _nativeWindowHandle;
+ private string _error;
+ private string _errorDetails;
+
+ internal HostedAppModel(PerspexAppHost host)
+ {
+ _host = host;
+ Background = Settings.Background;
+ }
+
+ public IntPtr NativeWindowHandle
+ {
+ get { return _nativeWindowHandle; }
+ set
+ {
+ if (value.Equals(_nativeWindowHandle)) return;
+ _nativeWindowHandle = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string Error
+ {
+ get { return _error; }
+ private set
+ {
+ if (value == _error) return;
+ _error = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string ErrorDetails
+ {
+ get { return _errorDetails; }
+ private set
+ {
+ if (value == _errorDetails) return;
+ _errorDetails = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string Background
+ {
+ get { return _background; }
+ set
+ {
+ if (value == _background) return;
+ _background = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public IReadOnlyList AvailableScalingFactors => new List() {1, 2, 4, 8};
+
+ public double CurrentScalingFactor
+ {
+ get { return _currentScalingFactor; }
+ set
+ {
+ _currentScalingFactor = value;
+ _host.Api.SetScalingFactor(value);
+ }
+ }
+
+ public void SetError(string error, string details = null)
+ {
+ Error = error;
+ ErrorDetails = details;
+ }
+
+ double _currentScalingFactor = 1;
+ private string _color;
+ private string _background;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs b/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs
new file mode 100644
index 0000000000..c3ca657baf
--- /dev/null
+++ b/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs
@@ -0,0 +1,297 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+using System.Windows.Forms.Integration;
+using System.Xml;
+using Perspex.Designer.Comm;
+using Perspex.Designer.InProcDesigner;
+using Perspex.Designer.Metadata;
+using Timer = System.Windows.Forms.Timer;
+using Perspex.DesignerSupport;
+
+namespace Perspex.Designer.AppHost
+{
+ class PerspexAppHost
+ {
+ private string _appDir;
+ private readonly CommChannel _comm;
+ private string _lastXaml;
+ private string _currentXaml;
+ private bool _initSuccess;
+ private readonly HostedAppModel _appModel;
+ private Control _window;
+
+ public PerspexAppHost(CommChannel channel)
+ {
+ _comm = channel;
+ _appModel = new HostedAppModel(this);
+ }
+
+ 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)
+ _currentXaml = updateXaml.Xaml;
+ }
+
+ void UpdateState(string state)
+ {
+ _comm.SendMessage(new StateMessage(state));
+ }
+
+ Type LookupType(params string[] names)
+ {
+ var asms = AppDomain.CurrentDomain.GetAssemblies();
+ foreach (var asm in asms)
+ {
+ foreach (var name in names)
+ {
+ var found = asm.GetType(name, false, true);
+ if (found != null)
+ return found;
+ }
+ }
+ throw new TypeLoadException("Unable to find any of types: " + string.Join(",", names));
+ }
+
+ 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)
+ {
+ var log = new StringBuilder();
+ try
+ {
+ DoInit(targetExe, log);
+ }
+ catch (Exception e)
+ {
+ UpdateState("Unable to load Perspex:\n\n" + e + "\n\n" + log);
+ }
+ }
+
+ PerspexDesignerMetadata BuildMetadata(List asms, Type xmlNsAttr)
+ {
+ var rv = new PerspexDesignerMetadata()
+ {
+
+ NamespaceAliases = new List(),
+ Types = new List()
+ };
+
+
+ foreach (var asm in asms)
+ {
+ foreach (dynamic xmlns in asm.GetCustomAttributes().Where(a => a.GetType() == xmlNsAttr))
+ {
+ rv.NamespaceAliases.Add(new MetadataNamespaceAlias
+ {
+ Namespace = (string)xmlns.ClrNamespace,
+ XmlNamespace = (string)xmlns.XmlNamespace
+ });
+ }
+
+ try
+ {
+ foreach (var type in asm.GetTypes())
+ {
+ try
+ {
+ if (!type.IsPublic || type.IsAbstract)
+ continue;
+ var t = new MetadataType()
+ {
+ Name = type.Name,
+ Namespace = type.Namespace,
+ Properties = new List()
+ };
+ rv.Types.Add(t);
+ foreach (var prop in type.GetProperties())
+ {
+ if (prop.GetMethod?.IsPublic != true)
+ continue;
+ var p = new MetadataProperty()
+ {
+ Name = prop.Name,
+ Type =
+ prop.PropertyType == typeof (string) ||
+ (prop.PropertyType.IsValueType &&
+ prop.PropertyType.Assembly == typeof (int).Assembly)
+ ? MetadataPropertyType.BasicType
+ : prop.PropertyType.IsEnum
+ ? MetadataPropertyType.Enum
+ : MetadataPropertyType.MetadataType
+
+ };
+ if (p.Type == MetadataPropertyType.Enum)
+ p.EnumValues = Enum.GetNames(prop.PropertyType);
+ if (p.Type == MetadataPropertyType.MetadataType)
+ p.MetadataFullTypeName = prop.PropertyType.Namespace + "." + prop.PropertyType.Name;
+ t.Properties.Add(p);
+ }
+ }
+ catch
+ {
+ //
+ }
+ }
+ }
+ catch
+ {
+ //
+ }
+ }
+ return rv;
+ }
+
+ void BuildMetadataAndSendMessageAsync(List asms)
+ {
+ var xmlNsAttr = LookupType("Perspex.Metadata.XmlnsDefinitionAttribute");
+ new Thread(() =>
+ {
+ _comm.SendMessage(new UpdateMetadataMessage(BuildMetadata(asms, xmlNsAttr)));
+ }).Start();
+ }
+
+ private void DoInit(string targetExe, StringBuilder logger)
+ {
+ _appDir = Path.GetFullPath(Path.GetDirectoryName(targetExe));
+ Directory.SetCurrentDirectory(_appDir);
+ Action log = s =>
+ {
+ UpdateState(s);
+ logger.AppendLine(s);
+ };
+ log("Loading assemblies from " + _appDir);
+ var asms = new List();
+ foreach(var asm in Directory.GetFiles(_appDir).Where(f=>f.ToLower().EndsWith(".dll")||f.ToLower().EndsWith(".exe")))
+ try
+ {
+ log("Trying to load " + asm);
+ asms.Add(Assembly.LoadFrom(asm));
+ }
+ catch (Exception e)
+ {
+ logger.AppendLine(e.ToString());
+ }
+
+ log("Looking up Perspex types");
+ BuildMetadataAndSendMessageAsync(asms);
+
+ log("Initializing built-in designer");
+ var dic = new Dictionary();
+ Api = new DesignerApi(dic) {OnResize = OnResize, OnWindowCreated = OnWindowCreated};
+ LookupStaticMethod("Perspex.DesignerSupport.DesignerAssist", "Init").Invoke(null, new object[] {dic});
+
+ _window = new Control
+ {
+ Controls =
+ {
+ new ElementHost()
+ {
+ Child = new InProcDesignerView(_appModel),
+ Dock = DockStyle.Fill
+ }
+ }
+ };
+ _window.CreateControl();
+
+ new Timer {Interval = 200, Enabled = true}.Tick += delegate { XamlUpdater(); };
+ _comm.SendMessage(new WindowCreatedMessage(_window.Handle));
+ _initSuccess = true;
+ }
+
+ private void OnWindowCreated(IntPtr hWnd)
+ {
+ _appModel.NativeWindowHandle = hWnd;
+ }
+
+
+ public DesignerApi Api { get; set; }
+
+
+ bool ValidateXml(string xml)
+ {
+ try
+ {
+ var rdr = new XmlTextReader(new StringReader(xml));
+ while (rdr.Read())
+ {
+
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void OnResize()
+ {
+
+ }
+
+ void XamlUpdater()
+ {
+ if (!_initSuccess)
+ return;
+ if(_lastXaml == _currentXaml)
+ return;
+ _lastXaml = _currentXaml;
+
+ if (!ValidateXml(_currentXaml))
+ {
+ _appModel.SetError("Invalid markup");
+ return;
+ }
+ try
+ {
+ Api.UpdateXaml(_currentXaml);
+
+ _appModel.SetError(null);
+ }
+ catch (Exception e)
+ {
+ _appModel.SetError("XAML load error", e.ToString());
+ }
+ }
+
+
+
+ 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;
+ }
+ }
+
+ static class Helper
+ {
+ public static object Prop(this object obj, string name) => obj.GetType().GetProperty(name).GetValue(obj);
+
+ }
+}
diff --git a/src/Windows/Perspex.Designer/AppHost/WindowHost.cs b/src/Windows/Perspex.Designer/AppHost/WindowHost.cs
new file mode 100644
index 0000000000..e598e4cb6e
--- /dev/null
+++ b/src/Windows/Perspex.Designer/AppHost/WindowHost.cs
@@ -0,0 +1,143 @@
+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 : UserControl
+ {
+ private readonly bool _supportScroll;
+
+ public WindowHost(bool supportScroll)
+ {
+ _supportScroll = supportScroll;
+ if (_supportScroll)
+ {
+ AutoScroll = true;
+ VerticalScroll.Enabled = true;
+ HorizontalScroll.Enabled = true;
+ }
+ SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+ Controls.Add(_windowHost);
+ _windowHost.Anchor = AnchorStyles.None;
+ if (!supportScroll)
+ _windowHost.Visible = false;
+ _timer.Tick += delegate
+ {
+ ReloadSettings();
+ FixWindow();
+ };
+ }
+
+ private void ReloadSettings()
+ {
+ var bkg = Settings.Background;
+ var color = System.Drawing.ColorTranslator.FromHtml(bkg);
+ if (BackColor != color)
+ BackColor = color;
+ }
+
+ private readonly Control _windowHost = new Control() {Text = "WindowWrapper"};
+ private readonly Timer _timer = new Timer {Enabled = true, Interval = 50};
+ private IntPtr _hWnd;
+ private int _desiredWidth;
+ private int _desiredHeight;
+
+ private const int WM_HSCROLL = 0x114;
+ private const int WM_VSCROLL = 0x115;
+
+ protected override void WndProc(ref Message m)
+ {
+ if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
+ && (((int) m.WParam & 0xFFFF) == 5))
+ {
+ // Change SB_THUMBTRACK to SB_THUMBPOSITION
+ m.WParam = (IntPtr) (((int) m.WParam & ~0xFFFF) | 4);
+ }
+ base.WndProc(ref m);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ using (var b = new SolidBrush(BackColor))
+ e.Graphics.FillRectangle(b, e.ClipRectangle);
+ }
+
+ void FixPosition()
+ {
+ var newScrollSize = new Size(_desiredWidth, _desiredHeight);
+ if (AutoScrollMinSize != newScrollSize)
+ AutoScrollMinSize = newScrollSize;
+
+ var width = Width - AutoScrollMargin.Width;
+ var height = Height - AutoScrollMargin.Height;
+ var x = Math.Max(0, (width - _windowHost.Width)/2);
+ var y = Math.Max(0, (height - _windowHost.Height)/2);
+
+ var newLoc = new Point(x - HorizontalScroll.Value, y - VerticalScroll.Value);
+ if(_windowHost.Location != newLoc)
+ _windowHost.Location = newLoc;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _timer.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ public void SetWindow(IntPtr hWnd)
+ {
+ if (_hWnd != IntPtr.Zero)
+ WinApi.SendMessage(_hWnd, WinApi.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
+ _hWnd = hWnd;
+ if (_hWnd != IntPtr.Zero)
+ {
+ WinApi.SetParent(hWnd, _supportScroll ? _windowHost.Handle : Handle);
+ FixWindow();
+ }
+ }
+
+ void FixWindow()
+ {
+ if (_hWnd != IntPtr.Zero)
+ {
+ if (_supportScroll)
+ {
+ WinApi.RECT rc;
+ WinApi.GetWindowRect(_hWnd, out rc);
+ _desiredWidth = rc.Right - rc.Left;
+ _desiredHeight = rc.Bottom - rc.Top;
+ var pt = _windowHost.PointToClient(new Point(rc.Left, rc.Top));
+
+ if (
+ !(pt.Y == 0 && pt.X == 0 && _desiredWidth == _windowHost.Width &&
+ _desiredHeight == _windowHost.Height))
+ {
+ _windowHost.Width = _desiredWidth;
+ _windowHost.Height = _desiredHeight;
+ WinApi.MoveWindow(_hWnd, 0, 0, _desiredWidth, _desiredHeight, true);
+ }
+ FixPosition();
+ }
+ else
+ {
+ 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..41d613d4d6
--- /dev/null
+++ b/src/Windows/Perspex.Designer/Comm/CommChannel.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Threading;
+
+namespace Perspex.Designer.Comm
+{
+ class CommChannel : IDisposable
+ {
+ private readonly BinaryReader _input;
+ private readonly BinaryWriter _output;
+ private readonly SynchronizationContext _dispatcher;
+ readonly TaskCompletionSource _terminating = new TaskCompletionSource();
+ private readonly BlockingCollection _outputQueue = new BlockingCollection();
+ public event Action