22 changed files with 946 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<configuration> |
||||
|
<startup> |
||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> |
||||
|
</startup> |
||||
|
</configuration> |
||||
@ -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> |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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 |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -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> |
||||
@ -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); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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())); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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")] |
||||
@ -0,0 +1,3 @@ |
|||||
|
To load data from XamlTestApplication run with: |
||||
|
|
||||
|
--exe=..\..\..\..\..\samples\XamlTestApplication\bin\Debug --xaml=..\..\..\..\..\samples\XamlTestApplication\Views\MainWindow.xaml |
||||
@ -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…
Reference in new issue