Browse Source

Adding various other runtime application configuration extensions to improve boilerplate main startup. Updated most Sample applications to use simpler bootstrapping extensions.

pull/504/head
Jason Jarvis 10 years ago
parent
commit
97388703b3
  1. 24
      samples/BindingTest/App.xaml.cs
  2. 5
      samples/BindingTest/MainWindow.xaml.cs
  3. 52
      samples/ControlCatalog/App.xaml.cs
  4. 1
      samples/ControlCatalog/ControlCatalog.csproj
  5. 7
      samples/ControlCatalog/MainWindow.xaml.cs
  6. 127
      samples/ControlCatalog/Program.cs
  7. 29
      samples/TestApplication/Program.cs
  8. 46
      samples/TestApplicationShared/App.cs
  9. 23
      samples/XamlTestApplication/App.cs
  10. 44
      samples/XamlTestApplication/Program.cs
  11. 15
      samples/XamlTestApplicationPcl/XamlTestApp.cs
  12. 12
      src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs
  13. 631
      src/Perspex.Controls/Window.cs
  14. 13
      src/Perspex.Diagnostics/DevTools.xaml.cs

24
samples/BindingTest/App.xaml.cs

@ -13,30 +13,20 @@ namespace BindingTest
public App()
{
RegisterServices();
InitializeSubsystems((int)Environment.OSVersion.Platform);
InitializeComponent();
InitializeLogging();
}
public static void AttachDevTools(Window window)
{
DevTools.Attach(window);
}
private static void Main()
{
var app = new App();
var window = new MainWindow();
window.Show();
app.Run(window);
}
InitializeLogging();
private void InitializeComponent()
{
PerspexXamlLoader.Load(this);
new App()
.UseWin32Subsystem()
.UseDirect2D()
.LoadFromXaml()
.RunWithMainWindow<MainWindow>();
}
private void InitializeLogging()
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()

5
samples/BindingTest/MainWindow.xaml.cs

@ -1,4 +1,5 @@
using BindingTest.ViewModels;
using Perspex;
using Perspex.Controls;
using Perspex.Markup.Xaml;
@ -10,12 +11,12 @@ namespace BindingTest
{
this.InitializeComponent();
this.DataContext = new MainWindowViewModel();
App.AttachDevTools(this);
this.AttachDevTools();
}
private void InitializeComponent()
{
PerspexXamlLoader.Load(this);
this.LoadFromXaml();
}
}
}

52
samples/ControlCatalog/App.xaml.cs

@ -9,58 +9,14 @@ using Serilog;
namespace ControlCatalog
{
// Eventually we should move this into a PCL library so we can access
// from mobile platforms
//
class App : Application
{
public App()
{
RegisterServices();
InitializeSubsystems(GetPlatformId());
InitializeLogging();
InitializeComponent();
}
public static void AttachDevTools(Window window)
{
#if DEBUG
DevTools.Attach(window);
#endif
}
static void Main(string[] args)
{
var app = new App();
var window = new MainWindow();
window.Show();
app.Run(window);
}
private void InitializeComponent()
{
PerspexXamlLoader.Load(this);
}
private void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
private int GetPlatformId()
{
var args = Environment.GetCommandLineArgs();
if (args.Contains("--gtk"))
{
return (int)PlatformID.Unix;
}
else
{
return (int)Environment.OSVersion.Platform;
}
}
}
}
}

1
samples/ControlCatalog/ControlCatalog.csproj

@ -103,6 +103,7 @@
<Compile Include="Pages\SliderPage.xaml.cs">
<DependentUpon>SliderPage.xaml</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

7
samples/ControlCatalog/MainWindow.xaml.cs

@ -1,4 +1,5 @@
using Perspex.Controls;
using Perspex;
using Perspex.Controls;
using Perspex.Markup.Xaml;
namespace ControlCatalog
@ -8,12 +9,12 @@ namespace ControlCatalog
public MainWindow()
{
this.InitializeComponent();
App.AttachDevTools(this);
this.AttachDevTools();
}
private void InitializeComponent()
{
PerspexXamlLoader.Load(this);
this.LoadFromXaml();
}
}
}

127
samples/ControlCatalog/Program.cs

@ -0,0 +1,127 @@
using Perspex.Logging.Serilog;
using Serilog;
using System;
using System.Linq;
using Perspex;
namespace ControlCatalog
{
internal class Program
{
static void Main(string[] args)
{
InitializeLogging();
new App()
.ConfigureRenderSystem(args)
.LoadFromXaml()
.RunWithMainWindow<MainWindow>();
}
// This will be made into a runtime configuration extension soon!
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Experimental: Would like to move this into a shared location once I figure out the best place for it
// considering all common libraries are PCL and do not have access to Environment.OSVersion.Platform
// nor do they have access to the platform specific render/subsystem extensions.
//
// Perhaps via DI we register each system with a priority/rank
//
public static class RenderSystemExtensions
{
[Flags]
enum RenderSystem
{
None = 0,
GTK = 1,
Skia = 2,
Direct2D = 4
};
/// <summary>
/// Default (Optimal) render system for a particular platform
/// </summary>
/// <returns></returns>
private static RenderSystem DefaultRenderSystem()
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.MacOSX:
return RenderSystem.GTK;
case PlatformID.Unix:
return RenderSystem.GTK;
case PlatformID.Win32Windows:
return RenderSystem.Direct2D;
}
return RenderSystem.None;
}
/// <summary>
/// Returns an array of avalidable rendering systems in priority order
/// </summary>
/// <returns></returns>
private static RenderSystem[] AvailableRenderSystems()
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.MacOSX:
return new RenderSystem[] { RenderSystem.GTK, RenderSystem.Skia };
case PlatformID.Unix:
return new RenderSystem[] { RenderSystem.GTK, RenderSystem.Skia };
case PlatformID.Win32Windows:
return new RenderSystem[] { RenderSystem.Direct2D, RenderSystem.Skia, RenderSystem.GTK };
}
return new RenderSystem[0];
}
/// <summary>
/// Selects the optimal render system for desktop platforms. Supports cmd line overrides
/// </summary>
/// <param name="app"></param>
/// <param name="args"></param>
public static AppT ConfigureRenderSystem<AppT>(this AppT app, string[] args) where AppT : Application
{
// So this all works great under Windows where it can support
// ALL configurations. But on OSX/Unix we cannot use Direct2D
//
if (args.Contains("--gtk") || DefaultRenderSystem() == RenderSystem.GTK)
{
app.UseGtkSubsystem();
app.UseCairo();
}
else
{
app.UseWin32Subsystem();
// not available until we do the SkiaSharp merge
//if (args.Contains("--skia") || DefaultRenderSystem() == RenderSystem.Skia)
//{
// app.UseSkia();
//}
//else
{
app.UseDirect2D();
}
}
return app;
}
}
}

29
samples/TestApplication/Program.cs

@ -31,11 +31,30 @@ namespace TestApplication
// The version of ReactiveUI currently included is for WPF and so expects a WPF
// dispatcher. This makes sure it's initialized.
System.Windows.Threading.Dispatcher foo = System.Windows.Threading.Dispatcher.CurrentDispatcher;
new App();
MainWindow.RootNamespace = "TestApplication";
var wnd = MainWindow.Create();
DevTools.Attach(wnd);
Application.Current.Run(wnd);
var app = new App();
if (args.Contains("--gtk"))
{
app.UseGtkSubsystem();
app.UseCairo();
}
else
{
app.UseWin32Subsystem();
// not available until we do the SkiaSharp merge
//if (args.Contains("--skia"))
//{
// app.UseSkia();
//}
//else
{
app.UseDirect2D();
}
}
app.Run();
}
}
}

46
samples/TestApplicationShared/App.cs

@ -8,6 +8,9 @@ using Perspex.Controls.Templates;
using Perspex.Markup.Xaml;
using Perspex.Styling;
using Perspex.Themes.Default;
using Perspex.Diagnostics;
using Perspex.Platform;
using Perspex.Shared.PlatformSupport;
namespace TestApplication
{
@ -15,22 +18,35 @@ namespace TestApplication
{
public App()
{
// TODO: I believe this has to happen before we select sub systems. Can we
// move this safely into Application itself? Is there anything in here
// that is platform specific??
//
RegisterServices();
InitializeSubsystems((int)Environment.OSVersion.Platform);
Styles.Add(new DefaultTheme());
}
var loader = new PerspexXamlLoader();
var baseLight = (IStyle)loader.Load(
new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.xaml?assembly=Perspex.Themes.Default"));
Styles.Add(baseLight);
public void Run()
{
Styles.Add(new DefaultTheme());
Styles.Add(new SampleTabStyle());
DataTemplates = new DataTemplates
{
new FuncTreeDataTemplate<Node>(
x => new TextBlock {Text = x.Name},
x => x.Children),
};
}
}
var loader = new PerspexXamlLoader();
var baseLight = (IStyle)loader.Load(
new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.xaml?assembly=Perspex.Themes.Default"));
Styles.Add(baseLight);
Styles.Add(new SampleTabStyle());
DataTemplates = new DataTemplates
{
new FuncTreeDataTemplate<Node>(
x => new TextBlock {Text = x.Name},
x => x.Children),
};
MainWindow.RootNamespace = "TestApplication";
var wnd = MainWindow.Create();
DevTools.Attach(wnd);
Run(wnd);
}
}
}

23
samples/XamlTestApplication/App.cs

@ -7,26 +7,5 @@ using Serilog;
namespace XamlTestApplication
{
public class App : XamlTestApp
{
public App()
{
InitializeLogging();
}
protected override void RegisterPlatform()
{
InitializeSubsystems((int)Environment.OSVersion.Platform);
}
private void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
}
// No longer needed!
}

44
samples/XamlTestApplication/Program.cs

@ -1,39 +1,39 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Diagnostics;
using System.Windows.Threading;
using Perspex;
using Perspex.Collections;
using Perspex.Controls;
using Perspex.Controls.Templates;
using ReactiveUI;
using XamlTestApplication.Views;
using Serilog;
using Perspex.Logging.Serilog;
namespace XamlTestApplication
{
internal class Program
{
private static void Main()
private static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
// this sucks. Can we fix this? Do we even need it anymore?
var foo = Dispatcher.CurrentDispatcher;
App application = new App
{
};
var window = new MainWindow();
window.Show();
InitializeLogging();
sw.Stop();
Debug.WriteLine($"Startup: {sw.ElapsedMilliseconds}ms");
Application.Current.Run(window);
new XamlTestApp()
.UseWin32Subsystem()
.UseDirect2D()
.LoadFromXaml()
.RunWithMainWindow<Views.MainWindow>();
}
}
private static void InitializeLogging()
{
#if DEBUG
SerilogLogger.Initialize(new LoggerConfiguration()
.MinimumLevel.Warning()
.WriteTo.Trace(outputTemplate: "{Area}: {Message}")
.CreateLogger());
#endif
}
}
}

15
samples/XamlTestApplicationPcl/XamlTestApp.cs

@ -1,23 +1,14 @@
using Perspex;
using Perspex.Markup.Xaml;
using XamlTestApplication.Views;
namespace XamlTestApplication
{
public abstract class XamlTestApp : Application
public class XamlTestApp : Application
{
protected abstract void RegisterPlatform();
public XamlTestApp()
{
RegisterServices();
RegisterPlatform();
InitializeComponent();
}
private void InitializeComponent()
{
var loader = new PerspexXamlLoader();
loader.Load(typeof(XamlTestApp), this);
}
}
}
}

12
src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs

@ -187,3 +187,15 @@ namespace Perspex.Markup.Xaml
}
}
}
namespace Perspex
{
public static class XamlObjectExtensions
{
public static ObjectT LoadFromXaml<ObjectT>(this ObjectT obj)
{
Markup.Xaml.PerspexXamlLoader.Load(obj);
return obj;
}
}
}

631
src/Perspex.Controls/Window.cs

@ -14,314 +14,327 @@ using System.Collections.Generic;
namespace Perspex.Controls
{
/// <summary>
/// Determines how a <see cref="Window"/> will size itself to fit its content.
/// </summary>
public enum SizeToContent
{
/// <summary>
/// The window will not automatically size itself to fit its content.
/// </summary>
Manual,
/// <summary>
/// The window will size itself horizontally to fit its content.
/// </summary>
Width,
/// <summary>
/// The window will size itself vertically to fit its content.
/// </summary>
Height,
/// <summary>
/// The window will size itself horizontally and vertically to fit its content.
/// </summary>
WidthAndHeight,
}
/// <summary>
/// A top-level window.
/// </summary>
public class Window : TopLevel, IStyleable, IFocusScope, ILayoutRoot, INameScope
{
private static IList<Window> s_windows = new List<Window>();
/// <summary>
/// Retrieves an enumeration of all Windows in the currently running application.
/// </summary>
public static IList<Window> OpenWindows => s_windows;
/// <summary>
/// Defines the <see cref="SizeToContent"/> property.
/// </summary>
public static readonly StyledProperty<SizeToContent> SizeToContentProperty =
PerspexProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// </summary>
public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
PerspexProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
/// <summary>
/// Defines the <see cref="Title"/> property.
/// </summary>
public static readonly StyledProperty<string> TitleProperty =
PerspexProperty.Register<Window, string>(nameof(Title), "Window");
private readonly NameScope _nameScope = new NameScope();
private object _dialogResult;
private readonly Size _maxPlatformClientSize;
/// <summary>
/// Initializes static members of the <see cref="Window"/> class.
/// </summary>
static Window()
{
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue));
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
/// <summary>
/// Determines how a <see cref="Window"/> will size itself to fit its content.
/// </summary>
public enum SizeToContent
{
/// <summary>
/// The window will not automatically size itself to fit its content.
/// </summary>
Manual,
/// <summary>
/// The window will size itself horizontally to fit its content.
/// </summary>
Width,
/// <summary>
/// The window will size itself vertically to fit its content.
/// </summary>
Height,
/// <summary>
/// The window will size itself horizontally and vertically to fit its content.
/// </summary>
WidthAndHeight,
}
/// <summary>
/// A top-level window.
/// </summary>
public class Window : TopLevel, IStyleable, IFocusScope, ILayoutRoot, INameScope
{
private static IList<Window> s_windows = new List<Window>();
/// <summary>
/// Retrieves an enumeration of all Windows in the currently running application.
/// </summary>
public static IList<Window> OpenWindows => s_windows;
/// <summary>
/// Defines the <see cref="SizeToContent"/> property.
/// </summary>
public static readonly StyledProperty<SizeToContent> SizeToContentProperty =
PerspexProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// </summary>
public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
PerspexProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
/// <summary>
/// Defines the <see cref="Title"/> property.
/// </summary>
public static readonly StyledProperty<string> TitleProperty =
PerspexProperty.Register<Window, string>(nameof(Title), "Window");
private readonly NameScope _nameScope = new NameScope();
private object _dialogResult;
private readonly Size _maxPlatformClientSize;
/// <summary>
/// Initializes static members of the <see cref="Window"/> class.
/// </summary>
static Window()
{
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue));
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
(s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue));
}
/// <summary>
/// Initializes a new instance of the <see cref="Window"/> class.
/// </summary>
public Window()
: base(PlatformManager.CreateWindow())
{
_maxPlatformClientSize = this.PlatformImpl.MaxClientSize;
}
/// <inheritdoc/>
event EventHandler<NameScopeEventArgs> INameScope.Registered
{
add { _nameScope.Registered += value; }
remove { _nameScope.Registered -= value; }
}
/// <inheritdoc/>
event EventHandler<NameScopeEventArgs> INameScope.Unregistered
{
add { _nameScope.Unregistered += value; }
remove { _nameScope.Unregistered -= value; }
}
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>
public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
/// <summary>
/// Gets or sets a value indicating how the window will size itself to fit its content.
/// </summary>
public SizeToContent SizeToContent
{
get { return GetValue(SizeToContentProperty); }
set { SetValue(SizeToContentProperty, value); }
}
/// <summary>
/// Gets or sets the title of the window.
/// </summary>
public string Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// </summary>
///
public bool HasSystemDecorations
{
get { return GetValue(HasSystemDecorationsProperty); }
set { SetValue(HasSystemDecorationsProperty, value); }
}
/// <summary>
/// Gets or sets the minimized/maximized state of the window.
/// </summary>
public WindowState WindowState
{
get { return this.PlatformImpl.WindowState; }
set { this.PlatformImpl.WindowState = value; }
}
/// <inheritdoc/>
Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize;
/// <inheritdoc/>
Type IStyleable.StyleKey => typeof(Window);
/// <summary>
/// Closes the window.
/// </summary>
public void Close()
{
s_windows.Remove(this);
PlatformImpl.Dispose();
}
protected override void HandleApplicationExiting()
{
base.HandleApplicationExiting();
Close();
}
/// <summary>
/// Closes a dialog window with the specified result.
/// </summary>
/// <param name="dialogResult">The dialog result.</param>
/// <remarks>
/// When the window is shown with the <see cref="ShowDialog{TResult}"/> method, the
/// resulting task will produce the <see cref="_dialogResult"/> value when the window
/// is closed.
/// </remarks>
public void Close(object dialogResult)
{
_dialogResult = dialogResult;
Close();
}
/// <summary>
/// Hides the window but does not close it.
/// </summary>
public void Hide()
{
using (BeginAutoSizing())
{
PlatformImpl.Hide();
}
}
/// <summary>
/// Shows the window.
/// </summary>
public void Show()
{
s_windows.Add(this);
EnsureInitialized();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
{
PlatformImpl.Show();
}
}
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <returns>
/// A task that can be used to track the lifetime of the dialog.
/// </returns>
public Task ShowDialog()
{
return ShowDialog<object>();
}
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <typeparam name="TResult">
/// The type of the result produced by the dialog.
/// </typeparam>
/// <returns>.
/// A task that can be used to retrive the result of the dialog when it closes.
/// </returns>
public Task<TResult> ShowDialog<TResult>()
{
s_windows.Add(this);
EnsureInitialized();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
{
var modal = PlatformImpl.ShowDialog();
var result = new TaskCompletionSource<TResult>();
Observable.FromEventPattern(this, nameof(Closed))
.Take(1)
.Subscribe(_ =>
{
modal.Dispose();
result.SetResult((TResult)_dialogResult);
});
return result.Task;
}
}
/// <inheritdoc/>
void INameScope.Register(string name, object element)
{
_nameScope.Register(name, element);
}
/// <inheritdoc/>
object INameScope.Find(string name)
{
return _nameScope.Find(name);
}
/// <inheritdoc/>
void INameScope.Unregister(string name)
{
_nameScope.Unregister(name);
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
var sizeToContent = SizeToContent;
var size = ClientSize;
var desired = base.MeasureOverride(availableSize.Constrain(_maxPlatformClientSize));
switch (sizeToContent)
{
case SizeToContent.Width:
size = new Size(desired.Width, ClientSize.Height);
break;
case SizeToContent.Height:
size = new Size(ClientSize.Width, desired.Height);
break;
case SizeToContent.WidthAndHeight:
size = new Size(desired.Width, desired.Height);
break;
case SizeToContent.Manual:
size = ClientSize;
break;
default:
throw new InvalidOperationException("Invalid value for SizeToContent.");
}
return size;
}
/// <inheritdoc/>
protected override void HandleResized(Size clientSize)
{
if (!AutoSizing)
{
SizeToContent = SizeToContent.Manual;
}
base.HandleResized(clientSize);
}
private void EnsureInitialized()
{
if (!this.IsInitialized)
{
var init = (ISupportInitialize)this;
init.BeginInit();
init.EndInit();
}
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Window"/> class.
/// </summary>
public Window()
: base(PlatformManager.CreateWindow())
{
_maxPlatformClientSize = this.PlatformImpl.MaxClientSize;
}
/// <inheritdoc/>
event EventHandler<NameScopeEventArgs> INameScope.Registered
{
add { _nameScope.Registered += value; }
remove { _nameScope.Registered -= value; }
}
/// <inheritdoc/>
event EventHandler<NameScopeEventArgs> INameScope.Unregistered
{
add { _nameScope.Unregistered += value; }
remove { _nameScope.Unregistered -= value; }
}
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>
public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
/// <summary>
/// Gets or sets a value indicating how the window will size itself to fit its content.
/// </summary>
public SizeToContent SizeToContent
{
get { return GetValue(SizeToContentProperty); }
set { SetValue(SizeToContentProperty, value); }
}
/// <summary>
/// Gets or sets the title of the window.
/// </summary>
public string Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Enables of disables system window decorations (title bar, buttons, etc)
/// </summary>
///
public bool HasSystemDecorations
{
get { return GetValue(HasSystemDecorationsProperty); }
set { SetValue(HasSystemDecorationsProperty, value); }
}
/// <summary>
/// Gets or sets the minimized/maximized state of the window.
/// </summary>
public WindowState WindowState
{
get { return this.PlatformImpl.WindowState; }
set { this.PlatformImpl.WindowState = value; }
}
/// <inheritdoc/>
Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize;
/// <inheritdoc/>
Type IStyleable.StyleKey => typeof(Window);
/// <summary>
/// Closes the window.
/// </summary>
public void Close()
{
s_windows.Remove(this);
PlatformImpl.Dispose();
}
protected override void HandleApplicationExiting()
{
base.HandleApplicationExiting();
Close();
}
/// <summary>
/// Closes a dialog window with the specified result.
/// </summary>
/// <param name="dialogResult">The dialog result.</param>
/// <remarks>
/// When the window is shown with the <see cref="ShowDialog{TResult}"/> method, the
/// resulting task will produce the <see cref="_dialogResult"/> value when the window
/// is closed.
/// </remarks>
public void Close(object dialogResult)
{
_dialogResult = dialogResult;
Close();
}
/// <summary>
/// Hides the window but does not close it.
/// </summary>
public void Hide()
{
using (BeginAutoSizing())
{
PlatformImpl.Hide();
}
}
/// <summary>
/// Shows the window.
/// </summary>
public void Show()
{
s_windows.Add(this);
EnsureInitialized();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
{
PlatformImpl.Show();
}
}
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <returns>
/// A task that can be used to track the lifetime of the dialog.
/// </returns>
public Task ShowDialog()
{
return ShowDialog<object>();
}
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <typeparam name="TResult">
/// The type of the result produced by the dialog.
/// </typeparam>
/// <returns>.
/// A task that can be used to retrive the result of the dialog when it closes.
/// </returns>
public Task<TResult> ShowDialog<TResult>()
{
s_windows.Add(this);
EnsureInitialized();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
{
var modal = PlatformImpl.ShowDialog();
var result = new TaskCompletionSource<TResult>();
Observable.FromEventPattern(this, nameof(Closed))
.Take(1)
.Subscribe(_ =>
{
modal.Dispose();
result.SetResult((TResult)_dialogResult);
});
return result.Task;
}
}
/// <inheritdoc/>
void INameScope.Register(string name, object element)
{
_nameScope.Register(name, element);
}
/// <inheritdoc/>
object INameScope.Find(string name)
{
return _nameScope.Find(name);
}
/// <inheritdoc/>
void INameScope.Unregister(string name)
{
_nameScope.Unregister(name);
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
var sizeToContent = SizeToContent;
var size = ClientSize;
var desired = base.MeasureOverride(availableSize.Constrain(_maxPlatformClientSize));
switch (sizeToContent)
{
case SizeToContent.Width:
size = new Size(desired.Width, ClientSize.Height);
break;
case SizeToContent.Height:
size = new Size(ClientSize.Width, desired.Height);
break;
case SizeToContent.WidthAndHeight:
size = new Size(desired.Width, desired.Height);
break;
case SizeToContent.Manual:
size = ClientSize;
break;
default:
throw new InvalidOperationException("Invalid value for SizeToContent.");
}
return size;
}
/// <inheritdoc/>
protected override void HandleResized(Size clientSize)
{
if (!AutoSizing)
{
SizeToContent = SizeToContent.Manual;
}
base.HandleResized(clientSize);
}
private void EnsureInitialized()
{
if (!this.IsInitialized)
{
var init = (ISupportInitialize)this;
init.BeginInit();
init.EndInit();
}
}
}
}
namespace Perspex
{
public static class WindowApplicationExtensions
{
public static void RunWithMainWindow<WindowT>(this Application app) where WindowT : Perspex.Controls.Window, new()
{
var window = new WindowT();
window.Show();
app.Run(window);
}
}
}

13
src/Perspex.Diagnostics/DevTools.xaml.cs

@ -13,9 +13,20 @@ using Perspex.Markup.Xaml;
using Perspex.VisualTree;
using ReactiveUI;
namespace Perspex
{
public static class WindowExtensions
{
public static void AttachDevTools(this Window window)
{
Perspex.Diagnostics.DevTools.Attach(window);
}
}
}
namespace Perspex.Diagnostics
{
public class DevTools : UserControl
public class DevTools : UserControl
{
private static Dictionary<Window, Window> s_open = new Dictionary<Window, Window>();
private IDisposable _keySubscription;

Loading…
Cancel
Save