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