diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index 1ff1e1938b..ad40e81895 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -58,7 +58,7 @@ namespace Direct3DInteropSample new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm), IsWindowed = true, - OutputHandle = PlatformImpl.Handle.Handle, + OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 4688017187..b8d54fa67b 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; +using JetBrains.Annotations; namespace Avalonia.Controls.Embedding { @@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding { } + [CanBeNull] public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; public void Prepare() @@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding protected override Size MeasureOverride(Size availableSize) { - base.MeasureOverride(PlatformImpl.ClientSize); - return PlatformImpl.ClientSize; + var cs = PlatformImpl?.ClientSize ?? default(Size); + base.MeasureOverride(cs); + return cs; } private readonly NameScope _nameScope = new NameScope(); @@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding public void Unregister(string name) => _nameScope.Unregister(name); Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); - public void Dispose() - { - PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); } } diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 86c1b47521..a999e4ae37 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -9,6 +9,7 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { @@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; /// @@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives IVisual IHostedVisualTreeRoot.Host => Parent; /// - public void Dispose() - { - this.PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f3a6ab92d0..53dd905eca 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -14,6 +14,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -92,13 +93,13 @@ namespace Avalonia.Controls var rendererFactory = TryGetService(dependencyResolver); Renderer = rendererFactory?.CreateRenderer(this, renderLoop); - PlatformImpl.SetInputRoot(this); + impl.SetInputRoot(this); - PlatformImpl.Closed = HandleClosed; - PlatformImpl.Input = HandleInput; - PlatformImpl.Paint = HandlePaint; - PlatformImpl.Resized = HandleResized; - PlatformImpl.ScalingChanged = HandleScalingChanged; + impl.Closed = HandleClosed; + impl.Input = HandleInput; + impl.Paint = HandlePaint; + impl.Resized = HandleResized; + impl.ScalingChanged = HandleScalingChanged; _keyboardNavigationHandler?.SetOwner(this); @@ -110,7 +111,7 @@ namespace Avalonia.Controls this.GetObservable(PointerOverElementProperty) .Select( x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty()) - .Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor)); + .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor)); if (_applicationLifecycle != null) { @@ -135,10 +136,8 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// - public ITopLevelImpl PlatformImpl - { - get; - } + [CanBeNull] + public ITopLevelImpl PlatformImpl { get; private set; } /// /// Gets the renderer for the window. @@ -177,7 +176,7 @@ namespace Avalonia.Controls Size ILayoutRoot.MaxClientSize => Size.Infinity; /// - double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling; + double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; IStyleHost IStyleHost.StylingParent { @@ -189,25 +188,27 @@ namespace Avalonia.Controls /// protected virtual IRenderTarget CreateRenderTarget() { + if(PlatformImpl == null) + throw new InvalidOperationException("PlatformImpl isn't available"); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); } /// void IRenderRoot.Invalidate(Rect rect) { - PlatformImpl.Invalidate(rect); + PlatformImpl?.Invalidate(rect); } /// Point IRenderRoot.PointToClient(Point p) { - return PlatformImpl.PointToClient(p); + return PlatformImpl?.PointToClient(p) ?? default(Point); } /// Point IRenderRoot.PointToScreen(Point p) { - return PlatformImpl.PointToScreen(p); + return PlatformImpl?.PointToScreen(p) ?? default(Point); } /// @@ -224,6 +225,8 @@ namespace Avalonia.Controls /// protected virtual void HandleClosed() { + PlatformImpl = null; + Closed?.Invoke(this, EventArgs.Empty); Renderer?.Dispose(); Renderer = null; diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 75587dcaec..32eaf499f0 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -12,6 +12,7 @@ using Avalonia.Platform; using Avalonia.Styling; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -87,11 +88,11 @@ namespace Avalonia.Controls static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); - TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); + TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( - (s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue)); + (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); - IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); + IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); } /// @@ -109,7 +110,7 @@ namespace Avalonia.Controls public Window(IWindowImpl impl) : base(impl) { - _maxPlatformClientSize = this.PlatformImpl.MaxClientSize; + _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); } /// @@ -129,6 +130,7 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; /// @@ -164,8 +166,12 @@ namespace Avalonia.Controls /// public WindowState WindowState { - get { return this.PlatformImpl.WindowState; } - set { this.PlatformImpl.WindowState = value; } + get { return PlatformImpl?.WindowState ?? WindowState.Normal; } + set + { + if (PlatformImpl != null) + PlatformImpl.WindowState = value; + } } /// @@ -189,7 +195,7 @@ namespace Avalonia.Controls public void Close() { s_windows.Remove(this); - PlatformImpl.Dispose(); + PlatformImpl?.Dispose(); IsVisible = false; } @@ -221,7 +227,7 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Hide(); + PlatformImpl?.Hide(); } IsVisible = false; @@ -240,7 +246,7 @@ namespace Avalonia.Controls using (BeginAutoSizing()) { - PlatformImpl.Show(); + PlatformImpl?.Show(); } } @@ -278,7 +284,7 @@ namespace Avalonia.Controls var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); SetIsEnabled(affectedWindows, false); - var modal = PlatformImpl.ShowDialog(); + var modal = PlatformImpl?.ShowDialog(); var result = new TaskCompletionSource(); Observable.FromEventPattern( @@ -287,7 +293,7 @@ namespace Avalonia.Controls .Take(1) .Subscribe(_ => { - modal.Dispose(); + modal?.Dispose(); SetIsEnabled(affectedWindows, true); activated?.Activate(); result.SetResult((TResult)_dialogResult); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 21c248db0c..1f484fd6cb 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -43,10 +44,10 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { - PlatformImpl.Activated = HandleActivated; - PlatformImpl.Deactivated = HandleDeactivated; - PlatformImpl.PositionChanged = HandlePositionChanged; - this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); + impl.Activated = HandleActivated; + impl.Deactivated = HandleDeactivated; + impl.PositionChanged = HandlePositionChanged; + this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); } /// @@ -64,6 +65,7 @@ namespace Avalonia.Controls /// public event EventHandler PositionChanged; + [CanBeNull] public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; /// @@ -80,8 +82,12 @@ namespace Avalonia.Controls /// public Point Position { - get { return PlatformImpl.Position; } - set { PlatformImpl.Position = value; } + get { return PlatformImpl?.Position ?? default(Point); } + set + { + if (PlatformImpl is IWindowBaseImpl impl) + impl.Position = value; + } } /// @@ -98,7 +104,7 @@ namespace Avalonia.Controls /// public void Activate() { - PlatformImpl.Activate(); + PlatformImpl?.Activate(); } /// @@ -110,7 +116,7 @@ namespace Avalonia.Controls try { - PlatformImpl.Hide(); + PlatformImpl?.Hide(); IsVisible = false; } finally @@ -131,7 +137,7 @@ namespace Avalonia.Controls EnsureInitialized(); IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); - PlatformImpl.Show(); + PlatformImpl?.Show(); } finally { @@ -163,10 +169,10 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Resize(finalSize); + PlatformImpl?.Resize(finalSize); } - return base.ArrangeOverride(PlatformImpl.ClientSize); + return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); } /// @@ -174,7 +180,7 @@ namespace Avalonia.Controls /// protected void EnsureInitialized() { - if (!this.IsInitialized) + if (!IsInitialized) { var init = (ISupportInitialize)this; init.BeginInit(); @@ -268,12 +274,12 @@ namespace Avalonia.Controls /// /// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// - public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); + public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag(); /// /// Starts resizing a window. This function is used if an application has window resizing controls. /// Should be called from left mouse button press event handler /// - public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); + public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge); } } diff --git a/src/Avalonia.DesignerSupport/DesignerAssist.cs b/src/Avalonia.DesignerSupport/DesignerAssist.cs index c9ae89354c..8d30f3cf25 100644 --- a/src/Avalonia.DesignerSupport/DesignerAssist.cs +++ b/src/Avalonia.DesignerSupport/DesignerAssist.cs @@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport private static void SetScalingFactor(double factor) { PlatformManager.SetDesignerScalingFactor(factor); - s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize); + s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize); } static Window s_currentWindow; @@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport s_currentWindow = window; window.Show(); Design.ApplyDesignerProperties(window, control); + // ReSharper disable once PossibleNullReferenceException + // Always not null at this point Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle); Api.OnResize?.Invoke(); } diff --git a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs index bdee85c91e..a484d6c0d2 100644 --- a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs @@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding { private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); - private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle; + private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero; public WinFormsAvaloniaControlHost() { @@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding if (_root.IsFocused) FocusManager.Instance.Focus(null); _root.GotFocus += RootGotFocus; + // ReSharper disable once PossibleNullReferenceException + // Always non-null at this point _root.PlatformImpl.LostFocus += PlatformImpl_LostFocus; FixPosition(); }