Browse Source

Designer: Scroll Bar and error panel

pull/148/head
Nikita Tsukanov 11 years ago
parent
commit
7a067e959a
  1. 65
      src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs
  2. 56
      src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs
  3. 76
      src/Windows/Perspex.Designer/AppHost/WindowHost.cs
  4. 2
      src/Windows/Perspex.Designer/Comm/ProcessHost.cs
  5. 13
      src/Windows/Perspex.Designer/DesignerAppHost.cs
  6. 20
      src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml
  7. 84
      src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml.cs
  8. 16
      src/Windows/Perspex.Designer/Perspex.Designer.csproj
  9. 3
      src/Windows/Perspex.Designer/PerspexDesigner.xaml
  10. 26
      src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs
  11. 11
      src/Windows/Perspex.Designer/WinApi.cs
  12. 4
      src/Windows/Perspex.Designer/packages.config

65
src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Perspex.Designer.AppHost
{
public class HostedAppModel : INotifyPropertyChanged
{
private IntPtr _nativeWindowHandle;
private string _error;
private string _errorDetails;
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 void SetError(string error, string details = null)
{
Error = error;
ErrorDetails = details;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

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

@ -5,10 +5,11 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
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;
@ -21,8 +22,9 @@ namespace Perspex.Designer.AppHost
private string _lastXaml;
private string _currentXaml;
private Func<Stream, object> _xamlReader;
private WindowHost _host;
private bool _initSuccess;
private HostedAppModel _appModel = new HostedAppModel();
private Control _window;
public PerspexAppHost(CommChannel channel)
{
@ -222,17 +224,47 @@ namespace Perspex.Designer.AppHost
LookupType("OmniXaml.XamlLoader").GetConstructors().First().Invoke(new object[] {xamlFactory});
_xamlReader = (stream) => xamlLoader.Load(stream);
_host = new WindowHost();
_window = new Control
{
Controls =
{
new ElementHost()
{
Child = new InProcDesignerView(_appModel),
Dock = DockStyle.Fill
}
}
};
_window.CreateControl();
new Timer {Interval = 10, Enabled = true}.Tick += delegate
{
dispatcher.RunJobs();
};
new Timer {Interval = 200, Enabled = true}.Tick += delegate { XamlUpdater(); };
_comm.SendMessage(new WindowCreatedMessage(_host.Handle));
_comm.SendMessage(new WindowCreatedMessage(_window.Handle));
_initSuccess = true;
}
bool ValidateXml(string xml)
{
try
{
var rdr = new XmlTextReader(new StringReader(xml));
while (rdr.Read())
{
}
}
catch
{
return false;
}
return true;
}
void XamlUpdater()
{
if (!_initSuccess)
@ -240,6 +272,14 @@ namespace Perspex.Designer.AppHost
if(_lastXaml == _currentXaml)
return;
_lastXaml = _currentXaml;
if (!ValidateXml(_currentXaml))
{
_appModel.SetError("Invalid markup");
return;
}
try
{
const string windowType = "Perspex.Controls.Window";
@ -254,13 +294,13 @@ namespace Perspex.Designer.AppHost
var hWnd = (IntPtr)window.PlatformImpl.Handle;
_host.SetWindow(hWnd);
_appModel.NativeWindowHandle = hWnd;
window.Show();
_appModel.SetError(null);
}
catch (Exception e)
{
_host.SetWindow(IntPtr.Zero);
_host.PlaceholderText = e.ToString();
_appModel.SetError("XAML load error", e.ToString());
}
}

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

@ -9,29 +9,58 @@ using System.Windows.Forms;
namespace Perspex.Designer.AppHost
{
class WindowHost : Control
class WindowHost : UserControl
{
public WindowHost()
{
BackColor = Color.AliceBlue;
_textBox = new TextBox {Dock = DockStyle.Fill, ReadOnly = true, Multiline = true, Visible = false};
Controls.Add(_textBox);
AutoScroll = true;
VerticalScroll.Enabled = true;
HorizontalScroll.Enabled = true;
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
Text = "ScrollableArea";
Controls.Add(_windowHost);
_windowHost.Anchor = AnchorStyles.None;
_timer.Tick += delegate { FixWindow(); };
}
private Control _windowHost = new Control() {Text = "WindowWrapper"};
private Timer _timer = new Timer {Enabled = true, Interval = 50};
private IntPtr _hWnd;
private TextBox _textBox;
private int _desiredWidth;
private int _desiredHeight;
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
public string PlaceholderText
protected override void WndProc(ref Message m)
{
get { return _textBox.Text; }
set
if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
&& (((int) m.WParam & 0xFFFF) == 5))
{
_textBox.Text = value;
FixWindow();
// Change SB_THUMBTRACK to SB_THUMBPOSITION
m.WParam = (IntPtr) (((int) m.WParam & ~0xFFFF) | 4);
}
base.WndProc(ref m);
}
void FixPosition()
{
AutoScrollMinSize = new Size(_desiredWidth, _desiredHeight);
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);
_windowHost.Location = new Point(x, y);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_timer.Dispose();
}
base.Dispose(disposing);
}
public void SetWindow(IntPtr hWnd)
{
@ -40,21 +69,34 @@ namespace Perspex.Designer.AppHost
_hWnd = hWnd;
if (_hWnd != IntPtr.Zero)
{
WinApi.SetParent(hWnd, Handle);
WinApi.SetParent(hWnd, _windowHost.Handle);
FixWindow();
}
}
void FixWindow()
{
if (_hWnd != IntPtr.Zero)
WinApi.MoveWindow(_hWnd, 0, 0, Width, Height, true);
_textBox.Visible = _hWnd == IntPtr.Zero && !string.IsNullOrWhiteSpace(_textBox.Text);
{
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)
return;
_windowHost.Width = _desiredWidth;
_windowHost.Height = _desiredHeight;
WinApi.MoveWindow(_hWnd, 0, 0, _desiredWidth, _desiredHeight, true);
FixPosition();
}
}
protected override void OnResize(EventArgs e)
{
FixWindow();
FixPosition();
base.OnResize(e);
}
}

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

@ -152,7 +152,7 @@ namespace Perspex.Designer.Comm
{
try
{
_proc.Kill();
_proc?.Kill();
_proc = null;
}
catch

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

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

20
src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml

@ -0,0 +1,20 @@
<UserControl x:Class="Perspex.Designer.InProcDesigner.InProcDesignerView"
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:appHost="clr-namespace:Perspex.Designer.AppHost"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" d:DataContext="{d:DesignInstance appHost:HostedAppModel}">
<DockPanel>
<Border x:Name="ErrorPanel" DockPanel.Dock="Bottom" BorderBrush="Red" BorderThickness="3" Padding="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Error}" FontSize="16"/>
<TextBlock x:Name="DetailsBlock" FontSize="16">
<Run> (</Run><Hyperlink Click="Hyperlink_OnClick">View details</Hyperlink><Run>)</Run>
</TextBlock>
</StackPanel>
</Border>
<WindowsFormsHost x:Name="WindowHostControl"/>
</DockPanel>
</UserControl>

84
src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml.cs

@ -0,0 +1,84 @@
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;
using Perspex.Designer.AppHost;
namespace Perspex.Designer.InProcDesigner
{
/// <summary>
/// Interaction logic for InProcDesignerView.xaml
/// </summary>
public partial class InProcDesignerView : UserControl
{
private readonly HostedAppModel _appModel;
private readonly WindowHost _host;
public InProcDesignerView(HostedAppModel appModel)
{
_appModel = appModel;
InitializeComponent();
DataContext = _appModel;
_appModel.PropertyChanged += ModelPropertyChanged;
WindowHostControl.Child = _host = new WindowHost();
HandleVisibility();
HandleWindow();
}
private void ModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(HostedAppModel.Error) || e.PropertyName == nameof(HostedAppModel.ErrorDetails))
HandleVisibility();
if (e.PropertyName == nameof(HostedAppModel.NativeWindowHandle))
HandleWindow();
}
private void HandleWindow()
{
_host.SetWindow(_appModel.NativeWindowHandle);
}
private void HandleVisibility()
{
ErrorPanel.Visibility = string.IsNullOrEmpty(_appModel.Error)
? Visibility.Collapsed
: Visibility.Visible;
DetailsBlock.Visibility = string.IsNullOrWhiteSpace(_appModel.ErrorDetails)
? Visibility.Collapsed
: Visibility.Visible;
}
public InProcDesignerView()
{
}
private void Hyperlink_OnClick(object sender, RoutedEventArgs e)
{
var wnd = new Window()
{
Content = new TextBox()
{
IsReadOnly = true,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
Text = _appModel.ErrorDetails
}
};
wnd.ShowDialog();
}
}
}

16
src/Windows/Perspex.Designer/Perspex.Designer.csproj

@ -46,6 +46,10 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="JetBrains.Annotations, Version=9.2.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\JetBrains.Annotations.9.2.0\lib\net20\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
@ -66,9 +70,10 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="AppHost\HostedAppModel.cs" />
<Compile Include="AppHost\PerspexAppHost.cs" />
<Compile Include="AppHost\WindowHost.cs">
<SubType>Component</SubType>
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Comm\CommChannel.cs" />
<Compile Include="Comm\InitMessage.cs" />
@ -80,7 +85,9 @@
<Compile Include="DemoWindow.xaml.cs">
<DependentUpon>DemoWindow.xaml</DependentUpon>
</Compile>
<Compile Include="DesignerAppHost.cs" />
<Compile Include="InProcDesigner\InProcDesignerView.xaml.cs">
<DependentUpon>InProcDesignerView.xaml</DependentUpon>
</Compile>
<Compile Include="Metadata\PerspexDesignerMetadata.cs" />
<Compile Include="PerspexDesigner.xaml.cs">
<DependentUpon>PerspexDesigner.xaml</DependentUpon>
@ -91,6 +98,7 @@
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="perspex.designer.snk" />
</ItemGroup>
<ItemGroup>
@ -102,6 +110,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="InProcDesigner\InProcDesignerView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="PerspexDesigner.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

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

@ -10,5 +10,8 @@
<ScrollViewer VerticalScrollBarVisibility="Auto" Panel.ZIndex="0">
<TextBox IsReadOnly="True" TextWrapping="WrapWithOverflow" x:Name="State"/>
</ScrollViewer>
<StackPanel x:Name="ErrorContainer">
<TextBlock ></TextBlock>
</StackPanel>
</Grid>
</UserControl>

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

@ -80,13 +80,33 @@ namespace Perspex.Designer
}
if (_host.WindowHandle != IntPtr.Zero)
{
var host = new WindowHost();
host.SetWindow(_host.WindowHandle);
NativeContainer.Content = new WindowsFormsHost() {Child = host};
var host = new NativeWindowHost(_host.WindowHandle);
NativeContainer.Content = host;
}
}
}
class NativeWindowHost :HwndHost
{
private readonly IntPtr _hWnd;
public NativeWindowHost(IntPtr hWnd)
{
_hWnd = hWnd;
}
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
WinApi.SetParent(_hWnd, hwndParent.Handle);
return new HandleRef(this, _hWnd);
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
WinApi.SendMessage(hwnd.Handle, WinApi.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
public void KillProcess()
{
_host.Kill();

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

@ -18,5 +18,16 @@ namespace Perspex.Designer
public const uint WM_CLOSE = 0x0010;
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
}
}

4
src/Windows/Perspex.Designer/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="9.2.0" targetFramework="net45" />
</packages>
Loading…
Cancel
Save