From fec2823537e88bf79be32ca1d0be1348b63c03a5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 22 Aug 2017 00:34:17 +0200 Subject: [PATCH 01/75] StyleResources -> ResourceDictionary. And move it to the `Avalonia.Controls` namespace. --- .../StyleResources.cs => Controls/ResourceDictionary.cs} | 6 +++--- src/Avalonia.Styling/Styling/Style.cs | 7 ++++--- tests/Avalonia.Styling.UnitTests/ResourceTests.cs | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) rename src/Avalonia.Styling/{Styling/StyleResources.cs => Controls/ResourceDictionary.cs} (95%) diff --git a/src/Avalonia.Styling/Styling/StyleResources.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs similarity index 95% rename from src/Avalonia.Styling/Styling/StyleResources.cs rename to src/Avalonia.Styling/Controls/ResourceDictionary.cs index e447c6adfd..96f523c83e 100644 --- a/src/Avalonia.Styling/Styling/StyleResources.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -2,12 +2,12 @@ using System.Collections; using System.Collections.Generic; -namespace Avalonia.Styling +namespace Avalonia.Controls { /// - /// Holds resources for a . + /// An indexed dictionary of resources. /// - public class StyleResources : IDictionary, IDictionary + public class ResourceDictionary : IDictionary, IDictionary { private Dictionary _inner = new Dictionary(); diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 3dfd9118af..d7df465722 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Reactive.Linq; +using Avalonia.Controls; using Avalonia.Metadata; namespace Avalonia.Styling @@ -16,7 +17,7 @@ namespace Avalonia.Styling private static Dictionary> _applied = new Dictionary>(); - private StyleResources _resources; + private ResourceDictionary _resources; /// /// Initializes a new instance of the class. @@ -37,13 +38,13 @@ namespace Avalonia.Styling /// /// Gets or sets a dictionary of style resources. /// - public StyleResources Resources + public ResourceDictionary Resources { get { if (_resources == null) { - _resources = new StyleResources(); + _resources = new ResourceDictionary(); } return _resources; diff --git a/tests/Avalonia.Styling.UnitTests/ResourceTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceTests.cs index a2535e0fb5..1efd043f6a 100644 --- a/tests/Avalonia.Styling.UnitTests/ResourceTests.cs +++ b/tests/Avalonia.Styling.UnitTests/ResourceTests.cs @@ -20,7 +20,7 @@ namespace Avalonia.Styling.UnitTests { new Style { - Resources = new StyleResources + Resources = new ResourceDictionary { { "Foo", "foo resource" }, { "Bar", "overridden" }, @@ -33,14 +33,14 @@ namespace Avalonia.Styling.UnitTests { new Style { - Resources = new StyleResources + Resources = new ResourceDictionary { { "Bar", "again overridden" }, } }, new Style { - Resources = new StyleResources + Resources = new ResourceDictionary { { "Bar", "bar resource" }, } @@ -64,7 +64,7 @@ namespace Avalonia.Styling.UnitTests { new Style { - Resources = new StyleResources + Resources = new ResourceDictionary { { "Foo", "foo" }, } From 39fc0ccdb5c23d2c973d05d307c2da0ef3b87d64 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 22 Aug 2017 00:58:56 +0200 Subject: [PATCH 02/75] Added IResourceDictionary. --- .../Controls/IResourceDictionary.cs | 13 +++++++++++++ src/Avalonia.Styling/Controls/ResourceDictionary.cs | 2 +- src/Avalonia.Styling/Styling/Style.cs | 7 ++----- 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 src/Avalonia.Styling/Controls/IResourceDictionary.cs diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Styling/Controls/IResourceDictionary.cs new file mode 100644 index 0000000000..9891249568 --- /dev/null +++ b/src/Avalonia.Styling/Controls/IResourceDictionary.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Avalonia.Controls +{ + /// + /// An indexed dictionary of resources. + /// + public interface IResourceDictionary : IDictionary + { + } +} diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index 96f523c83e..125bb0dcdf 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -7,7 +7,7 @@ namespace Avalonia.Controls /// /// An indexed dictionary of resources. /// - public class ResourceDictionary : IDictionary, IDictionary + public class ResourceDictionary : IResourceDictionary, IDictionary { private Dictionary _inner = new Dictionary(); diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index d7df465722..637e583a25 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -17,7 +17,7 @@ namespace Avalonia.Styling private static Dictionary> _applied = new Dictionary>(); - private ResourceDictionary _resources; + private IResourceDictionary _resources; /// /// Initializes a new instance of the class. @@ -38,7 +38,7 @@ namespace Avalonia.Styling /// /// Gets or sets a dictionary of style resources. /// - public ResourceDictionary Resources + public IResourceDictionary Resources { get { @@ -52,15 +52,12 @@ namespace Avalonia.Styling set { - var resources = Resources; if (!Equals(resources, value)) { foreach (var i in value) { resources[i.Key] = i.Value; - //resources.Add(i.Key, i.Value); - //(resources as IDictionary).Add(i); } } } From b5df0cdcee6f0a7c6c56b767daf9dc60ed0dd82f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 22 Aug 2017 01:03:02 +0200 Subject: [PATCH 03/75] Remove Style.Resources setter. We don't need it. --- src/Avalonia.Styling/Styling/Style.cs | 12 ------------ tests/Avalonia.Styling.UnitTests/ResourceTests.cs | 8 ++++---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 637e583a25..80238889c8 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -49,18 +49,6 @@ namespace Avalonia.Styling return _resources; } - - set - { - var resources = Resources; - if (!Equals(resources, value)) - { - foreach (var i in value) - { - resources[i.Key] = i.Value; - } - } - } } /// diff --git a/tests/Avalonia.Styling.UnitTests/ResourceTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceTests.cs index 1efd043f6a..bfef1bd762 100644 --- a/tests/Avalonia.Styling.UnitTests/ResourceTests.cs +++ b/tests/Avalonia.Styling.UnitTests/ResourceTests.cs @@ -20,7 +20,7 @@ namespace Avalonia.Styling.UnitTests { new Style { - Resources = new ResourceDictionary + Resources = { { "Foo", "foo resource" }, { "Bar", "overridden" }, @@ -33,14 +33,14 @@ namespace Avalonia.Styling.UnitTests { new Style { - Resources = new ResourceDictionary + Resources = { { "Bar", "again overridden" }, } }, new Style { - Resources = new ResourceDictionary + Resources = { { "Bar", "bar resource" }, } @@ -64,7 +64,7 @@ namespace Avalonia.Styling.UnitTests { new Style { - Resources = new ResourceDictionary + Resources = { { "Foo", "foo" }, } From 84aa27162f33bc80d60a66ad4eca57a1435d2ad4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 22 Aug 2017 02:59:41 +0200 Subject: [PATCH 04/75] Made a start adding Control/Application.Resources. --- samples/ControlCatalog/MainWindow.xaml.cs | 2 +- src/Avalonia.Controls/Application.cs | 19 +++- src/Avalonia.Controls/Control.cs | 15 ++++ src/Avalonia.Controls/ControlExtensions.cs | 29 +++++++ src/Avalonia.Controls/IControl.cs | 2 +- .../Presenters/TextPresenter.cs | 2 +- src/Avalonia.Styling/Avalonia.Styling.csproj | 1 + .../Controls/IResourceDictionary.cs | 17 +++- .../Controls/IResourceHost.cs | 22 +++++ .../Controls/ResourceDictionary.cs | 2 + src/Avalonia.Styling/Styling/IStyle.cs | 13 +-- src/Avalonia.Styling/Styling/IStyleHost.cs | 1 - src/Avalonia.Styling/Styling/Style.cs | 22 +---- .../Styling/StyleExtensions.cs | 39 --------- src/Avalonia.Styling/Styling/Styles.cs | 40 ++++++--- .../Data/StyleResourceBinding.cs | 7 +- .../Styling/StyleInclude.cs | 14 +-- .../ControlTests_Resources.cs | 87 +++++++++++++++++++ .../FullLayoutTests.cs | 4 + .../Xaml/BasicTests.cs | 6 +- .../Xaml/StyleTests.cs | 12 +-- .../ResourceTests.cs | 78 ----------------- tests/Avalonia.UnitTests/TestRoot.cs | 4 + 23 files changed, 249 insertions(+), 189 deletions(-) create mode 100644 src/Avalonia.Styling/Controls/IResourceHost.cs delete mode 100644 src/Avalonia.Styling/Styling/StyleExtensions.cs create mode 100644 tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs delete mode 100644 tests/Avalonia.Styling.UnitTests/ResourceTests.cs diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 413794dfa2..0d24967482 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -19,7 +19,7 @@ namespace ControlCatalog // so we must refer to this resource DLL statically. For // now I am doing that here. But we need a better solution!! var theme = new Avalonia.Themes.Default.DefaultTheme(); - theme.FindResource("Button"); + theme.TryGetResource("Button"); AvaloniaXamlLoader.Load(this); } } diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 3d13608226..0d89ad65b1 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -29,7 +29,7 @@ namespace Avalonia /// method. /// - Tracks the lifetime of the application. /// - public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IApplicationLifecycle + public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceHost { /// /// The application-global data templates. @@ -39,6 +39,7 @@ namespace Avalonia private readonly Lazy _clipboard = new Lazy(() => (IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))); private readonly Styler _styler = new Styler(); + private ResourceDictionary _resources; /// /// Initializes a new instance of the class. @@ -100,6 +101,11 @@ namespace Avalonia /// public IClipboard Clipboard => _clipboard.Value; + /// + /// Gets the application's global resource dictionary. + /// + public IResourceDictionary Resources => _resources ?? (_resources = new ResourceDictionary()); + /// /// Gets the application's global styles. /// @@ -142,13 +148,20 @@ namespace Avalonia { OnExit?.Invoke(this, EventArgs.Empty); } - + + /// + bool IResourceHost.TryGetResource(string key, out object value) + { + value = null; + return _resources?.TryGetResource(key, out value) ?? + Styles.TryGetResource(key, out value); + } + /// /// Sent when the application is exiting. /// public event EventHandler OnExit; - /// /// Called when the application is exiting. /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index eca5967a58..ab5d9a7f06 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -97,6 +97,7 @@ namespace Avalonia.Controls private bool _isAttachedToLogicalTree; private IAvaloniaList _logicalChildren; private INameScope _nameScope; + private ResourceDictionary _resources; private Styles _styles; private bool _styled; private Subject _styleDetach = new Subject(); @@ -286,6 +287,11 @@ namespace Avalonia.Controls set { SetValue(ContextMenuProperty, value); } } + /// + /// Gets or sets the control's resource dictionary. + /// + public IResourceDictionary Resources => _resources ?? (_resources = new ResourceDictionary()); + /// /// Gets or sets a user-defined object attached to the control. /// @@ -418,6 +424,15 @@ namespace Avalonia.Controls /// protected IPseudoClasses PseudoClasses => Classes; + /// + bool IResourceHost.TryGetResource(string key, out object value) + { + value = null; + return _resources?.TryGetResource(key, out value) ?? + _styles?.TryGetResource(key, out value) ?? + false; + } + /// /// Sets the control's logical parent. /// diff --git a/src/Avalonia.Controls/ControlExtensions.cs b/src/Avalonia.Controls/ControlExtensions.cs index 60a940627f..d0b8b75e7b 100644 --- a/src/Avalonia.Controls/ControlExtensions.cs +++ b/src/Avalonia.Controls/ControlExtensions.cs @@ -81,6 +81,35 @@ namespace Avalonia.Controls .FirstOrDefault(x => x != null); } + /// + /// Finds the specified resource by searching up the logical tree and then global styles. + /// + /// The control. + /// The resource key. + /// The resource, or null if not found. + public static object FindResource(this IControl control, string key) + { + Contract.Requires(control != null); + Contract.Requires(key != null); + + var current = control as IStyleHost; + + while (current != null) + { + if (current is IResourceHost host) + { + if (host.TryGetResource(key, out var value)) + { + return value; + } + } + + current = current.StylingParent; + } + + return null; + } + /// /// Adds or removes a pseudoclass depending on a boolean value. /// diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs index 3f5bd3fcac..02973bb38d 100644 --- a/src/Avalonia.Controls/IControl.cs +++ b/src/Avalonia.Controls/IControl.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// /// Interface for Avalonia controls. /// - public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IStyleable, IStyleHost + public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IResourceHost, IStyleable, IStyleHost { /// /// Occurs when the control has finished initialization. diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index a2b5f3f8b4..5f6b3ad4c8 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -115,7 +115,7 @@ namespace Avalonia.Controls.Presenters if (_highlightBrush == null) { - _highlightBrush = (IBrush)this.FindStyleResource("HighlightBrush"); + _highlightBrush = (IBrush)this.FindResource("HighlightBrush"); } foreach (var rect in rects) diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index 6bf37b522b..6b606616bb 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -2,6 +2,7 @@ netstandard1.3 false + Avalonia true diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Styling/Controls/IResourceDictionary.cs index 9891249568..bc76d8f60e 100644 --- a/src/Avalonia.Styling/Controls/IResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/IResourceDictionary.cs @@ -1,5 +1,7 @@ -using System; -using System.Collections; +// Copyright (c) The Avalonia 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.Collections.Generic; namespace Avalonia.Controls @@ -9,5 +11,16 @@ namespace Avalonia.Controls /// public interface IResourceDictionary : IDictionary { + /// + /// Tries to find a resource within the dictionary. + /// + /// The resource key. + /// + /// When this method returns, contains the value associated with the specified key, + /// if the key is found; otherwise, null + /// + /// True if the resource if found, otherwise false. + /// + bool TryGetResource(string key, out object value); } } diff --git a/src/Avalonia.Styling/Controls/IResourceHost.cs b/src/Avalonia.Styling/Controls/IResourceHost.cs new file mode 100644 index 0000000000..6cee7083e0 --- /dev/null +++ b/src/Avalonia.Styling/Controls/IResourceHost.cs @@ -0,0 +1,22 @@ +using System; + +namespace Avalonia.Controls +{ + /// + /// Defines an element that can be queried for resources. + /// + public interface IResourceHost + { + /// + /// Tries to find a resource within the element. + /// + /// The resource key. + /// + /// When this method returns, contains the value associated with the specified key, + /// if the key is found; otherwise, null + /// + /// True if the resource if found, otherwise false. + /// + bool TryGetResource(string key, out object value); + } +} diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index 125bb0dcdf..58dee17775 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -55,6 +55,8 @@ namespace Avalonia.Controls public bool TryGetValue(string key, out object value) => _inner.TryGetValue(key, out value); + public bool TryGetResource(string key, out object value) => _inner.TryGetValue(key, out value); + bool ICollection>.Contains(KeyValuePair item) { return ((IDictionary)_inner).Contains(item); diff --git a/src/Avalonia.Styling/Styling/IStyle.cs b/src/Avalonia.Styling/Styling/IStyle.cs index 7b8510fe2d..70f6c60d14 100644 --- a/src/Avalonia.Styling/Styling/IStyle.cs +++ b/src/Avalonia.Styling/Styling/IStyle.cs @@ -1,12 +1,14 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Controls; + namespace Avalonia.Styling { /// /// Defines the interface for styles. /// - public interface IStyle + public interface IStyle : IResourceHost { /// /// Attaches the style to a control if the style's selector matches. @@ -16,14 +18,5 @@ namespace Avalonia.Styling /// The control that contains this style. May be null. /// void Attach(IStyleable control, IStyleHost container); - - /// - /// Tries to find a named resource within the style. - /// - /// The resource name. - /// - /// The resource if found, otherwise . - /// - object FindResource(string name); } } diff --git a/src/Avalonia.Styling/Styling/IStyleHost.cs b/src/Avalonia.Styling/Styling/IStyleHost.cs index 8422f18b46..466edc4423 100644 --- a/src/Avalonia.Styling/Styling/IStyleHost.cs +++ b/src/Avalonia.Styling/Styling/IStyleHost.cs @@ -17,6 +17,5 @@ namespace Avalonia.Styling /// Gets the parent style host element. /// IStyleHost StylingParent { get; } - } } diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 80238889c8..5dc0409cf0 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -98,25 +98,11 @@ namespace Avalonia.Styling } } - /// - /// Tries to find a named resource within the style. - /// - /// The resource name. - /// - /// The resource if found, otherwise . - /// - public object FindResource(string name) + /// + public bool TryGetResource(string key, out object result) { - object result = null; - - if (_resources?.TryGetValue(name, out result) == true) - { - return result; - } - else - { - return AvaloniaProperty.UnsetValue; - } + result = null; + return _resources?.TryGetResource(key, out result) ?? false; } /// diff --git a/src/Avalonia.Styling/Styling/StyleExtensions.cs b/src/Avalonia.Styling/Styling/StyleExtensions.cs deleted file mode 100644 index e1073335a0..0000000000 --- a/src/Avalonia.Styling/Styling/StyleExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System; - -namespace Avalonia.Styling -{ - public static class StyleExtensions - { - /// - /// Tries to find a named style resource. - /// - /// The control from which to find the resource. - /// The resource name. - /// - /// The resource if found, otherwise . - /// - public static object FindStyleResource(this IStyleHost control, string name) - { - Contract.Requires(control != null); - Contract.Requires(name != null); - Contract.Requires(!string.IsNullOrWhiteSpace(name)); - - while (control != null) - { - var result = control.Styles.FindResource(name); - - if (result != AvaloniaProperty.UnsetValue) - { - return result; - } - - control = control.StylingParent; - } - - return AvaloniaProperty.UnsetValue; - } - } -} diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index 770ef8344f..75b8fd0c0c 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -3,6 +3,7 @@ using System.Linq; using Avalonia.Collections; +using Avalonia.Controls; namespace Avalonia.Styling { @@ -11,6 +12,24 @@ namespace Avalonia.Styling /// public class Styles : AvaloniaList, IStyle { + private IResourceDictionary _resources; + + /// + /// Gets or sets a dictionary of style resources. + /// + public IResourceDictionary Resources + { + get + { + if (_resources == null) + { + _resources = new ResourceDictionary(); + } + + return _resources; + } + } + /// /// Attaches the style to a control if the style's selector matches. /// @@ -26,26 +45,19 @@ namespace Avalonia.Styling } } - /// - /// Tries to find a named resource within the style. - /// - /// The resource name. - /// - /// The resource if found, otherwise . - /// - public object FindResource(string name) + /// + public bool TryGetResource(string key, out object value) { - foreach (var style in this.Reverse()) + for (var i = Count - 1; i >= 0; --i) { - var result = style.FindResource(name); - - if (result != AvaloniaProperty.UnsetValue) + if (this[i].TryGetResource(key, out value)) { - return result; + return true; } } - return AvaloniaProperty.UnsetValue; + value = null; + return false; } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs b/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs index c1a895f797..787aebbdc6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs @@ -46,11 +46,14 @@ namespace Avalonia.Markup.Xaml.Data if (host != null) { - resource = host.FindStyleResource(Name); + resource = host.FindResource(Name); } else if (style != null) { - resource = style.FindResource(Name); + if (!style.TryGetResource(Name, out resource)) + { + resource = AvaloniaProperty.UnsetValue; + } } if (resource != AvaloniaProperty.UnsetValue) diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 0f4824d493..a8fecdc6e1 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -3,6 +3,7 @@ using Avalonia.Styling; using System; +using Avalonia.Controls; namespace Avalonia.Markup.Xaml.Styling { @@ -55,16 +56,7 @@ namespace Avalonia.Markup.Xaml.Styling } } - /// - /// Tries to find a named resource within the style. - /// - /// The resource name. - /// - /// The resource if found, otherwise . - /// - public object FindResource(string name) - { - return Loaded.FindResource(name); - } + /// + public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value); } } \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs new file mode 100644 index 0000000000..361806c0a8 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs @@ -0,0 +1,87 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class ControlTests_Resources + { + [Fact] + public void FindResource_Should_Find_Control_Resource() + { + var target = new Control + { + Resources = + { + { "foo", "foo-value" }, + } + }; + + Assert.Equal("foo-value", target.FindResource("foo")); + } + + [Fact] + public void FindResource_Should_Find_Control_Resource_In_Parent() + { + Control target; + + var root = new Decorator + { + Resources = + { + { "foo", "foo-value" }, + }, + Child = target = new Control(), + }; + + Assert.Equal("foo-value", target.FindResource("foo")); + } + + [Fact] + public void FindResource_Should_Find_Application_Resource() + { + Control target; + + var app = new Application + { + Resources = + { + { "foo", "foo-value" }, + }, + }; + + var root = new TestRoot + { + Child = target = new Control(), + StylingParent = app, + }; + + Assert.Equal("foo-value", target.FindResource("foo")); + } + + [Fact] + public void FindResource_Should_Find_Style_Resource() + { + var target = new Control + { + Styles = + { + new Style + { + Resources = + { + { "foo", "foo-value" }, + } + } + } + }; + + Assert.Equal("foo-value", target.FindResource("foo")); + } + + } +} diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index 6b7c73da2a..006306a3e4 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -162,6 +162,10 @@ namespace Avalonia.Layout.UnitTests private void RegisterServices() { var globalStyles = new Mock(); + var globalStylesResources = globalStyles.As(); + var outObj = (object)10; + globalStylesResources.Setup(x => x.TryGetResource("FontSizeNormal", out outObj)).Returns(true); + var renderInterface = new Mock(); renderInterface.Setup(x => x.CreateFormattedText( diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index 318a98ad43..ce370790c3 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -480,13 +480,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.True(style.Resources.Count > 0); - var brush = style.FindResource("Brush") as SolidColorBrush; + style.TryGetResource("Brush", out var brush); Assert.NotNull(brush); - Assert.Equal(Colors.White, brush.Color); + Assert.Equal(Colors.White, ((SolidColorBrush)brush).Color); - var d = (double)style.FindResource("Double"); + style.TryGetResource("Double", out var d); Assert.Equal(10.0, d); } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs index 0c5a89b827..65fc9eaddd 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs @@ -141,7 +141,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml var loader = new AvaloniaXamlLoader(); var window = (Window)loader.Load(xaml); - var brush = (ISolidColorBrush)window.FindStyleResource("brush"); + var brush = (ISolidColorBrush)window.FindResource("brush"); var button = window.FindControl IStyleHost IStyleHost.StylingParent => null; + /// + bool IResourceProvider.HasResources => _resources?.Count > 0; + /// /// Initializes the application by loading XAML etc. /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 81acf1a204..c93742b7f6 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -382,6 +382,9 @@ namespace Avalonia.Controls /// IAvaloniaReadOnlyList ILogical.LogicalChildren => LogicalChildren; + /// + bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources; + /// IAvaloniaReadOnlyList IStyleable.Classes => Classes; diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceProvider.cs index 9308b7a18d..e3ea2e1aab 100644 --- a/src/Avalonia.Styling/Controls/IResourceProvider.cs +++ b/src/Avalonia.Styling/Controls/IResourceProvider.cs @@ -12,6 +12,11 @@ namespace Avalonia.Controls /// event EventHandler ResourcesChanged; + /// + /// Gets a value indicating whether the provider has resources. + /// + bool HasResources { get; } + /// /// Tries to find a resource within the element. /// diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 1ea99a42ac..b095f38035 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -66,6 +66,9 @@ namespace Avalonia.Styling [Content] public IList Setters { get; set; } = new List(); + /// + bool IResourceProvider.HasResources => _resources?.Count > 0; + /// /// Attaches the style to a control if the style's selector matches. /// diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index 4ce41f6f46..47647d726e 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -20,14 +20,33 @@ namespace Avalonia.Styling { ResetBehavior = ResetBehavior.Remove; this.ForEachItem( - x => x.ResourcesChanged += SubResourceChanged, - x => x.ResourcesChanged -= SubResourceChanged, + x => + { + if (x.HasResources) + { + ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + } + + x.ResourcesChanged += SubResourceChanged; + }, + x => + { + if (x.HasResources) + { + ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + } + + x.ResourcesChanged -= SubResourceChanged; + }, () => { }); } /// public event EventHandler ResourcesChanged; + /// + public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources); + /// /// Gets or sets a dictionary of style resources. /// diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 556e2324cd..8e571af4a0 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -50,6 +50,9 @@ namespace Avalonia.Markup.Xaml.Styling } } + /// + bool IResourceProvider.HasResources => Loaded.HasResources; + /// public void Attach(IStyleable control, IStyleHost container) { diff --git a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs index 4fa00ab171..561cad87c7 100644 --- a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs +++ b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.Styling; using Avalonia.UnitTests; using Xunit; @@ -142,5 +144,112 @@ namespace Avalonia.Controls.UnitTests Assert.Equal("foo-value", target.FindResource("foo")); } + + [Fact] + public void Adding_Resource_Should_Call_Raise_ResourceChanged_On_Logical_Children() + { + Border child; + + var target = new ContentControl + { + Content = child = new Border(), + Template = ContentControlTemplate(), + }; + + var raisedOnTarget = false; + var raisedOnPresenter = false; + var raisedOnChild = false; + + target.Measure(Size.Infinity); + target.ResourcesChanged += (_, __) => raisedOnTarget = true; + target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true; + child.ResourcesChanged += (_, __) => raisedOnChild = true; + + target.Resources.Add("foo", "bar"); + + Assert.True(raisedOnTarget); + Assert.False(raisedOnPresenter); + Assert.True(raisedOnChild); + } + + [Fact] + public void Adding_Resource_To_Styles_Should_Raise_ResourceChanged() + { + var target = new Decorator(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Styles.Resources.Add("foo", "bar"); + + Assert.True(raised); + } + + [Fact] + public void Adding_Resource_To_Nested_Style_Should_Raise_ResourceChanged() + { + Style style; + var target = new Decorator + { + Styles = + { + (style = new Style()), + } + }; + + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + style.Resources.Add("foo", "bar"); + + Assert.True(raised); + } + + [Fact] + public void Adding_Style_With_Resource_Should_Raise_ResourceChanged() + { + Style style = new Style + { + Resources = { { "foo", "bar" } }, + }; + + var target = new Decorator(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Styles.Add(style); + + Assert.True(raised); + } + + [Fact] + public void Removing_Style_With_Resource_Should_Raise_ResourceChanged() + { + var target = new Decorator + { + Styles = + { + new Style + { + Resources = { { "foo", "bar" } }, + } + } + }; + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Styles.Clear(); + + Assert.True(raised); + } + + private IControlTemplate ContentControlTemplate() + { + return new FuncControlTemplate(x => + new ContentPresenter + { + Name = "PART_ContentPresenter", + [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty], + }); + } } } From 0e155bd2d4925800ad6fefe32cb4d6e891ba4d4a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 02:50:49 +0200 Subject: [PATCH 22/75] Make dynamic resources work in more situations. Fixes #492 in a fashion: `DynamicResource` now works for this scenario. --- src/Avalonia.Controls/Application.cs | 3 + src/Avalonia.Controls/Control.cs | 10 ++ src/Avalonia.Controls/ControlExtensions.cs | 29 ----- .../Controls/IResourceProvider.cs | 5 + .../Controls/ResourceProviderExtensions.cs | 49 ++++++++ src/Avalonia.Styling/LogicalTree/ILogical.cs | 2 +- .../Styling/ISetStyleParent.cs | 32 +++++ src/Avalonia.Styling/Styling/Style.cs | 23 +++- src/Avalonia.Styling/Styling/Styles.cs | 56 ++++++++- .../DynamicResourceExtension.cs | 16 +-- .../Styling/StyleInclude.cs | 25 +++- .../ControlTests_Resources.cs | 38 ------ .../DynamicResourceExtensionTests.cs | 4 - .../SelectorTests_Child.cs | 5 + .../SelectorTests_Descendent.cs | 5 + .../Avalonia.Styling.UnitTests/StylesTests.cs | 115 ++++++++++++++++++ 16 files changed, 329 insertions(+), 88 deletions(-) create mode 100644 src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs create mode 100644 src/Avalonia.Styling/Styling/ISetStyleParent.cs create mode 100644 tests/Avalonia.Styling.UnitTests/StylesTests.cs diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 4533855b73..ce15c0b9e3 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -128,6 +128,9 @@ namespace Avalonia /// bool IResourceProvider.HasResources => _resources?.Count > 0; + /// + IResourceProvider IResourceProvider.ResourceParent => null; + /// /// Initializes the application by loading XAML etc. /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index c93742b7f6..a40b59d60a 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -283,10 +283,17 @@ namespace Avalonia.Controls { if (_styles != null) { + (_styles as ISetStyleParent)?.SetParent(null); _styles.ResourcesChanged -= StyleResourcesChanged; } _styles = value; + + if (value is ISetStyleParent setParent && setParent.ResourceParent == null) + { + setParent.SetParent(this); + } + _styles.ResourcesChanged += StyleResourcesChanged; } } @@ -385,6 +392,9 @@ namespace Avalonia.Controls /// bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources; + /// + IResourceProvider IResourceProvider.ResourceParent => ((IStyleHost)this).StylingParent as IResourceProvider; + /// IAvaloniaReadOnlyList IStyleable.Classes => Classes; diff --git a/src/Avalonia.Controls/ControlExtensions.cs b/src/Avalonia.Controls/ControlExtensions.cs index 87c774dfcf..60a940627f 100644 --- a/src/Avalonia.Controls/ControlExtensions.cs +++ b/src/Avalonia.Controls/ControlExtensions.cs @@ -81,35 +81,6 @@ namespace Avalonia.Controls .FirstOrDefault(x => x != null); } - /// - /// Finds the specified resource by searching up the logical tree and then global styles. - /// - /// The control. - /// The resource key. - /// The resource, or if not found. - public static object FindResource(this IControl control, string key) - { - Contract.Requires(control != null); - Contract.Requires(key != null); - - var current = control as IStyleHost; - - while (current != null) - { - if (current is IResourceProvider host) - { - if (host.TryGetResource(key, out var value)) - { - return value; - } - } - - current = current.StylingParent; - } - - return AvaloniaProperty.UnsetValue; - } - /// /// Adds or removes a pseudoclass depending on a boolean value. /// diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceProvider.cs index e3ea2e1aab..180476b2e4 100644 --- a/src/Avalonia.Styling/Controls/IResourceProvider.cs +++ b/src/Avalonia.Styling/Controls/IResourceProvider.cs @@ -17,6 +17,11 @@ namespace Avalonia.Controls /// bool HasResources { get; } + /// + /// Gets the parent resource provider, if any. + /// + IResourceProvider ResourceParent { get; } + /// /// Tries to find a resource within the element. /// diff --git a/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs b/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs new file mode 100644 index 0000000000..45e16438d0 --- /dev/null +++ b/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Linq; +using System.Text; + +namespace Avalonia.Controls +{ + public static class ResourceProviderExtensions + { + /// + /// Finds the specified resource by searching up the logical tree and then global styles. + /// + /// The control. + /// The resource key. + /// The resource, or if not found. + public static object FindResource(this IResourceProvider control, string key) + { + Contract.Requires(control != null); + Contract.Requires(key != null); + + var current = control; + + while (current != null) + { + if (current is IResourceProvider host) + { + if (host.TryGetResource(key, out var value)) + { + return value; + } + } + + current = current.ResourceParent; + } + + return AvaloniaProperty.UnsetValue; + } + + public static IObservable GetResourceObservable(this IResourceProvider target, string key) + { + return Observable.FromEventPattern( + x => target.ResourcesChanged += x, + x => target.ResourcesChanged -= x) + .StartWith((EventPattern)null) + .Select(x => target.FindResource(key)); + } + } +} diff --git a/src/Avalonia.Styling/LogicalTree/ILogical.cs b/src/Avalonia.Styling/LogicalTree/ILogical.cs index a6e804567d..8ee3c9ea4f 100644 --- a/src/Avalonia.Styling/LogicalTree/ILogical.cs +++ b/src/Avalonia.Styling/LogicalTree/ILogical.cs @@ -58,7 +58,7 @@ namespace Avalonia.LogicalTree void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e); /// - /// Notifies the control that a change has been made to its resources. + /// Notifies the control that a change has been made to resources that apply to it. /// /// The event args. /// diff --git a/src/Avalonia.Styling/Styling/ISetStyleParent.cs b/src/Avalonia.Styling/Styling/ISetStyleParent.cs new file mode 100644 index 0000000000..da5b34798e --- /dev/null +++ b/src/Avalonia.Styling/Styling/ISetStyleParent.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Controls; + +namespace Avalonia.Styling +{ + /// + /// Defines an interface through which a 's parent can be set. + /// + /// + /// You should not usually need to use this interface - it is for internal use only. + /// + public interface ISetStyleParent : IStyle + { + /// + /// Sets the style parent. + /// + /// The parent. + void SetParent(IResourceProvider parent); + + /// + /// Notifies the style that a change has been made to resources that apply to it. + /// + /// The event args. + /// + /// This method will be called automatically by the framework, you should not need to call + /// this method yourself. + /// + void NotifyResourcesChanged(ResourcesChangedEventArgs e); + } +} diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index b095f38035..54b53868da 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -13,10 +13,11 @@ namespace Avalonia.Styling /// /// Defines a style. /// - public class Style : IStyle + public class Style : IStyle, ISetStyleParent { private static Dictionary> _applied = new Dictionary>(); + private IResourceProvider _parent; private ResourceDictionary _resources; /// @@ -69,6 +70,9 @@ namespace Avalonia.Styling /// bool IResourceProvider.HasResources => _resources?.Count > 0; + /// + IResourceProvider IResourceProvider.ResourceParent => _parent; + /// /// Attaches the style to a control if the style's selector matches. /// @@ -128,6 +132,23 @@ namespace Avalonia.Styling } } + /// + void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e) + { + ResourcesChanged?.Invoke(this, e); + } + + /// + void ISetStyleParent.SetParent(IResourceProvider parent) + { + if (_parent != null && parent != null) + { + throw new InvalidOperationException("The Style already has a parent."); + } + + _parent = parent; + } + private static List GetSubscriptions(IStyleable control) { List subscriptions; diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index 47647d726e..43a542d460 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -12,8 +12,9 @@ namespace Avalonia.Styling /// /// A style that consists of a number of child styles. /// - public class Styles : AvaloniaList, IStyle + public class Styles : AvaloniaList, IStyle, ISetStyleParent { + private IResourceProvider _parent; private ResourceDictionary _resources; public Styles() @@ -22,6 +23,12 @@ namespace Avalonia.Styling this.ForEachItem( x => { + if (x.ResourceParent == null && x is ISetStyleParent setParent) + { + setParent.SetParent(this); + setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs()); + } + if (x.HasResources) { ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); @@ -31,6 +38,12 @@ namespace Avalonia.Styling }, x => { + if (x.ResourceParent == this && x is ISetStyleParent setParent) + { + setParent.SetParent(null); + setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs()); + } + if (x.HasResources) { ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); @@ -64,6 +77,9 @@ namespace Avalonia.Styling } } + /// + IResourceProvider IResourceProvider.ResourceParent => _parent; + /// /// Attaches the style to a control if the style's selector matches. /// @@ -99,13 +115,49 @@ namespace Avalonia.Styling return false; } + /// + void ISetStyleParent.SetParent(IResourceProvider parent) + { + if (_parent != null && parent != null) + { + throw new InvalidOperationException("The Style already has a parent."); + } + + _parent = parent; + } + + /// + void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e) + { + ResourcesChanged?.Invoke(this, e); + } + private void ResourceDictionaryChanged(object sender, NotifyCollectionChangedEventArgs e) { - ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + var ev = new ResourcesChangedEventArgs(); + + foreach (var child in this) + { + (child as ISetStyleParent)?.NotifyResourcesChanged(ev); + } + + ResourcesChanged?.Invoke(this, ev); } private void SubResourceChanged(object sender, ResourcesChangedEventArgs e) { + var foundSource = false; + + foreach (var child in this) + { + if (foundSource) + { + (child as ISetStyleParent)?.NotifyResourcesChanged(e); + } + + foundSource |= child == sender; + } + ResourcesChanged?.Invoke(this, e); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 7921510944..905bf16d1f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { public class DynamicResourceExtension : MarkupExtension, IBinding { - private IControl _anchor; + private IResourceProvider _anchor; public DynamicResourceExtension() { @@ -33,9 +33,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions var context = (ITypeDescriptorContext)serviceProvider; var provideTarget = context.GetService(); - if (!(provideTarget.TargetObject is IControl)) + if (!(provideTarget.TargetObject is IResourceProvider)) { - _anchor = GetAnchor(context); + _anchor = GetAnchor(context); } return this; @@ -47,17 +47,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions object anchor, bool enableDataValidation) { - var control = target as IControl ?? _anchor as IControl; + var control = target as IResourceProvider ?? _anchor; if (control != null) { - var o = Observable.FromEventPattern( - x => control.ResourcesChanged += x, - x => control.ResourcesChanged -= x) - .StartWith((EventPattern)null) - .Select(x => control.FindResource(ResourceKey)); - - return new InstancedBinding(o); + return new InstancedBinding(control.GetResourceObservable(ResourceKey)); } return null; diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 8e571af4a0..a121cd8c1b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -10,16 +10,16 @@ namespace Avalonia.Markup.Xaml.Styling /// /// Includes a style from a URL. /// - public class StyleInclude : IStyle + public class StyleInclude : IStyle, ISetStyleParent { private Uri _baseUri; private IStyle _loaded; + private IResourceProvider _parent; /// /// Initializes a new instance of the class. /// /// - public StyleInclude(Uri baseUri) { _baseUri = baseUri; @@ -44,6 +44,7 @@ namespace Avalonia.Markup.Xaml.Styling { var loader = new AvaloniaXamlLoader(); _loaded = (IStyle)loader.Load(Source, _baseUri); + (_loaded as ISetStyleParent)?.SetParent(this); } return _loaded; @@ -53,6 +54,9 @@ namespace Avalonia.Markup.Xaml.Styling /// bool IResourceProvider.HasResources => Loaded.HasResources; + /// + IResourceProvider IResourceProvider.ResourceParent => _parent; + /// public void Attach(IStyleable control, IStyleHost container) { @@ -64,5 +68,22 @@ namespace Avalonia.Markup.Xaml.Styling /// public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value); + + /// + void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e) + { + (Loaded as ISetStyleParent)?.NotifyResourcesChanged(e); + } + + /// + void ISetStyleParent.SetParent(IResourceProvider parent) + { + if (_parent != null && parent != null) + { + throw new InvalidOperationException("The Style already has a parent."); + } + + _parent = parent; + } } } \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs index 561cad87c7..6a6cb48001 100644 --- a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs +++ b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs @@ -204,44 +204,6 @@ namespace Avalonia.Controls.UnitTests Assert.True(raised); } - [Fact] - public void Adding_Style_With_Resource_Should_Raise_ResourceChanged() - { - Style style = new Style - { - Resources = { { "foo", "bar" } }, - }; - - var target = new Decorator(); - var raised = false; - - target.ResourcesChanged += (_, __) => raised = true; - target.Styles.Add(style); - - Assert.True(raised); - } - - [Fact] - public void Removing_Style_With_Resource_Should_Raise_ResourceChanged() - { - var target = new Decorator - { - Styles = - { - new Style - { - Resources = { { "foo", "bar" } }, - } - } - }; - var raised = false; - - target.ResourcesChanged += (_, __) => raised = true; - target.Styles.Clear(); - - Assert.True(raised); - } - private IControlTemplate ContentControlTemplate() { return new FuncControlTemplate(x => diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 46a90635b2..a8e93b29f0 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -360,8 +360,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'> Red - Green - Blue "; var style2Xaml = @" @@ -369,8 +367,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'> - - "; using (StyledWindow( diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs index 50b4828e73..352efe5358 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs @@ -154,6 +154,11 @@ namespace Avalonia.Styling.UnitTests { throw new NotImplementedException(); } + + public void NotifyResourcesChanged(ResourcesChangedEventArgs e) + { + throw new NotImplementedException(); + } } public class TestLogical1 : TestLogical diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs index 7cf8c3dd1c..c413904c8f 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs @@ -184,6 +184,11 @@ namespace Avalonia.Styling.UnitTests { throw new NotImplementedException(); } + + public void NotifyResourcesChanged(ResourcesChangedEventArgs e) + { + throw new NotImplementedException(); + } } public class TestLogical1 : TestLogical diff --git a/tests/Avalonia.Styling.UnitTests/StylesTests.cs b/tests/Avalonia.Styling.UnitTests/StylesTests.cs new file mode 100644 index 0000000000..c033dad0c6 --- /dev/null +++ b/tests/Avalonia.Styling.UnitTests/StylesTests.cs @@ -0,0 +1,115 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using Xunit; + +namespace Avalonia.Styling.UnitTests +{ + public class StylesTests + { + [Fact] + public void Adding_Style_With_Resources_Should_Raise_ResourceChanged() + { + var style = new Style + { + Resources = { { "foo", "bar" } }, + }; + + var target = new Styles(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Add(style); + + Assert.True(raised); + } + + [Fact] + public void Removing_Style_With_Resources_Should_Raise_ResourceChanged() + { + var target = new Styles + { + new Style + { + Resources = { { "foo", "bar" } }, + } + }; + + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Clear(); + + Assert.True(raised); + } + + [Fact] + public void Adding_Style_Without_Resources_Should_Not_Raise_ResourceChanged() + { + var style = new Style(); + var target = new Styles(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Add(style); + + Assert.False(raised); + } + + [Fact] + public void Adding_Resource_Should_Raise_Child_ResourceChanged() + { + Style child; + var target = new Styles + { + (child = new Style()), + }; + + var raised = false; + + child.ResourcesChanged += (_, __) => raised = true; + target.Resources.Add("foo", "bar"); + + Assert.True(raised); + } + + [Fact] + public void Adding_Resource_To_Younger_Sibling_Style_Should_Raise_ResourceChanged() + { + Style style1; + Style style2; + var target = new Styles + { + (style1 = new Style()), + (style2 = new Style()), + }; + + var raised = false; + + style2.ResourcesChanged += (_, __) => raised = true; + style1.Resources.Add("foo", "bar"); + + Assert.True(raised); + } + + [Fact] + public void Adding_Resource_To_Older_Sibling_Style_Should_Raise_ResourceChanged() + { + Style style1; + Style style2; + var target = new Styles + { + (style1 = new Style()), + (style2 = new Style()), + }; + + var raised = false; + + style1.ResourcesChanged += (_, __) => raised = true; + style2.Resources.Add("foo", "bar"); + + Assert.False(raised); + } + } +} From 71e8b4fc69415c2aa3a90dccd3fe225e2a66fc2b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 11:21:57 +0200 Subject: [PATCH 23/75] Update dynamic resources on set parent. --- src/Avalonia.Controls/Control.cs | 1 + .../DynamicResourceExtensionTests.cs | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index a40b59d60a..c9a3b2940d 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -521,6 +521,7 @@ namespace Avalonia.Controls } _parent = (IControl)parent; + ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs()); if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot) { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index a8e93b29f0..1a036546b4 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -393,6 +393,38 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Control_Property_Is_Updated_When_Parent_Is_Changed() + { + var xaml = @" + + + #ff506070 + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + + userControl.Content = null; + + Assert.Null(border.Background); + + userControl.Content = border; + + brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + private IDisposable StyledWindow(params (string, string)[] assets) { var services = TestServices.StyledWindow.With( From a0bda15576424eaa9b0b649685c7bdd50d028f4e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 11:30:27 +0200 Subject: [PATCH 24/75] More resource extension tests. One failing. --- .../DynamicResourceExtensionTests.cs | 22 +++++++++ .../StaticResourceExtensionTests.cs | 47 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 1a036546b4..f9c78eb80c 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -40,6 +40,28 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } + [Fact] + public void DynamicResource_Can_Be_Assigned_To_Attached_Property() + { + var xaml = @" + + + 5 + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + Assert.Equal(5, Grid.GetColumn(border)); + } + [Fact] public void DynamicResource_From_Style_Can_Be_Assigned_To_Property() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index cccbf4d0b5..fc30b0dd6d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -37,6 +37,27 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } + + [Fact] + public void StaticResource_Can_Be_Assigned_To_Attached_Property() + { + var xaml = @" + + + 5 + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + Assert.Equal(5, Grid.GetColumn(border)); + } + [Fact] public void StaticResource_From_Style_Can_Be_Assigned_To_Property() { @@ -290,6 +311,32 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed() + { + var xaml = @" + + + #ff506070 + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + + userControl.Content = null; + + brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + private IDisposable StyledWindow(params (string, string)[] assets) { var services = TestServices.StyledWindow.With( From 9a74e8409ea1c5305c4b5ea481dfc4b359e6159a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 13:19:23 +0200 Subject: [PATCH 25/75] Ensure properties registered. When calling `AvaloniaPropertyRegistry.GetAttached`. --- src/Avalonia.Base/AvaloniaPropertyRegistry.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs index 190b3ee2be..ec1643427b 100644 --- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs +++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs @@ -47,6 +47,9 @@ namespace Avalonia { Dictionary inner; + // Ensure the type's static ctor has been run. + RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle); + if (_attached.TryGetValue(ownerType, out inner)) { return inner.Values; From fcee846228d4c0d957a24913cf3ecdb52935ff02 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 19:27:59 +0200 Subject: [PATCH 26/75] Added tests for #1020 Passing. Closes #1020. --- .../DynamicResourceExtensionTests.cs | 25 +++++++++++++++++++ .../StaticResourceExtensionTests.cs | 23 ++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index f9c78eb80c..7de60a1029 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -298,6 +298,31 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } + + [Fact] + public void DynamicResource_Can_Be_Assigned_To_ItemTemplate_Property() + { + var xaml = @" + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var listBox = userControl.FindControl("listBox"); + + DelayedBinding.ApplyBindings(listBox); + + Assert.NotNull(listBox.ItemTemplate); + } + [Fact] public void DynamicResource_Tracks_Added_Resource() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index fc30b0dd6d..4f14f1eba8 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -37,7 +37,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } - [Fact] public void StaticResource_Can_Be_Assigned_To_Attached_Property() { @@ -311,6 +310,28 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void StaticResource_Can_Be_Assigned_To_ItemTemplate_Property() + { + var xaml = @" + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var listBox = userControl.FindControl("listBox"); + + Assert.NotNull(listBox.ItemTemplate); + } + [Fact] public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed() { From 2c1efe3773b95a7c7ec59b97c4e75192a96b72dc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 22:57:04 +0200 Subject: [PATCH 27/75] Static resource as binding converter Enable using a `StaticResource` as a binding converter. Fixes #818. --- .../DynamicResourceExtension.cs | 7 +++-- .../StaticResourceExtension.cs | 27 +++++++++++-------- .../StaticResourceExtensionTests.cs | 27 +++++++++++++++++++ .../MarkupExtensions/TestValueConverter.cs | 20 ++++++++++++++ 4 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 905bf16d1f..7937be60aa 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -3,7 +3,7 @@ using System; using System.ComponentModel; -using System.Reactive; +using System.Linq; using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Data; @@ -62,7 +62,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions var schemaContext = context.GetService()?.SchemaContext; var ambientProvider = context.GetService(); var xamlType = schemaContext.GetXamlType(typeof(T)); - return ambientProvider.GetFirstAmbientValue(xamlType) as T; + + // We override XamlType.CanAssignTo in BindingXamlType so the results we get back + // from GetAllAmbientValues aren't necessarily of the correct type. + return ambientProvider.GetAllAmbientValues(xamlType).OfType().FirstOrDefault(); } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index f92eca25bf..e3cd7b062b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -32,7 +32,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions var schemaContext = context.GetService()?.SchemaContext; var ambientProvider = context.GetService(); var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider)); - var resourceProviders = ambientProvider.GetAllAmbientValues(resourceProviderType); + var ambientValues = ambientProvider.GetAllAmbientValues(resourceProviderType); // Look upwards though the ambient context for IResourceProviders which might be able // to give us the resource. @@ -43,18 +43,23 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions // // StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File // - foreach (IResourceProvider resourceProvider in resourceProviders) + foreach (var ambientValue in ambientValues) { - if (resourceProvider is IControl control && control.StylingParent != null) + // We override XamlType.CanAssignTo in BindingXamlType so the results we get back + // from GetAllAmbientValues aren't necessarily of the correct type. + if (ambientValue is IResourceProvider resourceProvider) { - // If we've got to a control that has a StylingParent then it's probably - // a top level control and its StylingParent is pointing to the global - // styles. If this is case just do a FindResource on it. - return control.FindResource(ResourceKey); - } - else if (resourceProvider.TryGetResource(ResourceKey, out var value)) - { - return value; + if (resourceProvider is IControl control && control.StylingParent != null) + { + // If we've got to a control that has a StylingParent then it's probably + // a top level control and its StylingParent is pointing to the global + // styles. If this is case just do a FindResource on it. + return control.FindResource(ResourceKey); + } + else if (resourceProvider.TryGetResource(ResourceKey, out var value)) + { + return value; + } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index 4f14f1eba8..ed3ab3cc52 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -332,6 +332,33 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.NotNull(listBox.ItemTemplate); } + [Fact] + public void StaticResource_Can_Be_Assigned_To_Converter() + { + using (StyledWindow()) + { + var xaml = @" + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.DataContext = "foo"; + window.ApplyTemplate(); + + Assert.Equal("foobar", textBlock.Text); + } + } + [Fact] public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs new file mode 100644 index 0000000000..57570d8f5c --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; + +namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions +{ + public class TestValueConverter : IValueConverter + { + public string Append { get; set; } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value.ToString() + Append; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} From ea626a0e19cbec2c43b2d9fd859a9db2235a5376 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 24 Aug 2017 23:48:14 +0200 Subject: [PATCH 28/75] Updated ncrunch config --- .../Avalonia.Direct2D1.RenderTests.v3.ncrunchproject | 1 + ...onia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject | 9 +++++++++ .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject | 1 + src/Avalonia.Controls/Control.cs | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject index 235da29767..a8c3abe8f2 100644 --- a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject @@ -1,6 +1,7 @@  1000 + True True \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject new file mode 100644 index 0000000000..15d9efad87 --- /dev/null +++ b/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject @@ -0,0 +1,9 @@ + + + + + Avalonia.Markup.UnitTests.Data.Plugins.DataAnnotationsValidationPluginTests.Produces_Aggregate_BindingNotificationsx + + + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject index 235da29767..a8c3abe8f2 100644 --- a/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject @@ -1,6 +1,7 @@  1000 + True True \ No newline at end of file diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index c9a3b2940d..a2fc188000 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -292,7 +292,7 @@ namespace Avalonia.Controls if (value is ISetStyleParent setParent && setParent.ResourceParent == null) { setParent.SetParent(this); - } + } _styles.ResourcesChanged += StyleResourcesChanged; } From ac8cc99f16ce6491a859e3008ad460d8ab78012e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 25 Aug 2017 00:54:26 +0200 Subject: [PATCH 29/75] Started adding MergedDictionaries. --- src/Avalonia.Controls/Control.cs | 13 +- .../Controls/IResourceDictionary.cs | 10 + .../Controls/IResourceProvider.cs | 2 +- .../Controls/ResourceDictionary.cs | 77 +++++++- .../ResourceDictionaryTests.cs | 175 ++++++++++++++++++ 5 files changed, 264 insertions(+), 13 deletions(-) create mode 100644 tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index a2fc188000..889edd48f8 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -284,7 +284,7 @@ namespace Avalonia.Controls if (_styles != null) { (_styles as ISetStyleParent)?.SetParent(null); - _styles.ResourcesChanged -= StyleResourcesChanged; + _styles.ResourcesChanged -= ThisResourcesChanged; } _styles = value; @@ -294,7 +294,7 @@ namespace Avalonia.Controls setParent.SetParent(this); } - _styles.ResourcesChanged += StyleResourcesChanged; + _styles.ResourcesChanged += ThisResourcesChanged; } } } @@ -323,7 +323,7 @@ namespace Avalonia.Controls if (_resources == null) { _resources = new ResourceDictionary(); - _resources.CollectionChanged += ResourceDictionaryChanged; + _resources.ResourcesChanged += ThisResourcesChanged; } return _resources; @@ -914,12 +914,7 @@ namespace Avalonia.Controls } } - private void ResourceDictionaryChanged(object sender, NotifyCollectionChangedEventArgs e) - { - ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs()); - } - - private void StyleResourcesChanged(object sender, ResourcesChangedEventArgs e) + private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e) { ((ILogical)this).NotifyResourcesChanged(e); } diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Styling/Controls/IResourceDictionary.cs index bc76d8f60e..0ddeda0f3d 100644 --- a/src/Avalonia.Styling/Controls/IResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/IResourceDictionary.cs @@ -11,6 +11,16 @@ namespace Avalonia.Controls /// public interface IResourceDictionary : IDictionary { + /// + /// Raised when resources in the dictionary are changed. + /// + event EventHandler ResourcesChanged; + + /// + /// Gets a collection of child resource dictionaries. + /// + IList MergedDictionaries { get; } + /// /// Tries to find a resource within the dictionary. /// diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceProvider.cs index 180476b2e4..e133427350 100644 --- a/src/Avalonia.Styling/Controls/IResourceProvider.cs +++ b/src/Avalonia.Styling/Controls/IResourceProvider.cs @@ -8,7 +8,7 @@ namespace Avalonia.Controls public interface IResourceProvider { /// - /// Raised when the resources in the element are changed. + /// Raised when resources in the element are changed. /// event EventHandler ResourcesChanged; diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index bd08680a42..975d27c08b 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -2,7 +2,8 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; using Avalonia.Collections; namespace Avalonia.Controls @@ -10,9 +11,79 @@ namespace Avalonia.Controls /// /// An indexed dictionary of resources. /// - public class ResourceDictionary : AvaloniaDictionary, IResourceDictionary, IDictionary + public class ResourceDictionary : AvaloniaDictionary, IResourceDictionary { + private AvaloniaList _mergedDictionaries; + + public event EventHandler ResourcesChanged; + + public ResourceDictionary() + { + CollectionChanged += OnCollectionChanged; + } + + public IList MergedDictionaries + { + get + { + if (_mergedDictionaries == null) + { + _mergedDictionaries = new AvaloniaList(); + _mergedDictionaries.ResetBehavior = ResetBehavior.Remove; + _mergedDictionaries.ForEachItem( + x => + { + if (x.Count > 0) + { + OnResourcesChanged(); + } + + x.ResourcesChanged += MergedDictionaryResourcesChanged; + }, + x => + { + if (x.Count > 0) + { + OnResourcesChanged(); + } + + x.ResourcesChanged -= MergedDictionaryResourcesChanged; + }, + () => { }); + } + + return _mergedDictionaries; + } + } + /// - public bool TryGetResource(string key, out object value) => TryGetValue(key, out value); + public bool TryGetResource(string key, out object value) + { + if (TryGetValue(key, out value)) + { + return true; + } + + if (_mergedDictionaries != null) + { + for (var i = _mergedDictionaries.Count - 1; i >= 0; --i) + { + if (_mergedDictionaries[i].TryGetResource(key, out value)) + { + return true; + } + } + } + + return false; + } + + private void OnResourcesChanged() + { + ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + } + + private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => OnResourcesChanged(); + private void MergedDictionaryResourcesChanged(object sender, ResourcesChangedEventArgs e) => OnResourcesChanged(); } } diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs new file mode 100644 index 0000000000..f31cbbac0a --- /dev/null +++ b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs @@ -0,0 +1,175 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using Avalonia.Controls; +using Xunit; + +namespace Avalonia.Styling.UnitTests +{ + public class ResourceDictionaryTests + { + [Fact] + public void TryGetResource_Should_Find_Resource() + { + var target = new ResourceDictionary + { + { "foo", "bar" }, + }; + + Assert.True(target.TryGetResource("foo", out var result)); + Assert.Equal("bar", result); + } + + [Fact] + public void TryGetResource_Should_Find_Resource_From_Merged_Dictionary() + { + var target = new ResourceDictionary + { + MergedDictionaries = + { + new ResourceDictionary + { + { "foo", "bar" }, + } + } + }; + + Assert.True(target.TryGetResource("foo", out var result)); + Assert.Equal("bar", result); + } + + [Fact] + public void TryGetResource_Should_Find_Resource_From_Itself_Before_Merged_Dictionary() + { + var target = new ResourceDictionary + { + { "foo", "bar" }, + }; + + target.MergedDictionaries.Add(new ResourceDictionary + { + { "foo", "baz" }, + }); + + Assert.True(target.TryGetResource("foo", out var result)); + Assert.Equal("bar", result); + } + + [Fact] + public void TryGetResource_Should_Find_Resource_From_Later_Merged_Dictionary() + { + var target = new ResourceDictionary + { + MergedDictionaries = + { + new ResourceDictionary + { + { "foo", "bar" }, + }, + new ResourceDictionary + { + { "foo", "baz" }, + } + } + }; + + Assert.True(target.TryGetResource("foo", out var result)); + Assert.Equal("baz", result); + } + + [Fact] + public void ResourcesChanged_Should_Be_Raised_On_Resource_Add() + { + var target = new ResourceDictionary(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.Add("foo", "bar"); + + Assert.True(raised); + } + + [Fact] + public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Add() + { + var target = new ResourceDictionary(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.MergedDictionaries.Add(new ResourceDictionary + { + { "foo", "bar" }, + }); + + Assert.True(raised); + } + + [Fact] + public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Add() + { + var target = new ResourceDictionary(); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.MergedDictionaries.Add(new ResourceDictionary()); + + Assert.False(raised); + } + + [Fact] + public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Remove() + { + var target = new ResourceDictionary + { + MergedDictionaries = + { + new ResourceDictionary { { "foo", "bar" } }, + } + }; + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.MergedDictionaries.RemoveAt(0); + + Assert.True(raised); + } + + [Fact] + public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Remove() + { + var target = new ResourceDictionary + { + MergedDictionaries = + { + new ResourceDictionary(), + } + }; + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.MergedDictionaries.RemoveAt(0); + + Assert.False(raised); + } + + [Fact] + public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Resource_Add() + { + var target = new ResourceDictionary + { + MergedDictionaries = + { + new ResourceDictionary(), + } + }; + + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + target.MergedDictionaries[0].Add("foo", "bar"); + + Assert.True(raised); + } + } +} From e81b22b9d244ca4c10aa2db7d1e0df7833eed8f2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 25 Aug 2017 00:55:06 +0200 Subject: [PATCH 30/75] IResourceProvider -> IResourceNode --- src/Avalonia.Controls/Application.cs | 8 ++++---- src/Avalonia.Controls/Control.cs | 6 +++--- src/Avalonia.Controls/IControl.cs | 2 +- .../Controls/{IResourceProvider.cs => IResourceNode.cs} | 4 ++-- .../Controls/ResourceProviderExtensions.cs | 6 +++--- src/Avalonia.Styling/Styling/ISetStyleParent.cs | 2 +- src/Avalonia.Styling/Styling/IStyle.cs | 2 +- src/Avalonia.Styling/Styling/Style.cs | 8 ++++---- src/Avalonia.Styling/Styling/Styles.cs | 6 +++--- .../MarkupExtensions/DynamicResourceExtension.cs | 8 ++++---- .../MarkupExtensions/StaticResourceExtension.cs | 4 ++-- src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs | 8 ++++---- tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs | 2 +- 13 files changed, 33 insertions(+), 33 deletions(-) rename src/Avalonia.Styling/Controls/{IResourceProvider.cs => IResourceNode.cs} (92%) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index ce15c0b9e3..5102c9a952 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -29,7 +29,7 @@ namespace Avalonia /// method. /// - Tracks the lifetime of the application. /// - public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceProvider + public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode { /// /// The application-global data templates. @@ -126,10 +126,10 @@ namespace Avalonia IStyleHost IStyleHost.StylingParent => null; /// - bool IResourceProvider.HasResources => _resources?.Count > 0; + bool IResourceNode.HasResources => _resources?.Count > 0; /// - IResourceProvider IResourceProvider.ResourceParent => null; + IResourceNode IResourceNode.ResourceParent => null; /// /// Initializes the application by loading XAML etc. @@ -159,7 +159,7 @@ namespace Avalonia } /// - bool IResourceProvider.TryGetResource(string key, out object value) + bool IResourceNode.TryGetResource(string key, out object value) { value = null; return (_resources?.TryGetResource(key, out value) ?? false) || diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 889edd48f8..a05dccec58 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -390,10 +390,10 @@ namespace Avalonia.Controls IAvaloniaReadOnlyList ILogical.LogicalChildren => LogicalChildren; /// - bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources; + bool IResourceNode.HasResources => _resources?.Count > 0 || Styles.HasResources; /// - IResourceProvider IResourceProvider.ResourceParent => ((IStyleHost)this).StylingParent as IResourceProvider; + IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode; /// IAvaloniaReadOnlyList IStyleable.Classes => Classes; @@ -480,7 +480,7 @@ namespace Avalonia.Controls } /// - bool IResourceProvider.TryGetResource(string key, out object value) + bool IResourceNode.TryGetResource(string key, out object value) { value = null; return (_resources?.TryGetResource(key, out value) ?? false) || diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs index 37ad12cf91..a5730bf398 100644 --- a/src/Avalonia.Controls/IControl.cs +++ b/src/Avalonia.Controls/IControl.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// /// Interface for Avalonia controls. /// - public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IResourceProvider, IStyleable, IStyleHost + public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IResourceNode, IStyleable, IStyleHost { /// /// Occurs when the control has finished initialization. diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceNode.cs similarity index 92% rename from src/Avalonia.Styling/Controls/IResourceProvider.cs rename to src/Avalonia.Styling/Controls/IResourceNode.cs index e133427350..54cf39e3b5 100644 --- a/src/Avalonia.Styling/Controls/IResourceProvider.cs +++ b/src/Avalonia.Styling/Controls/IResourceNode.cs @@ -5,7 +5,7 @@ namespace Avalonia.Controls /// /// Defines an element that can be queried for resources. /// - public interface IResourceProvider + public interface IResourceNode { /// /// Raised when resources in the element are changed. @@ -20,7 +20,7 @@ namespace Avalonia.Controls /// /// Gets the parent resource provider, if any. /// - IResourceProvider ResourceParent { get; } + IResourceNode ResourceParent { get; } /// /// Tries to find a resource within the element. diff --git a/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs b/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs index 45e16438d0..c96e8ea7f3 100644 --- a/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs +++ b/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// The control. /// The resource key. /// The resource, or if not found. - public static object FindResource(this IResourceProvider control, string key) + public static object FindResource(this IResourceNode control, string key) { Contract.Requires(control != null); Contract.Requires(key != null); @@ -23,7 +23,7 @@ namespace Avalonia.Controls while (current != null) { - if (current is IResourceProvider host) + if (current is IResourceNode host) { if (host.TryGetResource(key, out var value)) { @@ -37,7 +37,7 @@ namespace Avalonia.Controls return AvaloniaProperty.UnsetValue; } - public static IObservable GetResourceObservable(this IResourceProvider target, string key) + public static IObservable GetResourceObservable(this IResourceNode target, string key) { return Observable.FromEventPattern( x => target.ResourcesChanged += x, diff --git a/src/Avalonia.Styling/Styling/ISetStyleParent.cs b/src/Avalonia.Styling/Styling/ISetStyleParent.cs index da5b34798e..9f5855b401 100644 --- a/src/Avalonia.Styling/Styling/ISetStyleParent.cs +++ b/src/Avalonia.Styling/Styling/ISetStyleParent.cs @@ -17,7 +17,7 @@ namespace Avalonia.Styling /// Sets the style parent. /// /// The parent. - void SetParent(IResourceProvider parent); + void SetParent(IResourceNode parent); /// /// Notifies the style that a change has been made to resources that apply to it. diff --git a/src/Avalonia.Styling/Styling/IStyle.cs b/src/Avalonia.Styling/Styling/IStyle.cs index aa8980ddde..5f12763825 100644 --- a/src/Avalonia.Styling/Styling/IStyle.cs +++ b/src/Avalonia.Styling/Styling/IStyle.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Defines the interface for styles. /// - public interface IStyle : IResourceProvider + public interface IStyle : IResourceNode { /// /// Attaches the style to a control if the style's selector matches. diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 54b53868da..a0d4c8c087 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -17,7 +17,7 @@ namespace Avalonia.Styling { private static Dictionary> _applied = new Dictionary>(); - private IResourceProvider _parent; + private IResourceNode _parent; private ResourceDictionary _resources; /// @@ -68,10 +68,10 @@ namespace Avalonia.Styling public IList Setters { get; set; } = new List(); /// - bool IResourceProvider.HasResources => _resources?.Count > 0; + bool IResourceNode.HasResources => _resources?.Count > 0; /// - IResourceProvider IResourceProvider.ResourceParent => _parent; + IResourceNode IResourceNode.ResourceParent => _parent; /// /// Attaches the style to a control if the style's selector matches. @@ -139,7 +139,7 @@ namespace Avalonia.Styling } /// - void ISetStyleParent.SetParent(IResourceProvider parent) + void ISetStyleParent.SetParent(IResourceNode parent) { if (_parent != null && parent != null) { diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index 43a542d460..767d5ce463 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -14,7 +14,7 @@ namespace Avalonia.Styling /// public class Styles : AvaloniaList, IStyle, ISetStyleParent { - private IResourceProvider _parent; + private IResourceNode _parent; private ResourceDictionary _resources; public Styles() @@ -78,7 +78,7 @@ namespace Avalonia.Styling } /// - IResourceProvider IResourceProvider.ResourceParent => _parent; + IResourceNode IResourceNode.ResourceParent => _parent; /// /// Attaches the style to a control if the style's selector matches. @@ -116,7 +116,7 @@ namespace Avalonia.Styling } /// - void ISetStyleParent.SetParent(IResourceProvider parent) + void ISetStyleParent.SetParent(IResourceNode parent) { if (_parent != null && parent != null) { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 7937be60aa..5e421b7e73 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { public class DynamicResourceExtension : MarkupExtension, IBinding { - private IResourceProvider _anchor; + private IResourceNode _anchor; public DynamicResourceExtension() { @@ -33,9 +33,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions var context = (ITypeDescriptorContext)serviceProvider; var provideTarget = context.GetService(); - if (!(provideTarget.TargetObject is IResourceProvider)) + if (!(provideTarget.TargetObject is IResourceNode)) { - _anchor = GetAnchor(context); + _anchor = GetAnchor(context); } return this; @@ -47,7 +47,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions object anchor, bool enableDataValidation) { - var control = target as IResourceProvider ?? _anchor; + var control = target as IResourceNode ?? _anchor; if (control != null) { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index e3cd7b062b..4764677ede 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -31,7 +31,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions var context = (ITypeDescriptorContext)serviceProvider; var schemaContext = context.GetService()?.SchemaContext; var ambientProvider = context.GetService(); - var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider)); + var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceNode)); var ambientValues = ambientProvider.GetAllAmbientValues(resourceProviderType); // Look upwards though the ambient context for IResourceProviders which might be able @@ -47,7 +47,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { // We override XamlType.CanAssignTo in BindingXamlType so the results we get back // from GetAllAmbientValues aren't necessarily of the correct type. - if (ambientValue is IResourceProvider resourceProvider) + if (ambientValue is IResourceNode resourceProvider) { if (resourceProvider is IControl control && control.StylingParent != null) { diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index a121cd8c1b..c1867da9c0 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -14,7 +14,7 @@ namespace Avalonia.Markup.Xaml.Styling { private Uri _baseUri; private IStyle _loaded; - private IResourceProvider _parent; + private IResourceNode _parent; /// /// Initializes a new instance of the class. @@ -52,10 +52,10 @@ namespace Avalonia.Markup.Xaml.Styling } /// - bool IResourceProvider.HasResources => Loaded.HasResources; + bool IResourceNode.HasResources => Loaded.HasResources; /// - IResourceProvider IResourceProvider.ResourceParent => _parent; + IResourceNode IResourceNode.ResourceParent => _parent; /// public void Attach(IStyleable control, IStyleHost container) @@ -76,7 +76,7 @@ namespace Avalonia.Markup.Xaml.Styling } /// - void ISetStyleParent.SetParent(IResourceProvider parent) + void ISetStyleParent.SetParent(IResourceNode parent) { if (_parent != null && parent != null) { diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index 8bf6102f27..98aaa29261 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -162,7 +162,7 @@ namespace Avalonia.Layout.UnitTests private void RegisterServices() { var globalStyles = new Mock(); - var globalStylesResources = globalStyles.As(); + var globalStylesResources = globalStyles.As(); var outObj = (object)10; globalStylesResources.Setup(x => x.TryGetResource("FontSizeNormal", out outObj)).Returns(true); From 0a6216a35226afb3c8884346220f41b1867f3ba2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 25 Aug 2017 01:15:12 +0200 Subject: [PATCH 31/75] Doc comments. --- src/Avalonia.Styling/Controls/IResourceNode.cs | 4 ++-- src/Avalonia.Styling/Controls/ResourceDictionary.cs | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Styling/Controls/IResourceNode.cs b/src/Avalonia.Styling/Controls/IResourceNode.cs index 54cf39e3b5..6ff5fb8d2f 100644 --- a/src/Avalonia.Styling/Controls/IResourceNode.cs +++ b/src/Avalonia.Styling/Controls/IResourceNode.cs @@ -13,12 +13,12 @@ namespace Avalonia.Controls event EventHandler ResourcesChanged; /// - /// Gets a value indicating whether the provider has resources. + /// Gets a value indicating whether the node has resources. /// bool HasResources { get; } /// - /// Gets the parent resource provider, if any. + /// Gets the parent resource node, if any. /// IResourceNode ResourceParent { get; } diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index 975d27c08b..30fd8056f1 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -15,13 +15,18 @@ namespace Avalonia.Controls { private AvaloniaList _mergedDictionaries; - public event EventHandler ResourcesChanged; - + /// + /// Initializes a new instance of the class. + /// public ResourceDictionary() { CollectionChanged += OnCollectionChanged; } + /// + public event EventHandler ResourcesChanged; + + /// public IList MergedDictionaries { get From 91387a74e1ff158b67314c9273842147b958315b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 25 Aug 2017 02:00:39 +0200 Subject: [PATCH 32/75] Start testing MergedDictionaries. --- src/Avalonia.Controls/Application.cs | 27 ++++++++- src/Avalonia.Controls/Control.cs | 23 ++++++-- .../Properties/AssemblyInfo.cs | 2 + src/Avalonia.Styling/Styling/Style.cs | 27 ++++++--- src/Avalonia.Styling/Styling/Styles.cs | 32 ++++++---- .../StaticResourceExtensionTests.cs | 58 +++++++++++++++++++ 6 files changed, 141 insertions(+), 28 deletions(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 5102c9a952..8c5f9abb8d 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -39,7 +39,7 @@ namespace Avalonia private readonly Lazy _clipboard = new Lazy(() => (IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))); private readonly Styler _styler = new Styler(); - private ResourceDictionary _resources; + private IResourceDictionary _resources; /// /// Initializes a new instance of the class. @@ -107,7 +107,30 @@ namespace Avalonia /// /// Gets the application's global resource dictionary. /// - public IResourceDictionary Resources => _resources ?? (_resources = new ResourceDictionary()); + public IResourceDictionary Resources + { + get => _resources ?? (Resources = new ResourceDictionary()); + set + { + Contract.Requires(value != null); + + var hadResources = false; + + if (_resources != null) + { + hadResources = _resources.Count > 0; + _resources.ResourcesChanged -= ResourcesChanged; + } + + _resources = value; + _resources.ResourcesChanged += ResourcesChanged; + + if (hadResources || _resources.Count > 0) + { + ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + } + } + } /// /// Gets the application's global styles. diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index a05dccec58..bde9bb6760 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -97,7 +97,7 @@ namespace Avalonia.Controls private bool _isAttachedToLogicalTree; private IAvaloniaList _logicalChildren; private INameScope _nameScope; - private ResourceDictionary _resources; + private IResourceDictionary _resources; private Styles _styles; private bool _styled; private Subject _styleDetach = new Subject(); @@ -318,15 +318,26 @@ namespace Avalonia.Controls /// public IResourceDictionary Resources { - get + get => _resources ?? (Resources = new ResourceDictionary()); + set { - if (_resources == null) + Contract.Requires(value != null); + + var hadResources = false; + + if (_resources != null) { - _resources = new ResourceDictionary(); - _resources.ResourcesChanged += ThisResourcesChanged; + hadResources = _resources.Count > 0; + _resources.ResourcesChanged -= ThisResourcesChanged; } - return _resources; + _resources = value; + _resources.ResourcesChanged += ThisResourcesChanged; + + if (hadResources || _resources.Count > 0) + { + ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs()); + } } } diff --git a/src/Avalonia.Styling/Properties/AssemblyInfo.cs b/src/Avalonia.Styling/Properties/AssemblyInfo.cs index b53681aeed..0a639139f7 100644 --- a/src/Avalonia.Styling/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Styling/Properties/AssemblyInfo.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using Avalonia.Metadata; [assembly: AssemblyTitle("Avalonia.Styling")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] [assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests")] \ No newline at end of file diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index a0d4c8c087..6e79ee038e 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -18,7 +18,7 @@ namespace Avalonia.Styling private static Dictionary> _applied = new Dictionary>(); private IResourceNode _parent; - private ResourceDictionary _resources; + private IResourceDictionary _resources; /// /// Initializes a new instance of the class. @@ -44,15 +44,26 @@ namespace Avalonia.Styling /// public IResourceDictionary Resources { - get + get => _resources ?? (Resources = new ResourceDictionary()); + set { - if (_resources == null) + Contract.Requires(value != null); + + var hadResources = false; + + if (_resources != null) { - _resources = new ResourceDictionary(); - _resources.CollectionChanged += ResourceDictionaryChanged; + hadResources = _resources.Count > 0; + _resources.ResourcesChanged -= ResourceDictionaryChanged; } - return _resources; + _resources = value; + _resources.ResourcesChanged += ResourceDictionaryChanged; + + if (hadResources || _resources.Count > 0) + { + ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs()); + } } } @@ -180,9 +191,9 @@ namespace Avalonia.Styling _applied.Remove(control); } - private void ResourceDictionaryChanged(object sender, NotifyCollectionChangedEventArgs e) + private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e) { - ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + ResourcesChanged?.Invoke(this, e); } } } diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index 767d5ce463..714e7f6def 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Specialized; using System.Linq; using Avalonia.Collections; using Avalonia.Controls; @@ -15,7 +14,7 @@ namespace Avalonia.Styling public class Styles : AvaloniaList, IStyle, ISetStyleParent { private IResourceNode _parent; - private ResourceDictionary _resources; + private IResourceDictionary _resources; public Styles() { @@ -65,15 +64,26 @@ namespace Avalonia.Styling /// public IResourceDictionary Resources { - get + get => _resources ?? (Resources = new ResourceDictionary()); + set { - if (_resources == null) + Contract.Requires(value != null); + + var hadResources = false; + + if (_resources != null) { - _resources = new ResourceDictionary(); - _resources.CollectionChanged += ResourceDictionaryChanged; + hadResources = _resources.Count > 0; + _resources.ResourcesChanged -= ResourceDictionaryChanged; } - return _resources; + _resources = value; + _resources.ResourcesChanged += ResourceDictionaryChanged; + + if (hadResources || _resources.Count > 0) + { + ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs()); + } } } @@ -132,16 +142,14 @@ namespace Avalonia.Styling ResourcesChanged?.Invoke(this, e); } - private void ResourceDictionaryChanged(object sender, NotifyCollectionChangedEventArgs e) + private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e) { - var ev = new ResourcesChangedEventArgs(); - foreach (var child in this) { - (child as ISetStyleParent)?.NotifyResourcesChanged(ev); + (child as ISetStyleParent)?.NotifyResourcesChanged(e); } - ResourcesChanged?.Invoke(this, ev); + ResourcesChanged?.Invoke(this, e); } private void SubResourceChanged(object sender, ResourcesChangedEventArgs e) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index ed3ab3cc52..862ce2b3c0 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -104,6 +104,64 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void StaticResource_From_MergedDictionary_Can_Be_Assigned_To_Property() + { + var xaml = @" + + + + + + #ff506070 + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + + [Fact] + public void StaticResource_From_MergedDictionary_In_Style_Can_Be_Assigned_To_Property() + { + var xaml = @" + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + [Fact] public void StaticResource_From_Application_Can_Be_Assigned_To_Property_In_UserControl() { From 56c06be403c58d456f54131d7e86923f9374c9e8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 25 Aug 2017 08:30:57 +0200 Subject: [PATCH 33/75] DynamicResource merged dictionary tests. --- .../DynamicResourceExtensionTests.cs | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 7de60a1029..c751f9e056 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -76,6 +76,68 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + + [Fact] + public void DynamicResource_From_MergedDictionary_Can_Be_Assigned_To_Property() + { + var xaml = @" + + + + + + #ff506070 + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + + [Fact] + public void DynamicResource_From_MergedDictionary_In_Style_Can_Be_Assigned_To_Property() + { + var xaml = @" + + + + + "; @@ -399,6 +461,99 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } + [Fact] + public void DynamicResource_Tracks_Added_MergedResource() + { + var xaml = @" + + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + Assert.Null(border.Background); + + userControl.Resources.MergedDictionaries[0].Add("brush", new SolidColorBrush(0xff506070)); + + var brush = (SolidColorBrush)border.Background; + Assert.NotNull(brush); + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + + [Fact] + public void DynamicResource_Tracks_Added_MergedResource_Dictionary() + { + var xaml = @" + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + Assert.Null(border.Background); + + var dictionary = new ResourceDictionary + { + { "brush", new SolidColorBrush(0xff506070) }, + }; + + userControl.Resources.MergedDictionaries.Add(dictionary); + + var brush = (SolidColorBrush)border.Background; + Assert.NotNull(brush); + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + + [Fact] + public void DynamicResource_Tracks_Added_Style_MergedResource_Dictionary() + { + var xaml = @" + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + DelayedBinding.ApplyBindings(border); + + Assert.Null(border.Background); + + var dictionary = new ResourceDictionary + { + { "brush", new SolidColorBrush(0xff506070) }, + }; + + ((Style)userControl.Styles[0]).Resources.MergedDictionaries.Add(dictionary); + + var brush = (SolidColorBrush)border.Background; + Assert.NotNull(brush); + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + [Fact] public void DynamicResource_Can_Be_Found_Across_Xaml_Style_Files() { From 426cd8c9ddeee12685cea2e5e534b19f4e694dec Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Fri, 25 Aug 2017 12:55:36 +0300 Subject: [PATCH 34/75] ToolTip: IsOpen, Placement, Offset --- samples/ControlCatalog/Pages/ToolTipPage.xaml | 48 ++-- src/Avalonia.Controls/Primitives/Popup.cs | 26 +- src/Avalonia.Controls/ToolTip.cs | 236 ++++++++++++------ src/Avalonia.Controls/ToolTipService.cs | 98 ++++++++ 4 files changed, 300 insertions(+), 108 deletions(-) create mode 100644 src/Avalonia.Controls/ToolTipService.cs diff --git a/samples/ControlCatalog/Pages/ToolTipPage.xaml b/samples/ControlCatalog/Pages/ToolTipPage.xaml index 29df11510c..79114bc9de 100644 --- a/samples/ControlCatalog/Pages/ToolTipPage.xaml +++ b/samples/ControlCatalog/Pages/ToolTipPage.xaml @@ -1,22 +1,34 @@ - - ToolTip - A control which pops up a hint when a control is hovered + + ToolTip + A control which pops up a hint when a control is hovered - - - - - ToolTip - A control which pops up a hint when a control is hovered - - - Hover Here - + + + + + + ToolTip + A control which pops up a hint when a control is hovered + + + Hover Here + + + And Here + + - \ No newline at end of file diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index daea187a69..5cd3b22fc9 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -277,7 +277,7 @@ namespace Avalonia.Controls.Primitives { base.OnDetachedFromLogicalTree(e); _topLevel = null; - + if (_popupRoot != null) { ((ISetLogicalParent)_popupRoot).SetParent(null); @@ -327,34 +327,40 @@ namespace Avalonia.Controls.Primitives /// /// The popup's position in screen coordinates. protected virtual Point GetPosition() + { + return GetPosition(PlacementTarget ?? this.GetVisualParent(), PlacementMode, PopupRoot, + HorizontalOffset, VerticalOffset); + } + + internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset) { var zero = default(Point); - var mode = PlacementMode; - var target = PlacementTarget ?? this.GetVisualParent(); + var mode = placement; if (target?.GetVisualRoot() == null) { mode = PlacementMode.Pointer; - } + } switch (mode) { case PlacementMode.Pointer: - if(PopupRoot != null) + if (popupRoot != null) { // Scales the Horizontal and Vertical offset to screen co-ordinates. - var screenOffset = new Point(HorizontalOffset * (PopupRoot as ILayoutRoot).LayoutScaling, VerticalOffset * (PopupRoot as ILayoutRoot).LayoutScaling); - return (((IInputRoot)PopupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset; + var screenOffset = new Point(horizontalOffset * (popupRoot as ILayoutRoot).LayoutScaling, + verticalOffset * (popupRoot as ILayoutRoot).LayoutScaling); + return (((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset; } return default(Point); case PlacementMode.Bottom: - - return target?.PointToScreen(new Point(0 + HorizontalOffset, target.Bounds.Height + VerticalOffset)) ?? zero; + return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ?? + zero; case PlacementMode.Right: - return target?.PointToScreen(new Point(target.Bounds.Width + HorizontalOffset, 0 + VerticalOffset)) ?? zero; + return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? zero; default: throw new InvalidOperationException("Invalid value for Popup.PlacementMode"); diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index e1b69637af..e45f30f818 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -3,11 +3,7 @@ using System; using System.Reactive.Linq; -using System.Reactive.Subjects; using Avalonia.Controls.Primitives; -using Avalonia.Input; -using Avalonia.Threading; -using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -29,29 +25,50 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterAttached("Tip"); /// - /// The popup window used to display the active tooltip. + /// Defines the ToolTip.IsOpen attached property. /// - private static PopupRoot s_popup; + public static readonly AttachedProperty IsOpenProperty = + AvaloniaProperty.RegisterAttached("IsOpen"); /// - /// The control that the currently visible tooltip is attached to. + /// Defines the ToolTip.Placement property. /// - private static Control s_current; + public static readonly AttachedProperty PlacementProperty = + AvaloniaProperty.RegisterAttached("Placement", defaultValue: PlacementMode.Pointer); /// - /// Observable fired when a tooltip should be displayed for a control. The output from this - /// observable is throttled and calls when the time - /// period expires. + /// Defines the ToolTip.HorizontalOffset property. /// - private static readonly Subject s_show = new Subject(); + public static readonly AttachedProperty HorizontalOffsetProperty = + AvaloniaProperty.RegisterAttached("HorizontalOffset"); + + /// + /// Defines the ToolTip.VerticalOffset property. + /// + public static readonly AttachedProperty VerticalOffsetProperty = + AvaloniaProperty.RegisterAttached("VerticalOffset", 20); + + /// + /// Defines the ToolTip.ShowDelay property. + /// + public static readonly AttachedProperty ShowDelayProperty = + AvaloniaProperty.RegisterAttached("ShowDelay", 400); + + /// + /// Stores the curernt instance in the control. + /// + private static readonly AttachedProperty ToolTipProperty = + AvaloniaProperty.RegisterAttached("ToolTip"); + + private PopupRoot _popup; /// /// Initializes static members of the class. /// static ToolTip() { - TipProperty.Changed.Subscribe(TipChanged); - s_show.Throttle(TimeSpan.FromSeconds(0.5), AvaloniaScheduler.Instance).Subscribe(ShowToolTip); + TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged); + IsOpenProperty.Changed.Subscribe(IsOpenChanged); } /// @@ -77,101 +94,160 @@ namespace Avalonia.Controls } /// - /// called when the property changes on a control. + /// Gets the value of the ToolTip.IsOpen attached property. /// - /// The event args. - private static void TipChanged(AvaloniaPropertyChangedEventArgs e) + /// The control to get the property from. + /// + /// A value indicating whether the tool tip is visible. + /// + public static bool GetIsOpen(Control element) { - var control = (Control)e.Sender; + return element.GetValue(IsOpenProperty); + } - if (e.OldValue != null) - { - control.PointerEnter -= ControlPointerEnter; - control.PointerLeave -= ControlPointerLeave; - } + /// + /// Sets the value of the ToolTip.IsOpen attached property. + /// + /// The control to get the property from. + /// A value indicating whether the tool tip is visible. + public static void SetIsOpen(Control element, bool value) + { + element.SetValue(IsOpenProperty, value); + } - if (e.NewValue != null) - { - control.PointerEnter += ControlPointerEnter; - control.PointerLeave += ControlPointerLeave; - } + /// + /// Gets the value of the ToolTip.Placement attached property. + /// + /// The control to get the property from. + /// + /// A value indicating how the tool tip is positioned. + /// + public static PlacementMode GetPlacement(Control element) + { + return element.GetValue(PlacementProperty); } /// - /// Shows a tooltip for the specified control. + /// Sets the value of the ToolTip.Placement attached property. /// - /// The control. - private static void ShowToolTip(Control control) + /// The control to get the property from. + /// A value indicating how the tool tip is positioned. + public static void SetPlacement(Control element, PlacementMode value) { - if (control != null && control.IsVisible && control.GetVisualRoot() != null) - { - var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control); + element.SetValue(PlacementProperty, value); + } - if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value)) - { - var position = control.PointToScreen(cp.Value) + new Vector(0, 22); - - if (s_popup == null) - { - s_popup = new PopupRoot(); - s_popup.Content = new ToolTip(); - } - else - { - ((ISetLogicalParent)s_popup).SetParent(null); - } - - ((ISetLogicalParent)s_popup).SetParent(control); - ((ToolTip)s_popup.Content).Content = GetTip(control); - s_popup.Position = position; - s_popup.Show(); - - s_current = control; - } - } + /// + /// Gets the value of the ToolTip.HorizontalOffset attached property. + /// + /// The control to get the property from. + /// + /// A value indicating how the tool tip is positioned. + /// + public static double GetHorizontalOffset(Control element) + { + return element.GetValue(HorizontalOffsetProperty); + } + + /// + /// Sets the value of the ToolTip.HorizontalOffset attached property. + /// + /// The control to get the property from. + /// A value indicating how the tool tip is positioned. + public static void SetHorizontalOffset(Control element, double value) + { + element.SetValue(HorizontalOffsetProperty, value); + } + + /// + /// Gets the value of the ToolTip.VerticalOffset attached property. + /// + /// The control to get the property from. + /// + /// A value indicating how the tool tip is positioned. + /// + public static double GetVerticalOffset(Control element) + { + return element.GetValue(VerticalOffsetProperty); + } + + /// + /// Sets the value of the ToolTip.VerticalOffset attached property. + /// + /// The control to get the property from. + /// A value indicating how the tool tip is positioned. + public static void SetVerticalOffset(Control element, double value) + { + element.SetValue(VerticalOffsetProperty, value); } /// - /// Called when the pointer enters a control with an attached tooltip. + /// Gets the value of the ToolTip.ShowDelay attached property. /// - /// The event sender. - /// The event args. - private static void ControlPointerEnter(object sender, PointerEventArgs e) + /// The control to get the property from. + /// + /// A value indicating the time, in milliseconds, before a tool tip opens. + /// + public static int GetShowDelay(Control element) { - s_current = (Control)sender; - s_show.OnNext(s_current); + return element.GetValue(ShowDelayProperty); } /// - /// Called when the pointer leaves a control with an attached tooltip. + /// Sets the value of the ToolTip.ShowDelay attached property. /// - /// The event sender. - /// The event args. - private static void ControlPointerLeave(object sender, PointerEventArgs e) + /// The control to get the property from. + /// A value indicating the time, in milliseconds, before a tool tip opens. + public static void SetShowDelay(Control element, int value) { - var control = (Control)sender; + element.SetValue(ShowDelayProperty, value); + } + + private static void IsOpenChanged(AvaloniaPropertyChangedEventArgs e) + { + var control = (Control)e.Sender; - if (control == s_current) + if ((bool)e.NewValue) { - if (s_popup != null) + var tip = GetTip(control); + if (tip == null) return; + + var toolTip = control.GetValue(ToolTipProperty); + if (toolTip == null || (tip != toolTip && tip != toolTip.Content)) { - DisposeTooltip(); - s_show.OnNext(null); + toolTip?.Close(); + + toolTip = tip as ToolTip ?? new ToolTip { Content = tip }; + control.SetValue(ToolTipProperty, toolTip); } + + toolTip.Open(control); + } + else + { + var toolTip = control.GetValue(ToolTipProperty); + toolTip?.Close(); } } - private static void DisposeTooltip() + private void Open(Control control) { - if (s_popup != null) - { - // Clear the ToolTip's Content in case it has control content: this will - // reset its visual parent allowing it to be used again. - ((ToolTip)s_popup.Content).Content = null; + Close(); + + _popup = new PopupRoot { Content = this }; + ((ISetLogicalParent)_popup).SetParent(control); + _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup, + GetHorizontalOffset(control), GetVerticalOffset(control)); + _popup.Show(); + } - // Dispose of the popup. - s_popup.Dispose(); - s_popup = null; + private void Close() + { + if (_popup != null) + { + _popup.Content = null; + _popup.Hide(); + _popup = null; } } } diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs new file mode 100644 index 0000000000..bfd7ef0f33 --- /dev/null +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -0,0 +1,98 @@ +using System; +using Avalonia.Input; +using Avalonia.Threading; + +namespace Avalonia.Controls +{ + /// + /// Handeles interaction with controls. + /// + internal sealed class ToolTipService + { + public static ToolTipService Instance { get; } = new ToolTipService(); + + private DispatcherTimer _timer; + + private ToolTipService() { } + + /// + /// called when the property changes on a control. + /// + /// The event args. + internal void TipChanged(AvaloniaPropertyChangedEventArgs e) + { + var control = (Control)e.Sender; + + if (e.OldValue != null) + { + control.PointerEnter -= ControlPointerEnter; + control.PointerLeave -= ControlPointerLeave; + } + + if (e.NewValue != null) + { + control.PointerEnter += ControlPointerEnter; + control.PointerLeave += ControlPointerLeave; + } + } + + /// + /// Called when the pointer enters a control with an attached tooltip. + /// + /// The event sender. + /// The event args. + private void ControlPointerEnter(object sender, PointerEventArgs e) + { + StopTimer(); + + var control = (Control)sender; + var showDelay = ToolTip.GetShowDelay(control); + if (showDelay == 0) + { + Open(control); + } + else + { + StartShowTimer(showDelay, control); + } + } + + /// + /// Called when the pointer leaves a control with an attached tooltip. + /// + /// The event sender. + /// The event args. + private void ControlPointerLeave(object sender, PointerEventArgs e) + { + var control = (Control)sender; + Close(control); + } + + private void StartShowTimer(int showDelay, Control control) + { + _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(showDelay) }; + _timer.Tick += (o, e) => Open(control); + _timer.Start(); + } + + private void Open(Control control) + { + StopTimer(); + + ToolTip.SetIsOpen(control, true); + } + + private void Close(Control control) + { + StopTimer(); + + ToolTip.SetIsOpen(control, false); + } + + private void StopTimer() + { + _timer?.Stop(); + _timer = null; + } + } +} \ No newline at end of file From a72ce3671d6b6917186813e5cec5d136c1d3f7ef Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Aug 2017 19:01:21 +0200 Subject: [PATCH 35/75] Added ResourceInclude. --- src/Avalonia.Controls/Application.cs | 4 +- src/Avalonia.Controls/Control.cs | 4 +- .../Controls/IResourceDictionary.cs | 21 +------ .../Controls/IResourceNode.cs | 26 +------- .../Controls/IResourceProvider.cs | 32 ++++++++++ .../Controls/ResourceDictionary.cs | 17 +++-- src/Avalonia.Styling/Styling/Style.cs | 4 +- .../Avalonia.Markup.Xaml.csproj | 1 + .../Data/ResourceInclude.cs | 63 +++++++++++++++++++ .../Styling/StyleInclude.cs | 2 +- .../Data/ResourceIncludeTests.cs | 55 ++++++++++++++++ .../DynamicResourceExtensionTests.cs | 2 +- .../ResourceDictionaryTests.cs | 2 +- 13 files changed, 176 insertions(+), 57 deletions(-) create mode 100644 src/Avalonia.Styling/Controls/IResourceProvider.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 8c5f9abb8d..725163bba8 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -149,7 +149,7 @@ namespace Avalonia IStyleHost IStyleHost.StylingParent => null; /// - bool IResourceNode.HasResources => _resources?.Count > 0; + bool IResourceProvider.HasResources => _resources?.Count > 0; /// IResourceNode IResourceNode.ResourceParent => null; @@ -182,7 +182,7 @@ namespace Avalonia } /// - bool IResourceNode.TryGetResource(string key, out object value) + bool IResourceProvider.TryGetResource(string key, out object value) { value = null; return (_resources?.TryGetResource(key, out value) ?? false) || diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index bde9bb6760..b6283ab7ee 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -401,7 +401,7 @@ namespace Avalonia.Controls IAvaloniaReadOnlyList ILogical.LogicalChildren => LogicalChildren; /// - bool IResourceNode.HasResources => _resources?.Count > 0 || Styles.HasResources; + bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources; /// IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode; @@ -491,7 +491,7 @@ namespace Avalonia.Controls } /// - bool IResourceNode.TryGetResource(string key, out object value) + bool IResourceProvider.TryGetResource(string key, out object value) { value = null; return (_resources?.TryGetResource(key, out value) ?? false) || diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Styling/Controls/IResourceDictionary.cs index 0ddeda0f3d..e6da375544 100644 --- a/src/Avalonia.Styling/Controls/IResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/IResourceDictionary.cs @@ -9,28 +9,11 @@ namespace Avalonia.Controls /// /// An indexed dictionary of resources. /// - public interface IResourceDictionary : IDictionary + public interface IResourceDictionary : IResourceProvider, IDictionary { - /// - /// Raised when resources in the dictionary are changed. - /// - event EventHandler ResourcesChanged; - /// /// Gets a collection of child resource dictionaries. /// - IList MergedDictionaries { get; } - - /// - /// Tries to find a resource within the dictionary. - /// - /// The resource key. - /// - /// When this method returns, contains the value associated with the specified key, - /// if the key is found; otherwise, null - /// - /// True if the resource if found, otherwise false. - /// - bool TryGetResource(string key, out object value); + IList MergedDictionaries { get; } } } diff --git a/src/Avalonia.Styling/Controls/IResourceNode.cs b/src/Avalonia.Styling/Controls/IResourceNode.cs index 6ff5fb8d2f..b6a6cdc3e3 100644 --- a/src/Avalonia.Styling/Controls/IResourceNode.cs +++ b/src/Avalonia.Styling/Controls/IResourceNode.cs @@ -3,35 +3,13 @@ namespace Avalonia.Controls { /// - /// Defines an element that can be queried for resources. + /// Represents resource provider in a tree. /// - public interface IResourceNode + public interface IResourceNode : IResourceProvider { - /// - /// Raised when resources in the element are changed. - /// - event EventHandler ResourcesChanged; - - /// - /// Gets a value indicating whether the node has resources. - /// - bool HasResources { get; } - /// /// Gets the parent resource node, if any. /// IResourceNode ResourceParent { get; } - - /// - /// Tries to find a resource within the element. - /// - /// The resource key. - /// - /// When this method returns, contains the value associated with the specified key, - /// if the key is found; otherwise, null - /// - /// True if the resource if found, otherwise false. - /// - bool TryGetResource(string key, out object value); } } diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceProvider.cs new file mode 100644 index 0000000000..2ca83ea2d2 --- /dev/null +++ b/src/Avalonia.Styling/Controls/IResourceProvider.cs @@ -0,0 +1,32 @@ +using System; + +namespace Avalonia.Controls +{ + /// + /// Represents an object that can be queried for resources. + /// + public interface IResourceProvider + { + /// + /// Raised when resources in the provider are changed. + /// + event EventHandler ResourcesChanged; + + /// + /// Gets a value indicating whether the element has resources. + /// + bool HasResources { get; } + + /// + /// Tries to find a resource within the provider. + /// + /// The resource key. + /// + /// When this method returns, contains the value associated with the specified key, + /// if the key is found; otherwise, null + /// + /// True if the resource if found, otherwise false. + /// + bool TryGetResource(string key, out object value); + } +} diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index 30fd8056f1..ec0a59dad9 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; using Avalonia.Collections; namespace Avalonia.Controls @@ -13,7 +14,7 @@ namespace Avalonia.Controls /// public class ResourceDictionary : AvaloniaDictionary, IResourceDictionary { - private AvaloniaList _mergedDictionaries; + private AvaloniaList _mergedDictionaries; /// /// Initializes a new instance of the class. @@ -27,18 +28,18 @@ namespace Avalonia.Controls public event EventHandler ResourcesChanged; /// - public IList MergedDictionaries + public IList MergedDictionaries { get { if (_mergedDictionaries == null) { - _mergedDictionaries = new AvaloniaList(); + _mergedDictionaries = new AvaloniaList(); _mergedDictionaries.ResetBehavior = ResetBehavior.Remove; _mergedDictionaries.ForEachItem( x => { - if (x.Count > 0) + if (x.HasResources) { OnResourcesChanged(); } @@ -47,7 +48,7 @@ namespace Avalonia.Controls }, x => { - if (x.Count > 0) + if (x.HasResources) { OnResourcesChanged(); } @@ -61,6 +62,12 @@ namespace Avalonia.Controls } } + /// + bool IResourceProvider.HasResources + { + get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false); + } + /// public bool TryGetResource(string key, out object value) { diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 6e79ee038e..4182ffada3 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -79,10 +79,10 @@ namespace Avalonia.Styling public IList Setters { get; set; } = new List(); /// - bool IResourceNode.HasResources => _resources?.Count > 0; + IResourceNode IResourceNode.ResourceParent => _parent; /// - IResourceNode IResourceNode.ResourceParent => _parent; + bool IResourceProvider.HasResources => _resources?.Count > 0; /// /// Attaches the style to a control if the style's selector matches. diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 5c2d228e62..f04f148c5b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -34,6 +34,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs new file mode 100644 index 0000000000..035765fae0 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs @@ -0,0 +1,63 @@ +using System; +using System.ComponentModel; +using Avalonia.Controls; +using Portable.Xaml.ComponentModel; +using Portable.Xaml.Markup; + +namespace Avalonia.Markup.Xaml.Data +{ + /// + /// Loads a resource dictionary from a specified URL. + /// + public class ResourceInclude : MarkupExtension, IResourceProvider + { + private Uri _baseUri; + private IResourceDictionary _loaded; + + public event EventHandler ResourcesChanged; + + /// + /// Gets the loaded resource dictionary. + /// + public IResourceDictionary Loaded + { + get + { + if (_loaded == null) + { + var loader = new AvaloniaXamlLoader(); + _loaded = (IResourceDictionary)loader.Load(Source, _baseUri); + + if (_loaded.HasResources) + { + ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); + } + } + + return _loaded; + } + } + + /// + /// Gets or sets the source URL. + /// + public Uri Source { get; set; } + + /// + bool IResourceProvider.HasResources => Loaded.HasResources; + + /// + bool IResourceProvider.TryGetResource(string key, out object value) + { + return Loaded.TryGetResource(key, out value); + } + + /// + public override object ProvideValue(IServiceProvider serviceProvider) + { + var tdc = (ITypeDescriptorContext)serviceProvider; + _baseUri = tdc?.GetBaseUri(); + return this; + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index c1867da9c0..fb308e62b8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -52,7 +52,7 @@ namespace Avalonia.Markup.Xaml.Styling } /// - bool IResourceNode.HasResources => Loaded.HasResources; + bool IResourceProvider.HasResources => Loaded.HasResources; /// IResourceNode IResourceNode.ResourceParent => _parent; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs new file mode 100644 index 0000000000..6dc56e425c --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs @@ -0,0 +1,55 @@ +using System; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Data +{ + public class ResourceIncludeTests + { + public class StaticResourceExtensionTests + { + [Fact] + public void ResourceInclude_Loads_ResourceDictionary() + { + var includeXaml = @" + + #ff506070 + +"; + using (StartWithResources(("test:include.xaml", includeXaml))) + { + var xaml = @" + + + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + + var brush = (SolidColorBrush)border.Background; + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + } + + private IDisposable StartWithResources(params (string, string)[] assets) + { + var assetLoader = new MockAssetLoader(assets); + var services = new TestServices(assetLoader: assetLoader); + return UnitTestApplication.Start(services); + } + } + } +} \ No newline at end of file diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index c751f9e056..d55e34cbe6 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -485,7 +485,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Null(border.Background); - userControl.Resources.MergedDictionaries[0].Add("brush", new SolidColorBrush(0xff506070)); + ((IResourceDictionary)userControl.Resources.MergedDictionaries[0]).Add("brush", new SolidColorBrush(0xff506070)); var brush = (SolidColorBrush)border.Background; Assert.NotNull(brush); diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs index f31cbbac0a..1eb3cd9750 100644 --- a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs +++ b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs @@ -167,7 +167,7 @@ namespace Avalonia.Styling.UnitTests var raised = false; target.ResourcesChanged += (_, __) => raised = true; - target.MergedDictionaries[0].Add("foo", "bar"); + ((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar"); Assert.True(raised); } From e54f48b63ce7335472eb3626174991c37c0272f9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Aug 2017 19:14:32 +0200 Subject: [PATCH 36/75] React to application resources changing. --- src/Avalonia.Controls/TopLevel.cs | 24 +++++++++++++++++-- .../TopLevelTests.cs | 17 +++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f8db0e2a5b..1af347ab4e 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -10,9 +10,11 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.Logging; +using Avalonia.LogicalTree; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; +using Avalonia.Utilities; using Avalonia.VisualTree; using JetBrains.Annotations; @@ -26,7 +28,13 @@ namespace Avalonia.Controls /// It handles scheduling layout, styling and rendering as well as /// tracking the widget's . /// - public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot + public abstract class TopLevel : ContentControl, + IInputRoot, + ILayoutRoot, + IRenderRoot, + ICloseable, + IStyleRoot, + IWeakSubscriber { /// /// Defines the property. @@ -100,7 +108,6 @@ namespace Avalonia.Controls impl.Resized = HandleResized; impl.ScalingChanged = HandleScalingChanged; - _keyboardNavigationHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this); styler?.ApplyStyles(this); @@ -116,6 +123,14 @@ namespace Avalonia.Controls { _applicationLifecycle.OnExit += OnApplicationExiting; } + + if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources) + { + WeakSubscriptionManager.Subscribe( + applicationResources, + nameof(IResourceProvider.ResourcesChanged), + this); + } } /// @@ -165,6 +180,11 @@ namespace Avalonia.Controls /// IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; + void IWeakSubscriber.OnEvent(object sender, ResourcesChangedEventArgs e) + { + ((ILogical)this).NotifyResourcesChanged(e); + } + /// /// Gets or sets a value indicating whether access keys are shown in the window. /// diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index da30336be6..da0719893f 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -219,6 +219,23 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Adding_Resource_To_Application_Should_Raise_ResourcesChanged() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var impl = new Mock(); + impl.SetupAllProperties(); + var target = new TestTopLevel(impl.Object); + var raised = false; + + target.ResourcesChanged += (_, __) => raised = true; + Application.Current.Resources.Add("foo", "bar"); + + Assert.True(raised); + } + } + private FuncControlTemplate CreateTemplate() { return new FuncControlTemplate(x => From c47e44192bf9626f8ef3ab597ee388dd2e19638c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Aug 2017 19:56:24 +0200 Subject: [PATCH 37/75] Make resource dictionary keys objects. For parity with other XAML frameworks. --- src/Avalonia.Styling/Controls/IResourceDictionary.cs | 2 +- src/Avalonia.Styling/Controls/ResourceDictionary.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Styling/Controls/IResourceDictionary.cs index e6da375544..d7c86b7d74 100644 --- a/src/Avalonia.Styling/Controls/IResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/IResourceDictionary.cs @@ -9,7 +9,7 @@ namespace Avalonia.Controls /// /// An indexed dictionary of resources. /// - public interface IResourceDictionary : IResourceProvider, IDictionary + public interface IResourceDictionary : IResourceProvider, IDictionary { /// /// Gets a collection of child resource dictionaries. diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index ec0a59dad9..74a861b36b 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -12,7 +12,7 @@ namespace Avalonia.Controls /// /// An indexed dictionary of resources. /// - public class ResourceDictionary : AvaloniaDictionary, IResourceDictionary + public class ResourceDictionary : AvaloniaDictionary, IResourceDictionary { private AvaloniaList _mergedDictionaries; From c244ac809dd423290affdde201bd57cf1431b021 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Aug 2017 21:13:47 +0200 Subject: [PATCH 38/75] Fixed malformed doc comment. --- src/Avalonia.Styling/Controls/IResourceProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceProvider.cs index 2ca83ea2d2..eec783623c 100644 --- a/src/Avalonia.Styling/Controls/IResourceProvider.cs +++ b/src/Avalonia.Styling/Controls/IResourceProvider.cs @@ -23,7 +23,8 @@ namespace Avalonia.Controls /// The resource key. /// /// When this method returns, contains the value associated with the specified key, - /// if the key is found; otherwise, null + /// if the key is found; otherwise, null. + /// /// /// True if the resource if found, otherwise false. /// From fe957d8093276e45c46a34dcd616fda5e91fe525 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Aug 2017 23:27:34 +0200 Subject: [PATCH 39/75] Fix compile error. --- src/Avalonia.Base/Collections/AvaloniaDictionary.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index 9f2a271092..724442c5f2 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -62,8 +62,6 @@ namespace Avalonia.Collections public object SyncRoot => ((IDictionary)_inner).SyncRoot; - public object this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; } - /// /// Gets or sets the named resource. /// @@ -102,6 +100,8 @@ namespace Avalonia.Collections } } + object IDictionary.this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; } + /// public void Add(TKey key, TValue value) { From dff3031e201ae5acba9494f0403cbbc0b9ffce3f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 5 Aug 2017 18:40:15 -0700 Subject: [PATCH 40/75] Upgrade ReactiveUI to the v8 alpha nuget package so we don't need to maintain our own fork. Avalonia.ReactiveUI only registers the MainScheduler, and it does so via a call to UseReactiveUI on AppBuilder. Update Rx.Net dependency to match reactiveui's dependency on Rx. Removed ReactiveUI dependeny in BindingTests. Updated BindingTest sample to use the new reactiveui APIs. Update samples to use current ReactiveUI APIs. Fixed bug I introduced into tests. Remove RxUI submodule info from .gitmodules --- .gitmodules | 4 -- Avalonia.sln | 3 +- build/ReactiveUI.props | 5 ++ build/Rx.props | 14 +++--- samples/BindingTest/App.xaml.cs | 1 + samples/BindingTest/BindingTest.csproj | 2 +- .../ViewModels/MainWindowViewModel.cs | 10 ++-- samples/RenderTest/Program.cs | 1 + samples/RenderTest/RenderTest.csproj | 1 + .../ViewModels/MainWindowViewModel.cs | 10 ++-- samples/VirtualizationTest/Program.cs | 1 + .../ViewModels/MainWindowViewModel.cs | 25 ++++------ .../VirtualizationTest.csproj | 1 + .../AppBuilderExtensions.cs | 24 +++++++++ .../Avalonia.ReactiveUI.csproj | 50 +------------------ .../Properties/AssemblyInfo.cs | 3 -- src/Avalonia.ReactiveUI/Registrations.cs | 23 --------- src/Avalonia.ReactiveUI/Shims.cs | 34 ------------- src/Avalonia.ReactiveUI/src | 1 - .../Avalonia.Markup.Xaml.UnitTests.csproj | 1 - .../Data/BindingTests.cs | 18 +++++-- .../Data/BindingTests_Source.cs | 20 ++++++-- 22 files changed, 95 insertions(+), 157 deletions(-) create mode 100644 build/ReactiveUI.props create mode 100644 src/Avalonia.ReactiveUI/AppBuilderExtensions.cs delete mode 100644 src/Avalonia.ReactiveUI/Registrations.cs delete mode 100644 src/Avalonia.ReactiveUI/Shims.cs delete mode 160000 src/Avalonia.ReactiveUI/src diff --git a/.gitmodules b/.gitmodules index f446fe421e..98b6d076c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "src/Avalonia.ReactiveUI/src"] - path = src/Avalonia.ReactiveUI/src - url = https://github.com/AvaloniaUI/ReactiveUI.git - branch = avalonia-snapshot [submodule "src/Avalonia.HtmlRenderer/external"] path = src/Avalonia.HtmlRenderer/external url = https://github.com/AvaloniaUI/HTML-Renderer.git diff --git a/Avalonia.sln b/Avalonia.sln index 391d02382e..39d44cb172 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject @@ -159,6 +159,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props build\Moq.props = build\Moq.props build\NetCore.props = build\NetCore.props + build\ReactiveUI.props = build\ReactiveUI.props build\Rx.props = build\Rx.props build\Serilog.props = build\Serilog.props build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.props diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props new file mode 100644 index 0000000000..4473447647 --- /dev/null +++ b/build/ReactiveUI.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Rx.props b/build/Rx.props index 5d74a6b214..e63055da3e 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -1,11 +1,11 @@  - - - - - - - + + + + + + + diff --git a/samples/BindingTest/App.xaml.cs b/samples/BindingTest/App.xaml.cs index 7bcaf837a8..42e5716541 100644 --- a/samples/BindingTest/App.xaml.cs +++ b/samples/BindingTest/App.xaml.cs @@ -20,6 +20,7 @@ namespace BindingTest AppBuilder.Configure() .UsePlatformDetect() + .UseReactiveUI() .Start(); } diff --git a/samples/BindingTest/BindingTest.csproj b/samples/BindingTest/BindingTest.csproj index eab656eb33..b4dd17194b 100644 --- a/samples/BindingTest/BindingTest.csproj +++ b/samples/BindingTest/BindingTest.csproj @@ -45,7 +45,6 @@ - @@ -163,4 +162,5 @@ + \ No newline at end of file diff --git a/samples/BindingTest/ViewModels/MainWindowViewModel.cs b/samples/BindingTest/ViewModels/MainWindowViewModel.cs index d8ea280abf..1116810ccb 100644 --- a/samples/BindingTest/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingTest/ViewModels/MainWindowViewModel.cs @@ -28,15 +28,13 @@ namespace BindingTest.ViewModels SelectedItems = new ObservableCollection(); - ShuffleItems = ReactiveCommand.Create(); - ShuffleItems.Subscribe(_ => + ShuffleItems = ReactiveCommand.Create(() => { var r = new Random(); Items.Move(r.Next(Items.Count), 1); }); - StringValueCommand = ReactiveCommand.Create(); - StringValueCommand.Subscribe(param => + StringValueCommand = ReactiveCommand.Create(param => { BooleanFlag = !BooleanFlag; StringValue = param.ToString(); @@ -58,7 +56,7 @@ namespace BindingTest.ViewModels public ObservableCollection Items { get; } public ObservableCollection SelectedItems { get; } - public ReactiveCommand ShuffleItems { get; } + public ReactiveCommand ShuffleItems { get; } public string BooleanString { @@ -91,7 +89,7 @@ namespace BindingTest.ViewModels } public IObservable CurrentTimeObservable { get; } - public ReactiveCommand StringValueCommand { get; } + public ReactiveCommand StringValueCommand { get; } public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel(); diff --git a/samples/RenderTest/Program.cs b/samples/RenderTest/Program.cs index 7a23e09dd4..514771097f 100644 --- a/samples/RenderTest/Program.cs +++ b/samples/RenderTest/Program.cs @@ -18,6 +18,7 @@ namespace RenderTest // again. AppBuilder.Configure() .UsePlatformDetect() + .UseReactiveUI() .Start(); } diff --git a/samples/RenderTest/RenderTest.csproj b/samples/RenderTest/RenderTest.csproj index b7e64f4dae..974b8802ac 100644 --- a/samples/RenderTest/RenderTest.csproj +++ b/samples/RenderTest/RenderTest.csproj @@ -191,4 +191,5 @@ + \ No newline at end of file diff --git a/samples/RenderTest/ViewModels/MainWindowViewModel.cs b/samples/RenderTest/ViewModels/MainWindowViewModel.cs index bd68752f66..b2fa2e8b7a 100644 --- a/samples/RenderTest/ViewModels/MainWindowViewModel.cs +++ b/samples/RenderTest/ViewModels/MainWindowViewModel.cs @@ -10,10 +10,8 @@ namespace RenderTest.ViewModels public MainWindowViewModel() { - ToggleDrawDirtyRects = ReactiveCommand.Create(); - ToggleDrawDirtyRects.Subscribe(_ => DrawDirtyRects = !DrawDirtyRects); - ToggleDrawFps = ReactiveCommand.Create(); - ToggleDrawFps.Subscribe(_ => DrawFps = !DrawFps); + ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects); + ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps); } public bool DrawDirtyRects @@ -28,7 +26,7 @@ namespace RenderTest.ViewModels set { this.RaiseAndSetIfChanged(ref drawFps, value); } } - public ReactiveCommand ToggleDrawDirtyRects { get; } - public ReactiveCommand ToggleDrawFps { get; } + public ReactiveCommand ToggleDrawDirtyRects { get; } + public ReactiveCommand ToggleDrawFps { get; } } } diff --git a/samples/VirtualizationTest/Program.cs b/samples/VirtualizationTest/Program.cs index d5649f6a06..e9156d803f 100644 --- a/samples/VirtualizationTest/Program.cs +++ b/samples/VirtualizationTest/Program.cs @@ -17,6 +17,7 @@ namespace VirtualizationTest AppBuilder.Configure() .UsePlatformDetect() + .UseReactiveUI() .Start(); } diff --git a/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs index 86869eb46a..a0e1570a36 100644 --- a/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs +++ b/samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs @@ -23,20 +23,15 @@ namespace VirtualizationTest.ViewModels public MainWindowViewModel() { this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems); - RecreateCommand = ReactiveCommand.Create(); - RecreateCommand.Subscribe(_ => Recreate()); + RecreateCommand = ReactiveCommand.Create(() => Recreate()); - AddItemCommand = ReactiveCommand.Create(); - AddItemCommand.Subscribe(_ => AddItem()); + AddItemCommand = ReactiveCommand.Create(() => AddItem()); - RemoveItemCommand = ReactiveCommand.Create(); - RemoveItemCommand.Subscribe(_ => Remove()); + RemoveItemCommand = ReactiveCommand.Create(() => Remove()); - SelectFirstCommand = ReactiveCommand.Create(); - SelectFirstCommand.Subscribe(_ => SelectItem(0)); + SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0)); - SelectLastCommand = ReactiveCommand.Create(); - SelectLastCommand.Subscribe(_ => SelectItem(Items.Count - 1)); + SelectLastCommand = ReactiveCommand.Create(() => SelectItem(Items.Count - 1)); } public string NewItemString @@ -78,11 +73,11 @@ namespace VirtualizationTest.ViewModels public IEnumerable VirtualizationModes => Enum.GetValues(typeof(ItemVirtualizationMode)).Cast(); - public ReactiveCommand AddItemCommand { get; private set; } - public ReactiveCommand RecreateCommand { get; private set; } - public ReactiveCommand RemoveItemCommand { get; private set; } - public ReactiveCommand SelectFirstCommand { get; private set; } - public ReactiveCommand SelectLastCommand { get; private set; } + public ReactiveCommand AddItemCommand { get; private set; } + public ReactiveCommand RecreateCommand { get; private set; } + public ReactiveCommand RemoveItemCommand { get; private set; } + public ReactiveCommand SelectFirstCommand { get; private set; } + public ReactiveCommand SelectLastCommand { get; private set; } private void ResizeItems(int count) { diff --git a/samples/VirtualizationTest/VirtualizationTest.csproj b/samples/VirtualizationTest/VirtualizationTest.csproj index 327e659966..9d31a6cb86 100644 --- a/samples/VirtualizationTest/VirtualizationTest.csproj +++ b/samples/VirtualizationTest/VirtualizationTest.csproj @@ -158,4 +158,5 @@ + \ No newline at end of file diff --git a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs new file mode 100644 index 0000000000..e081763e99 --- /dev/null +++ b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs @@ -0,0 +1,24 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia.Controls; +using Avalonia.Threading; +using ReactiveUI; +using System; +using System.Reactive.Concurrency; +using System.Threading; + +namespace Avalonia +{ + public static class AppBuilderExtensions + { + public static TAppBuilder UseReactiveUI(this TAppBuilder builder) + where TAppBuilder : AppBuilderBase, new() + { + return builder.AfterSetup(_ => + { + RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; + }); + } + } +} diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 22d815d786..d523f71416 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -1,7 +1,6 @@  netstandard1.3 - False false @@ -29,53 +28,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -88,5 +41,6 @@ + \ No newline at end of file diff --git a/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs b/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs index c8a5c5cc41..c9ead6f6e6 100644 --- a/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs +++ b/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs @@ -3,7 +3,4 @@ using System.Reflection; -// 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("Avalonia.ReactiveUI")] diff --git a/src/Avalonia.ReactiveUI/Registrations.cs b/src/Avalonia.ReactiveUI/Registrations.cs deleted file mode 100644 index 066aa46dc1..0000000000 --- a/src/Avalonia.ReactiveUI/Registrations.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) The Avalonia 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.Reactive.Concurrency; -using System.Threading; - - -namespace ReactiveUI -{ - /// - /// Ignore me. This class is a secret handshake between RxUI and RxUI.Xaml - /// in order to register certain classes on startup that would be difficult - /// to register otherwise. - /// - public class PlatformRegistrations : IWantsToRegisterStuff - { - public void Register(Action, Type> registerFunction) - { - RxApp.MainThreadScheduler = new SynchronizationContextScheduler(SynchronizationContext.Current); - } - } -} diff --git a/src/Avalonia.ReactiveUI/Shims.cs b/src/Avalonia.ReactiveUI/Shims.cs deleted file mode 100644 index 2729a68d6f..0000000000 --- a/src/Avalonia.ReactiveUI/Shims.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace System.Runtime.Serialization -{ - class IgnoreDataMemberAttribute : Attribute - { - } - - class DataMemberAttribute : Attribute - { - } - class OnDeserializedAttribute : Attribute - { - } - - class DataContractAttribute : Attribute - { - } - - class StreamingContext { } -} - -namespace System.Diagnostics.Contracts -{ - static class Contract - { - public static void Requires(bool condition) - { - - } - } -} diff --git a/src/Avalonia.ReactiveUI/src b/src/Avalonia.ReactiveUI/src deleted file mode 160000 index 3f725c808b..0000000000 --- a/src/Avalonia.ReactiveUI/src +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3f725c808b1d4c8457f0d3204e0a071aa462cd75 diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index f6f8f6bcb0..48f8c6f396 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -19,7 +19,6 @@ - diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index bd86877404..230e61f300 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -10,8 +10,9 @@ using Avalonia.Data; using Avalonia.Markup.Data; using Avalonia.Markup.Xaml.Data; using Moq; -using ReactiveUI; using Xunit; +using System.ComponentModel; +using System.Runtime.CompilerServices; namespace Avalonia.Markup.Xaml.UnitTests.Data { @@ -350,14 +351,25 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data } } - public class Source : ReactiveObject + public class Source : INotifyPropertyChanged { private string _foo; public string Foo { get { return _foo; } - set { this.RaiseAndSetIfChanged(ref _foo, value); } + set + { + _foo = value; + RaisePropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + private void RaisePropertyChanged([CallerMemberName] string prop = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs index 778a93d326..c6006f3afb 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs @@ -6,8 +6,9 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Markup.Data; using Avalonia.Markup.Xaml.Data; -using ReactiveUI; using Xunit; +using System.ComponentModel; +using System.Runtime.CompilerServices; namespace Avalonia.Markup.Xaml.UnitTests.Data { @@ -24,15 +25,26 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data Assert.Equal(target.Text, "foo"); } - - public class Source : ReactiveObject + + public class Source : INotifyPropertyChanged { private string _foo; public string Foo { get { return _foo; } - set { this.RaiseAndSetIfChanged(ref _foo, value); } + set + { + _foo = value; + RaisePropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + private void RaisePropertyChanged([CallerMemberName] string prop = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } } } From c1ad9b36d85339fa7d330799e0fffc8a0b7147b5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 5 Aug 2017 20:11:01 -0700 Subject: [PATCH 41/75] Create Avalonia.ReactiveUI package. PR feedback Update ReactiveUI version to version that supports .NET Core --- build/ReactiveUI.props | 2 +- build/Rx.props | 3 +-- packages.cake | 22 ++++++++++++++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index 4473447647..de8d1d2104 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/build/Rx.props b/build/Rx.props index e63055da3e..e88ab1ec37 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -5,7 +5,6 @@ - - + diff --git a/packages.cake b/packages.cake index 22a519802f..8aa8216498 100644 --- a/packages.cake +++ b/packages.cake @@ -111,6 +111,7 @@ public class Packages var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1; var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1; var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1; + var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1; var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1; SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1; SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1; @@ -124,6 +125,7 @@ public class Packages context.Information("Package: Splat, version: {0}", SplatVersion); context.Information("Package: Sprache, version: {0}", SpracheVersion); context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion); + context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion); context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion); context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion); context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion); @@ -176,7 +178,6 @@ public class Packages new [] { "./src/", "Avalonia.Visuals", ".xml" }, new [] { "./src/", "Avalonia.Styling", ".dll" }, new [] { "./src/", "Avalonia.Styling", ".xml" }, - new [] { "./src/", "Avalonia.ReactiveUI", ".dll" }, new [] { "./src/", "Avalonia.Themes.Default", ".dll" }, new [] { "./src/", "Avalonia.Themes.Default", ".xml" }, new [] { "./src/Markup/", "Avalonia.Markup", ".dll" }, @@ -273,7 +274,24 @@ public class Packages }, BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"), OutputDirectory = parameters.NugetRoot - } + }, + /////////////////////////////////////////////////////////////////////////////// + // Avalonia.ReactiveUI + /////////////////////////////////////////////////////////////////////////////// + new NuGetPackSettings() + { + Id = "Avalonia.ReactiveUI", + Dependencies = new DependencyBuilder(this) + { + new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }, + }.Deps(new string[] {null}, "reactiveui"), + Files = new [] + { + new NuSpecContent { Source = "Avalonia.ReactiveUI.dll", Target = "lib/netstandard1.3" } + }, + BasePath = context.Directory("./src/Avalonia.ReactiveUI/bin/" + parameters.DirSuffix + "/netstandard1.3"), + OutputDirectory = parameters.NugetRoot + }, }; var nuspecNuGetSettingsMobile = new [] From 1cf3e6bfc6ef9d42c5212518b9e653bb9e8a46c0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 2 Sep 2017 00:37:37 +0200 Subject: [PATCH 42/75] Use discard in TryGetResource. --- samples/ControlCatalog/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index f6894e4d8a..63f9ab1d0d 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -19,7 +19,7 @@ namespace ControlCatalog // so we must refer to this resource DLL statically. For // now I am doing that here. But we need a better solution!! var theme = new Avalonia.Themes.Default.DefaultTheme(); - theme.TryGetResource("Button", out var button); + theme.TryGetResource("Button", out _); AvaloniaXamlLoader.Load(this); } } From 814a3a5ee6fe1c0d753919c376b490cc0acbc1a9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 2 Sep 2017 00:37:55 +0200 Subject: [PATCH 43/75] Make `IDictionary` members explicit. And use expression bodied members where possible. --- .../Collections/AvaloniaDictionary.cs | 57 +++++-------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index 724442c5f2..b90dccf74e 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -52,15 +52,15 @@ namespace Avalonia.Collections /// public ICollection Values => _inner.Values; - public bool IsFixedSize => ((IDictionary)_inner).IsFixedSize; + bool IDictionary.IsFixedSize => ((IDictionary)_inner).IsFixedSize; ICollection IDictionary.Keys => ((IDictionary)_inner).Keys; ICollection IDictionary.Values => ((IDictionary)_inner).Values; - public bool IsSynchronized => ((IDictionary)_inner).IsSynchronized; + bool ICollection.IsSynchronized => ((IDictionary)_inner).IsSynchronized; - public object SyncRoot => ((IDictionary)_inner).SyncRoot; + object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot; /// /// Gets or sets the named resource. @@ -131,10 +131,7 @@ namespace Avalonia.Collections } /// - public bool ContainsKey(TKey key) - { - return _inner.ContainsKey(key); - } + public bool ContainsKey(TKey key) => _inner.ContainsKey(key); /// public void CopyTo(KeyValuePair[] array, int arrayIndex) @@ -143,21 +140,16 @@ namespace Avalonia.Collections } /// - public IEnumerator> GetEnumerator() - { - return _inner.GetEnumerator(); - } + public IEnumerator> GetEnumerator() => _inner.GetEnumerator(); /// public bool Remove(TKey key) { - TValue value; - - if (_inner.TryGetValue(key, out value)) + if (_inner.TryGetValue(key, out TValue value)) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); - + if (CollectionChanged != null) { var e = new NotifyCollectionChangedEventArgs( @@ -176,22 +168,13 @@ namespace Avalonia.Collections } /// - public bool TryGetValue(TKey key, out TValue value) - { - return _inner.TryGetValue(key, out value); - } + public bool TryGetValue(TKey key, out TValue value) => _inner.TryGetValue(key, out value); /// - IEnumerator IEnumerable.GetEnumerator() - { - return _inner.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator(); /// - void ICollection.CopyTo(Array array, int index) - { - ((ICollection)_inner).CopyTo(array, index); - } + void ICollection.CopyTo(Array array, int index) => ((ICollection)_inner).CopyTo(array, index); /// void ICollection>.Add(KeyValuePair item) @@ -212,28 +195,16 @@ namespace Avalonia.Collections } /// - void IDictionary.Add(object key, object value) - { - Add((TKey)key, (TValue)value); - } + void IDictionary.Add(object key, object value) => Add((TKey)key, (TValue)value); /// - bool IDictionary.Contains(object key) - { - return ((IDictionary)_inner).Contains(key); - } + bool IDictionary.Contains(object key) => ((IDictionary) _inner).Contains(key); /// - IDictionaryEnumerator IDictionary.GetEnumerator() - { - return ((IDictionary)_inner).GetEnumerator(); - } + IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator(); /// - void IDictionary.Remove(object key) - { - Remove((TKey)key); - } + void IDictionary.Remove(object key) => Remove((TKey)key); private void NotifyAdd(TKey key, TValue value) { From bf4bb7d54c2373e15cbfa30b97ad2d00b1e9fb18 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 2 Sep 2017 01:01:46 +0200 Subject: [PATCH 44/75] Added TryFindResource extension method. `FindResource` can be ambiguous because it returns `AvaloniaProperty.UnsetValue` in the case of a resource not being found, which is a valid resource value. `TryFindResource` removes this ambiguity. `FindResource` has been left in for API compatibility with other frameworks. --- .../Controls/ResourceProviderExtensions.cs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs b/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs index c96e8ea7f3..1f25fa132d 100644 --- a/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs +++ b/src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Reactive; using System.Reactive.Linq; -using System.Text; namespace Avalonia.Controls { @@ -15,6 +13,23 @@ namespace Avalonia.Controls /// The resource key. /// The resource, or if not found. public static object FindResource(this IResourceNode control, string key) + { + if (control.TryFindResource(key, out var value)) + { + return value; + } + + return AvaloniaProperty.UnsetValue; + } + + /// + /// Tries to the specified resource by searching up the logical tree and then global styles. + /// + /// The control. + /// The resource key. + /// On return, contains the resource if found, otherwise null. + /// True if the resource was found; otherwise false. + public static bool TryFindResource(this IResourceNode control, string key, out object value) { Contract.Requires(control != null); Contract.Requires(key != null); @@ -25,16 +40,17 @@ namespace Avalonia.Controls { if (current is IResourceNode host) { - if (host.TryGetResource(key, out var value)) + if (host.TryGetResource(key, out value)) { - return value; + return true; } } current = current.ResourceParent; } - return AvaloniaProperty.UnsetValue; + value = null; + return false; } public static IObservable GetResourceObservable(this IResourceNode target, string key) From 31c9446429423aaea3451528781105da7a22f353 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Sat, 2 Sep 2017 15:11:44 +0300 Subject: [PATCH 45/75] Improve demo --- samples/ControlCatalog/Pages/ToolTipPage.xaml | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/samples/ControlCatalog/Pages/ToolTipPage.xaml b/samples/ControlCatalog/Pages/ToolTipPage.xaml index 79114bc9de..5cf7fee4d1 100644 --- a/samples/ControlCatalog/Pages/ToolTipPage.xaml +++ b/samples/ControlCatalog/Pages/ToolTipPage.xaml @@ -4,13 +4,26 @@ ToolTip A control which pops up a hint when a control is hovered - - + + + Hover Here + + A control which pops up a hint when a control is hovered - Hover Here - - - And Here + ToolTip bottom placement - + \ No newline at end of file From 1d4aabb739f62142d61ca3aa14fee66eb5c125b8 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Sun, 3 Sep 2017 19:39:32 +0200 Subject: [PATCH 46/75] Replaced Set/GetWindowLong with Set/GetWindowLongPtr for 64 bit support --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 4 ++-- src/Windows/Avalonia.Win32/WindowImpl.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 5473ef9bea..a8c9ec101c 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -699,10 +699,10 @@ namespace Avalonia.Win32.Interop public static extern int GetSystemMetrics(SystemMetric smIndex); [DllImport("user32.dll", SetLastError = true)] - public static extern uint GetWindowLong(IntPtr hWnd, int nIndex); + public static extern uint GetWindowLongPtr(IntPtr hWnd, int nIndex); [DllImport("user32.dll", SetLastError = true)] - public static extern uint SetWindowLong(IntPtr hWnd, int nIndex, uint value); + public static extern uint SetWindowLongPtr(IntPtr hWnd, int nIndex, uint value); [DllImport("user32.dll", SetLastError = true)] public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 4a30d48878..4e67b47a36 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -66,8 +66,8 @@ namespace Avalonia.Win32 { get { - var style = UnmanagedMethods.GetWindowLong(_hwnd, -16); - var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, -20); + var style = UnmanagedMethods.GetWindowLongPtr(_hwnd, -16); + var exStyle = UnmanagedMethods.GetWindowLongPtr(_hwnd, -20); var padding = new UnmanagedMethods.RECT(); if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle)) @@ -219,7 +219,7 @@ namespace Avalonia.Win32 return; } - var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -16); + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLongPtr(_hwnd, -16); style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW; @@ -235,7 +235,7 @@ namespace Avalonia.Win32 Rect newRect; var oldThickness = BorderThickness; - UnmanagedMethods.SetWindowLong(_hwnd, -16, (uint)style); + UnmanagedMethods.SetWindowLongPtr(_hwnd, -16, (uint)style); if (value) { From 8f006ff655d37687f3676dc6c83802a151196660 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Sun, 3 Sep 2017 20:44:08 +0200 Subject: [PATCH 47/75] Added utility functions to pinvoke the correct function based on bitness --- .../Interop/UnmanagedMethods.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index a8c9ec101c..4f69e022e2 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -701,9 +701,39 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowLongPtr(IntPtr hWnd, int nIndex); + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLong")] + public static extern uint GetWindowLong32b(IntPtr hWnd, int nIndex); + + public static uint GetWindowLong(IntPtr hWnd, int nIndex) + { + if(IntPtr.Size == 4) + { + return GetWindowLong32b(hWnd, nIndex); + } + else + { + return GetWindowLongPtr(hWnd, nIndex); + } + } + + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] + public static extern uint SetWindowLong32b(IntPtr hWnd, int nIndex, uint value); + [DllImport("user32.dll", SetLastError = true)] public static extern uint SetWindowLongPtr(IntPtr hWnd, int nIndex, uint value); + public static uint SetWindowLong(IntPtr hWnd, int nIndex, uint value) + { + if (IntPtr.Size == 4) + { + return SetWindowLong32b(hWnd, nIndex, value); + } + else + { + return SetWindowLongPtr(hWnd, nIndex, value); + } + } + [DllImport("user32.dll", SetLastError = true)] public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); From 7605909c79c4483530e611196b7ac5950a94df7b Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Sun, 3 Sep 2017 21:14:47 +0200 Subject: [PATCH 48/75] Made the pinvoke calls private Changed SetWindowLongPtr calls to SetWindowLong in Win32/WindowImpl --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 4 ++-- src/Windows/Avalonia.Win32/WindowImpl.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 4f69e022e2..f58beadc0b 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -717,10 +717,10 @@ namespace Avalonia.Win32.Interop } [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] - public static extern uint SetWindowLong32b(IntPtr hWnd, int nIndex, uint value); + private static extern uint SetWindowLong32b(IntPtr hWnd, int nIndex, uint value); [DllImport("user32.dll", SetLastError = true)] - public static extern uint SetWindowLongPtr(IntPtr hWnd, int nIndex, uint value); + private static extern uint SetWindowLongPtr(IntPtr hWnd, int nIndex, uint value); public static uint SetWindowLong(IntPtr hWnd, int nIndex, uint value) { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 4e67b47a36..4a30d48878 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -66,8 +66,8 @@ namespace Avalonia.Win32 { get { - var style = UnmanagedMethods.GetWindowLongPtr(_hwnd, -16); - var exStyle = UnmanagedMethods.GetWindowLongPtr(_hwnd, -20); + var style = UnmanagedMethods.GetWindowLong(_hwnd, -16); + var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, -20); var padding = new UnmanagedMethods.RECT(); if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle)) @@ -219,7 +219,7 @@ namespace Avalonia.Win32 return; } - var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLongPtr(_hwnd, -16); + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -16); style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW; @@ -235,7 +235,7 @@ namespace Avalonia.Win32 Rect newRect; var oldThickness = BorderThickness; - UnmanagedMethods.SetWindowLongPtr(_hwnd, -16, (uint)style); + UnmanagedMethods.SetWindowLong(_hwnd, -16, (uint)style); if (value) { From d278c5ddeb7c6851683b4a4ec62c9dec6a92ca17 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Mon, 4 Sep 2017 17:07:22 +0200 Subject: [PATCH 49/75] Added ShowTaskbarIcon implementation --- samples/ControlCatalog/MainWindow.xaml.cs | 4 +++- src/Avalonia.Controls/Platform/IWindowImpl.cs | 4 ++++ src/Gtk/Avalonia.Gtk/WindowImpl.cs | 5 +++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 15 ++++++++++++++- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 6 ++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 17 +++++++++++++++++ 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 413794dfa2..ef86b1a434 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System; namespace ControlCatalog { @@ -10,7 +11,8 @@ namespace ControlCatalog { this.InitializeComponent(); this.AttachDevTools(); - Renderer.DrawDirtyRects = Renderer.DrawFps = true; + //Renderer.DrawDirtyRects = Renderer.DrawFps = true; + PlatformImpl.ShowTaskbarIcon(false); } private void InitializeComponent() diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index 69b946346e..d56cdc4e19 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -39,5 +39,9 @@ namespace Avalonia.Platform /// Sets the icon of this window. /// void SetIcon(IWindowIconImpl icon); + + + + void ShowTaskbarIcon(bool value); } } diff --git a/src/Gtk/Avalonia.Gtk/WindowImpl.cs b/src/Gtk/Avalonia.Gtk/WindowImpl.cs index d8555b4e05..4c0eacbcf5 100644 --- a/src/Gtk/Avalonia.Gtk/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk/WindowImpl.cs @@ -127,5 +127,10 @@ namespace Avalonia.Gtk { Window.Icon = ((IconImpl)icon).Pixbuf; } + + public void ShowTaskbarIcon(bool value) + { + Window.SkipTaskbarHint = !value; + } } } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index fb1a9955e3..c212807c6a 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -38,6 +38,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_hide(GtkWidget gtkWidget); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_widget_show(GtkWidget gtkWidget); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_icon(GtkWindow window, Pixbuf pixbuf); @@ -75,7 +78,6 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height); - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_realize(GtkWidget gtkWidget); @@ -86,6 +88,13 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_skip_taskbar_hint(GtkWindow gtkWindow, bool setting); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate bool gtk_window_get_skip_taskbar_hint(GtkWindow gtkWindow); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_get_size(GtkWindow gtkWindow, out int width, out int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] @@ -271,6 +280,9 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_window_set_decorated GtkWindowSetDecorated; + public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint; + public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint; + public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint; public static D.gtk_window_set_title GtkWindowSetTitle; public static D.gtk_application_new GtkApplicationNew; public static D.gtk_main_iteration GtkMainIteration; @@ -280,6 +292,7 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_init GtkInit; public static D.gtk_window_present GtkWindowPresent; public static D.gtk_widget_hide GtkWidgetHide; + public static D.gtk_widget_show GtkWidgetShow; public static D.gdk_get_native_handle GetNativeGdkWindowHandle; public static D.gtk_widget_get_window GtkWidgetGetWindow; public static D.gtk_widget_get_scale_factor GtkWidgetGetScaleFactor; diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index f083185a84..df834561d4 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Gtk3.Interop; using Avalonia.Platform; +using System.Runtime.InteropServices; namespace Avalonia.Gtk3 { @@ -59,6 +60,11 @@ namespace Avalonia.Gtk3 //Why do we even have that? } + public void ShowTaskbarIcon(bool value) + { + Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value); + } + class EmptyDisposable : IDisposable { public void Dispose() diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 4a30d48878..bb2d4e808c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -750,5 +750,22 @@ namespace Avalonia.Win32 return (int)(ptr.ToInt64() & 0xffffffff); } + + public void ShowTaskbarIcon(bool value) + { + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -20); + + style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE); + + style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW; + if (value) + style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW; + else + style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW); + + UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); + UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style); + UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Show); + } } } From 913149d093dc069cf62dd0bf54eeaf0ac262b7af Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 11:30:36 +0300 Subject: [PATCH 50/75] Switched to .NET Standard 2.0 --- appveyor.yml | 4 +- build.cake | 2 +- packages.cake | 60 +++++++++---------- .../ControlCatalog.NetCore.csproj | 2 +- samples/ControlCatalog/ControlCatalog.csproj | 2 +- .../Avalonia.Animation.csproj | 2 +- src/Avalonia.Base/Avalonia.Base.csproj | 2 +- .../Avalonia.Controls.csproj | 2 +- .../Avalonia.DesignerSupport.csproj | 2 +- .../Avalonia.Diagnostics.csproj | 2 +- .../Avalonia.DotNetCoreRuntime.csproj | 2 +- .../Avalonia.HtmlRenderer.csproj | 2 +- src/Avalonia.Input/Avalonia.Input.csproj | 2 +- .../Avalonia.Interactivity.csproj | 2 +- src/Avalonia.Layout/Avalonia.Layout.csproj | 2 +- .../Avalonia.Logging.Serilog.csproj | 2 +- .../Avalonia.ReactiveUI.csproj | 2 +- src/Avalonia.Styling/Avalonia.Styling.csproj | 2 +- .../Avalonia.Themes.Default.csproj | 2 +- src/Avalonia.Visuals/Avalonia.Visuals.csproj | 2 +- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 2 +- .../Avalonia.LinuxFramebuffer.csproj | 2 +- .../Avalonia.Markup.Xaml.csproj | 2 +- .../Avalonia.Markup/Avalonia.Markup.csproj | 2 +- src/Skia/Avalonia.Skia/Avalonia.Skia.csproj | 2 +- .../Avalonia.Direct2D1.csproj | 2 +- .../Avalonia.Win32.NetStandard.csproj | 2 +- .../Avalonia.Base.UnitTests.csproj | 2 +- .../Avalonia.Controls.UnitTests.csproj | 2 +- .../Avalonia.Input.UnitTests.csproj | 2 +- .../Avalonia.Interactivity.UnitTests.csproj | 2 +- .../Avalonia.Layout.UnitTests.csproj | 2 +- .../Avalonia.Markup.UnitTests.csproj | 2 +- .../Avalonia.Markup.Xaml.UnitTests.csproj | 2 +- .../Avalonia.Skia.RenderTests.csproj | 2 +- .../Avalonia.Styling.UnitTests.csproj | 2 +- .../Avalonia.UnitTests.csproj | 6 +- .../Avalonia.Visuals.UnitTests.csproj | 2 +- 38 files changed, 70 insertions(+), 70 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7457a1d5bb..ca548b6b0a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,9 +17,9 @@ init: - ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")} install: - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi - - if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe" + - if not exist dotnet-2.0.0.exe appveyor DownloadFile https://download.microsoft.com/download/0/F/D/0FD852A4-7EA1-4E2A-983A-0484AC19B92C/dotnet-sdk-2.0.0-win-x64.exe -FileName "dotnet-2.0.0.exe" - ps: Start-Process -FilePath "msiexec" -ArgumentList "/i gtk-sharp-2.12.26.msi /quiet /qn /norestart" -Wait - - ps: Start-Process -FilePath "dotnet-1.0.1.exe" -ArgumentList "/quiet" -Wait + - ps: Start-Process -FilePath "dotnet-2.0.0.exe" -ArgumentList "/quiet" -Wait - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH% before_build: - git submodule update --init diff --git a/build.cake b/build.cake index 4e18f23780..b7b349b12b 100644 --- a/build.cake +++ b/build.cake @@ -162,7 +162,7 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj"); Information("Running tests from " + project); DotNetCoreRestore(project); - var frameworks = new List(){"netcoreapp1.1"}; + var frameworks = new List(){"netcoreapp2.0"}; if(parameters.IsRunningOnWindows) frameworks.Add("net461"); foreach(var fw in frameworks) diff --git a/packages.cake b/packages.cake index 22a519802f..4d33a90908 100644 --- a/packages.cake +++ b/packages.cake @@ -43,7 +43,7 @@ public class Packages } } - //new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, + //new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" }, public Packages(ICakeContext context, Parameters parameters) { // NUGET NUSPECS @@ -186,12 +186,12 @@ public class Packages }; var coreLibrariesFiles = coreLibraries.Select((lib) => { - return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.3/" + lib[1] + lib[2]); + return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard2.0/" + lib[1] + lib[2]); }).ToList(); var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => { return new NuSpecContent { - Source = file.FullPath, Target = "lib/netstandard1.3" + Source = file.FullPath, Target = "lib/netstandard2.0" }; }); @@ -203,7 +203,7 @@ public class Packages var netcoreappCoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => { return new NuSpecContent { - Source = file.FullPath, Target = "lib/netcoreapp1.0" + Source = file.FullPath, Target = "lib/netcoreapp2.0" }; }); @@ -218,8 +218,8 @@ public class Packages var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"}; var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => { return new NuSpecContent { - Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp1.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, - Target = "lib/netcoreapp1.0" + Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, + Target = "lib/netcoreapp2.0" }; }); @@ -238,15 +238,15 @@ public class Packages new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion }, new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion }, //.NET Core - new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, - new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" }, - new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" }, - new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion }, - new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion }, - new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion }, - new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion }, + new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" }, + new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" }, + new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" }, + new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp2.0", Version = SplatVersion }, + new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion }, + new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion }, + new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion }, } - .Deps(new string[]{null, "netcoreapp1.0"}, + .Deps(new string[]{null, "netcoreapp2.0"}, "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives", "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter") .ToArray(), @@ -269,9 +269,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard2.0" } }, - BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"), + BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard2.0"), OutputDirectory = parameters.NugetRoot } }; @@ -331,7 +331,7 @@ public class Packages Files = new [] { new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" }, - new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Win32.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.Win32.dll", Target = "lib/netstandard2.0" } }, BasePath = context.Directory("./src/Windows"), OutputDirectory = parameters.NugetRoot @@ -352,9 +352,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard2.0" } }, - BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard1.3"), + BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard2.0"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// @@ -386,9 +386,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard2.0" } }, - BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.3"), + BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard2.0"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// @@ -418,18 +418,18 @@ public class Packages { new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }, - new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp1.1" }, - new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp1.1" }, - new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp1.1" }, + new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp2.0" }, + new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp2.0" }, + new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp2.0" }, new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="net461" }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="net461" }, new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="net461" } }, Files = new [] { - new NuSpecContent { Source = "Avalonia.Skia.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.Skia.dll", Target = "lib/netstandard2.0" } }, - BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard1.3"), + BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard2.0"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// @@ -448,9 +448,9 @@ public class Packages new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="net45", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version }, //.NET Core - new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp1.0", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.0", Version = parameters.Version } + new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp2.0", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp2.0", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp2.0", Version = parameters.Version } }, Files = new NuSpecContent[] { @@ -488,7 +488,7 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard2.0" } }, BasePath = context.Directory("./src/Linux/"), OutputDirectory = parameters.NugetRoot diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index e0e848b91b..b308c59789 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp1.1 + netcoreapp2.0 diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index a75de098e8..99ddce0757 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 False false diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index 8f832dabcf..bafac2b261 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 6906aed42b..58c510f483 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false Avalonia diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 037546b186..d999e3b74e 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index a68e8760f2..63cb8132a9 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index be3f397283..5cb56e0be1 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj index 36443ce6f3..5c72d433ce 100644 --- a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj +++ b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj @@ -1,6 +1,6 @@  - netcoreapp1.0 + netcoreapp2.0 false diff --git a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj index f715217e42..0cbe318c9b 100644 --- a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj +++ b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 False False false diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj index b5482ebce1..901f7b5675 100644 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ b/src/Avalonia.Input/Avalonia.Input.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj index 9d22de86b3..d683c77468 100644 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj index d0260391d8..810a985e36 100644 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ b/src/Avalonia.Layout/Avalonia.Layout.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj index 9ac40cba07..b9fa2f208b 100644 --- a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj +++ b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 22d815d786..835e2295e3 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 False false diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index 6bf37b522b..965b6d87e6 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index 4e980680d9..b4b200834b 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index 127760d8ac..fe18b0e446 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false Avalonia diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 0f4b8c2e1b..b58a83b3d2 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 False false diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj index 82da3ec090..c38cb0f114 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj +++ b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 true diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 08ea6b6877..8f538b38a5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 False false false diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index 3d16196b70..57552f852c 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj index bae10e4d97..f5ed89d154 100644 --- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj +++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false Avalonia.Skia Avalonia.Skia diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index 4d656680b1..a84c373886 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 false diff --git a/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj b/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj index cedf3c4213..bae634b030 100644 --- a/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj +++ b/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj @@ -1,6 +1,6 @@  - netstandard1.3 + netstandard2.0 False false Avalonia.Win32 diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index c656801d90..8692cdef42 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj index 957cdd7036..8b2f5093cf 100644 --- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj +++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj b/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj index 186d293b96..b2ca80e5b8 100644 --- a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj +++ b/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj b/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj index 86c9cf0617..78d2128478 100644 --- a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj +++ b/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj index 0950856dca..0020ff46d9 100644 --- a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj +++ b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj index 3ccd3da044..5d3b04e24b 100644 --- a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj +++ b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index f6f8f6bcb0..a1f66d0c72 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj index 134e89f2e5..370cfac6dd 100644 --- a/tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -6,7 +6,7 @@ - netcoreapp1.1 + netcoreapp2.0 bin\Skia\$(Configuration) false False diff --git a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj index 8dd8faf9db..4a2f2caf1b 100644 --- a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj +++ b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 Library diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index 40023134fd..b04fd5d2bc 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 false Library @@ -33,7 +33,7 @@ - + @@ -52,7 +52,7 @@ - + \ No newline at end of file diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj index d35542b51f..6dc9e3324d 100644 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp1.1 + net461;netcoreapp2.0 From 39d5389b1f91d02ab60eefa6fedaac41188ca0cf Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 12:33:40 +0300 Subject: [PATCH 51/75] Fixed build issues --- src/Avalonia.Controls/DropDown.cs | 2 +- src/Avalonia.HtmlRenderer/Compat/Api.cs | 12 ------------ .../Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj | 1 + .../PortableXaml/portable.xaml.github | 2 +- .../PlatformSupport/StandardRuntimePlatform.cs | 2 +- .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 +- 6 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs index 0b1371b10e..5349fb1ca7 100644 --- a/src/Avalonia.Controls/DropDown.cs +++ b/src/Avalonia.Controls/DropDown.cs @@ -120,7 +120,7 @@ namespace Avalonia.Controls /// protected override void OnPointerPressed(PointerPressedEventArgs e) { - if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() != typeof(PopupRoot)) + if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() is PopupRoot) { IsDropDownOpen = true; e.Handled = true; diff --git a/src/Avalonia.HtmlRenderer/Compat/Api.cs b/src/Avalonia.HtmlRenderer/Compat/Api.cs index 798b697277..d4f4c6abbe 100644 --- a/src/Avalonia.HtmlRenderer/Compat/Api.cs +++ b/src/Avalonia.HtmlRenderer/Compat/Api.cs @@ -4,18 +4,6 @@ using System.Text; namespace System.Net { - internal class AsyncCompletedEventArgs - { - public object UserState { get; set; } - public Exception Error { get; set; } - public bool Cancelled { get; set; } - - public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState) - { - - } - } - class WebException : Exception { public object Response { get; set; } diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 8f538b38a5..99b62181d7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -94,6 +94,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github index dfc5affa5d..eebf9dbb92 160000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github @@ -1 +1 @@ -Subproject commit dfc5affa5d8f4ddf5a7707e3202d5593519de640 +Subproject commit eebf9dbb9275ecc48c18ec24f6fbad8cb494857f diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs index e7402fb23c..092910a08f 100644 --- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs @@ -12,7 +12,7 @@ namespace Avalonia.Shared.PlatformSupport internal partial class StandardRuntimePlatform : IRuntimePlatform { -#if NETCOREAPP1_0 +#if NETCOREAPP2_0 public void PostThreadPoolItem(Action cb) => ThreadPool.QueueUserWorkItem(_ => cb(), null); #else public Assembly[] GetLoadedAssemblies() => AppDomain.CurrentDomain.GetAssemblies(); diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index d5a6f1a7a1..1cc06af0d6 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -811,7 +811,7 @@ namespace Avalonia.Win32.Interop return SetClassLong64(hWnd, nIndex, dwNewLong); } -#if !NETSTANDARD && !NETSTANDARD1_3 +#if !NETSTANDARD && !NETSTANDARD2_0 [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] internal class FileOpenDialogRCW { } From 8621abc7248a72752a02215839eefa5bdac55ed6 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Tue, 5 Sep 2017 12:44:42 +0200 Subject: [PATCH 52/75] Fixed potential null reference --- samples/ControlCatalog/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index ef86b1a434..4cd9b93828 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -12,7 +12,7 @@ namespace ControlCatalog this.InitializeComponent(); this.AttachDevTools(); //Renderer.DrawDirtyRects = Renderer.DrawFps = true; - PlatformImpl.ShowTaskbarIcon(false); + PlatformImpl?.ShowTaskbarIcon(false); } private void InitializeComponent() From 65b4cfb6ba5c01b2fcd58455a6cf2d76c0b3c9ae Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 13:48:03 +0300 Subject: [PATCH 53/75] Use new SDK for DotNetFrameworkRuntime --- .../Avalonia.DotNetFrameworkRuntime.csproj | 94 ++++--------------- .../Properties/AssemblyInfo.cs | 15 --- 2 files changed, 20 insertions(+), 89 deletions(-) delete mode 100644 src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj index e2c866fe3d..e3db94fc1f 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj +++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj @@ -1,75 +1,21 @@ - - - - - Debug - AnyCPU - {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0} - Library - Properties - Avalonia.DotNetFrameworkRuntime - Avalonia.DotNetFrameworkRuntime - v4.6.1 - 512 - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;FULLDOTNET - prompt - 4 - bin\Debug\Avalonia.DotNetFrameworkRuntime.xml - - - pdbonly - true - bin\Release\ - TRACE;FULLDOTNET - prompt - 4 - bin\Release\Avalonia.DotNetFrameworkRuntime.xml - true - - - - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {eb582467-6abb-43a1-b052-e981ba910e3a} - Avalonia.Visuals - - - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} - Avalonia.Styling - - - - - + + + net461 + false + bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xmlL + $(DefineConstants);FULLDOTNET + true + + + + Properties\SharedAssemblyInfo.cs + + + + + + + + + \ No newline at end of file diff --git a/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs b/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs deleted file mode 100644 index 3a91d50a24..0000000000 --- a/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Reflection; -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("Avalonia.DotNetFrameworkRuntime")] - -// 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("4a1abb09-9047-4bd5-a4ad-a055e52c5ee0")] From 3ce69edb249cc8d30199be960a1af9b7f5e3b00f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 14:43:05 +0300 Subject: [PATCH 54/75] fixed framework version --- build/XUnit.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/XUnit.props b/build/XUnit.props index d59cee6536..9d4d401743 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -9,7 +9,7 @@ - + From 4722e636c6aa0c377b6c7c015d1c05e5cd801aa8 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 14:52:15 +0300 Subject: [PATCH 55/75] Use dotnet 2.0.0 for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3c02eea58..6d7600c36b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ env: - DOTNET_CLI_TELEMETRY_OPTOUT=1 mono: - latest -dotnet: 1.0.1 +dotnet: 2.0.0 script: - ./build.sh --target "Travis" --platform "Mono" --configuration "Release" notifications: From a9ef43f17905b20299907d55d2d32bd05084545a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 15:03:23 +0300 Subject: [PATCH 56/75] Fixed paths --- packages.cake | 2 +- .../Avalonia.DotNetFrameworkRuntime.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages.cake b/packages.cake index 4d33a90908..9ae45d15bd 100644 --- a/packages.cake +++ b/packages.cake @@ -210,7 +210,7 @@ public class Packages var net45RuntimePlatformExtensions = new [] {".xml", ".dll"}; var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => { return new NuSpecContent { - Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath, + Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/net461/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath, Target = "lib/net45" }; }); diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj index e3db94fc1f..181f5e3a1e 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj +++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj @@ -2,7 +2,7 @@ net461 false - bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xmlL + bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xml $(DefineConstants);FULLDOTNET true From 017f8ee8b007e9858a7507d24c19f2a8ad67a9ba Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 Sep 2017 15:30:50 +0300 Subject: [PATCH 57/75] Specify osx_image --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6d7600c36b..52a400d19a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ os: - linux - osx dist: trusty +osx_image: xcode8.3 env: global: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 From a12c4649cbca6e5d729bdd22a55db171434a021d Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Tue, 5 Sep 2017 18:35:09 +0200 Subject: [PATCH 58/75] Cleaned up implementation of ShowTaskbarIcon Created property in Window to enable/disable the taskbar icon --- src/Avalonia.Controls/Platform/IWindowImpl.cs | 7 +++--- src/Avalonia.Controls/Window.cs | 24 ++++++++++++++++--- src/Gtk/Avalonia.Gtk/WindowImpl.cs | 21 +++++++--------- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 6 ++--- src/Windows/Avalonia.Win32/WindowImpl.cs | 1 + 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index d56cdc4e19..37637b1624 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -31,7 +31,7 @@ namespace Avalonia.Platform IDisposable ShowDialog(); /// - /// Enables of disables system window decorations (title bar, buttons, etc) + /// Enables or disables system window decorations (title bar, buttons, etc) /// void SetSystemDecorations(bool enabled); @@ -40,8 +40,9 @@ namespace Avalonia.Platform /// void SetIcon(IWindowIconImpl icon); - - + /// + /// Enables or disables the taskbar icon + /// void ShowTaskbarIcon(bool value); } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 46c625cc4c..37f7c6d47f 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -61,11 +61,17 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(SizeToContent)); /// - /// Enables of disables system window decorations (title bar, buttons, etc) + /// Enables or disables system window decorations (title bar, buttons, etc) /// public static readonly StyledProperty HasSystemDecorationsProperty = AvaloniaProperty.Register(nameof(HasSystemDecorations), true); - + + /// + /// Enables or disables the taskbar icon + /// + public static readonly StyledProperty HasTaskbarIconProperty = + AvaloniaProperty.Register(nameof(HasTaskbarIcon), true); + /// /// Defines the property. /// @@ -92,6 +98,8 @@ namespace Avalonia.Controls HasSystemDecorationsProperty.Changed.AddClassHandler( (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); + HasTaskbarIconProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); + IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); } @@ -152,7 +160,7 @@ namespace Avalonia.Controls } /// - /// Enables of disables system window decorations (title bar, buttons, etc) + /// Enables or disables system window decorations (title bar, buttons, etc) /// /// public bool HasSystemDecorations @@ -160,6 +168,16 @@ namespace Avalonia.Controls get { return GetValue(HasSystemDecorationsProperty); } set { SetValue(HasSystemDecorationsProperty, value); } } + + /// + /// Enables or disables the taskbar icon + /// + /// + public bool HasTaskbarIcon + { + get { return GetValue(HasTaskbarIconProperty); } + set { SetValue(HasTaskbarIconProperty, value); } + } /// /// Gets or sets the minimized/maximized state of the window. diff --git a/src/Gtk/Avalonia.Gtk/WindowImpl.cs b/src/Gtk/Avalonia.Gtk/WindowImpl.cs index 4c0eacbcf5..2c1826c509 100644 --- a/src/Gtk/Avalonia.Gtk/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk/WindowImpl.cs @@ -6,11 +6,12 @@ using Gdk; namespace Avalonia.Gtk { using Gtk = global::Gtk; + public class WindowImpl : TopLevelImpl, IWindowImpl { private Gtk.Window _window; - private Gtk.Window Window => _window ?? (_window = (Gtk.Window) Widget); - + private Gtk.Window Window => _window ?? (_window = (Gtk.Window)Widget); + public WindowImpl(Gtk.WindowType type) : base(new PlatformHandleAwareWindow(type)) { Init(); @@ -29,8 +30,10 @@ namespace Avalonia.Gtk _lastClientSize = ClientSize; _lastPosition = Position; } + private Size _lastClientSize; private Point _lastPosition; + void OnConfigureEvent(object o, Gtk.ConfigureEventArgs args) { var evnt = args.Event; @@ -44,7 +47,7 @@ namespace Avalonia.Gtk } var newPosition = new Point(evnt.X, evnt.Y); - + if (newPosition != _lastPosition) { PositionChanged(newPosition); @@ -107,10 +110,7 @@ namespace Avalonia.Gtk Window.GetPosition(out x, out y); return new Point(x, y); } - set - { - Window.Move((int)value.X, (int)value.Y); - } + set { Window.Move((int)value.X, (int)value.Y); } } public IDisposable ShowDialog() @@ -128,9 +128,6 @@ namespace Avalonia.Gtk Window.Icon = ((IconImpl)icon).Pixbuf; } - public void ShowTaskbarIcon(bool value) - { - Window.SkipTaskbarHint = !value; - } + public void ShowTaskbarIcon(bool value) => Window.SkipTaskbarHint = !value; } -} +} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index df834561d4..e6935c4f20 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -60,10 +60,8 @@ namespace Avalonia.Gtk3 //Why do we even have that? } - public void ShowTaskbarIcon(bool value) - { - Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value); - } + public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value); + class EmptyDisposable : IDisposable { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index bb2d4e808c..b9e2151b8f 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -763,6 +763,7 @@ namespace Avalonia.Win32 else style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW); + //Toggle to make the styles stick UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style); UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Show); From cc04dc7c788985a9cb03268454992862555939f7 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 Sep 2017 00:31:21 +0300 Subject: [PATCH 59/75] Removed commented code --- packages.cake | 1 - 1 file changed, 1 deletion(-) diff --git a/packages.cake b/packages.cake index 9ae45d15bd..2c0298b669 100644 --- a/packages.cake +++ b/packages.cake @@ -43,7 +43,6 @@ public class Packages } } - //new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" }, public Packages(ICakeContext context, Parameters parameters) { // NUGET NUSPECS From 284f0a75c79b62999b3e87b010edf88f9fd8b1df Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Wed, 6 Sep 2017 10:35:28 +0200 Subject: [PATCH 60/75] Uncommented DrawDirtyRects/DrawFps --- samples/ControlCatalog/MainWindow.xaml.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 4cd9b93828..c2e8c0c082 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -11,8 +11,7 @@ namespace ControlCatalog { this.InitializeComponent(); this.AttachDevTools(); - //Renderer.DrawDirtyRects = Renderer.DrawFps = true; - PlatformImpl?.ShowTaskbarIcon(false); + Renderer.DrawDirtyRects = Renderer.DrawFps = true; } private void InitializeComponent() From c9fa06637f4ffe34e93500e841b08eb50b10f0c9 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Wed, 6 Sep 2017 11:46:04 +0200 Subject: [PATCH 61/75] Cleaned up Native --- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index c212807c6a..ba6a6ace92 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -90,10 +90,13 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_skip_taskbar_hint(GtkWindow gtkWindow, bool setting); - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate bool gtk_window_get_skip_taskbar_hint(GtkWindow gtkWindow); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate bool gtk_window_get_skip_pager_hint(GtkWindow gtkWindow); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_get_size(GtkWindow gtkWindow, out int width, out int height); @@ -119,9 +122,6 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id); - - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] public delegate CairoSurface cairo_image_surface_create(int format, int width, int height); @@ -281,8 +281,9 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_window_set_decorated GtkWindowSetDecorated; public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint; - public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint; public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint; + public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint; + public static D.gtk_window_get_skip_pager_hint GtkWindowGetSkipPagerHint; public static D.gtk_window_set_title GtkWindowSetTitle; public static D.gtk_application_new GtkApplicationNew; public static D.gtk_main_iteration GtkMainIteration; From 34e82c19fb7f48e07902a67a5a9bc860d420ea9a Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Wed, 6 Sep 2017 11:50:02 +0200 Subject: [PATCH 62/75] Win32 implementation now checks the previous ShowWindowCommand and sets it back to that after hiding. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index b9e2151b8f..128e2a6338 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -763,10 +763,14 @@ namespace Avalonia.Win32 else style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW); - //Toggle to make the styles stick - UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); - UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style); - UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Show); + WINDOWPLACEMENT windowPlacement = new WINDOWPLACEMENT(); + if (UnmanagedMethods.GetWindowPlacement(_hwnd, ref windowPlacement)) + { + //Toggle to make the styles stick + UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); + UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style); + UnmanagedMethods.ShowWindow(_hwnd, windowPlacement.ShowCmd); + } } } } From b888cbb8652972b78095054dffb90deb5010db3f Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Wed, 6 Sep 2017 11:59:12 +0200 Subject: [PATCH 63/75] Fixed windowplacement failing due to incorrect length --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 128e2a6338..72485379a7 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -763,7 +763,7 @@ namespace Avalonia.Win32 else style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW); - WINDOWPLACEMENT windowPlacement = new WINDOWPLACEMENT(); + WINDOWPLACEMENT windowPlacement = UnmanagedMethods.WINDOWPLACEMENT.Default; if (UnmanagedMethods.GetWindowPlacement(_hwnd, ref windowPlacement)) { //Toggle to make the styles stick From 61e4e50d3120b6ccac553f233cbdb65d7bce3882 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 Sep 2017 13:28:19 +0300 Subject: [PATCH 64/75] Enable win32 file dialog functionality for .NET Standard build --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 6 ++---- src/Windows/Avalonia.Win32/SystemDialogImpl.cs | 9 +-------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 2d739f095d..e7401c19b9 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -841,14 +841,13 @@ namespace Avalonia.Win32.Interop return SetClassLong64(hWnd, nIndex, dwNewLong); } -#if !NETSTANDARD && !NETSTANDARD2_0 + [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] internal class FileOpenDialogRCW { } [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv); -#endif [DllImport("user32.dll", SetLastError = true)] public static extern bool OpenClipboard(IntPtr hWndOwner); @@ -1183,7 +1182,7 @@ namespace Avalonia.Win32.Interop public int flagsEx; } } -#if !NETSTANDARD && !NETSTANDARD1_3 + [ComImport(), Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IFileDialog { @@ -1283,5 +1282,4 @@ namespace Avalonia.Win32.Interop uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder); } -#endif } diff --git a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs index 4d8c375aaf..eef8a4d449 100644 --- a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs +++ b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs @@ -100,17 +100,14 @@ namespace Avalonia.Win32 var pofn = &ofn; // We should save the current directory to restore it later. -#if !NETSTANDARD var currentDirectory = Environment.CurrentDirectory; -#endif + var res = dialog is OpenFileDialog ? UnmanagedMethods.GetOpenFileName(new IntPtr(pofn)) : UnmanagedMethods.GetSaveFileName(new IntPtr(pofn)); // Restore the old current directory, since GetOpenFileName and GetSaveFileName change it after they're called -#if !NETSTANDARD Environment.CurrentDirectory = currentDirectory; -#endif if (!res) return null; @@ -155,9 +152,6 @@ namespace Avalonia.Win32 public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) { -#if NETSTANDARD - throw new NotImplementedException(); -#else return Task.Factory.StartNew(() => { string result = string.Empty; @@ -214,7 +208,6 @@ namespace Avalonia.Win32 return result; }); -#endif } } } From f330132dc3232ebd914a29d095a9a44e9f1f45c5 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 Sep 2017 14:40:46 +0300 Subject: [PATCH 65/75] Use CoCreateInstance for .NET Core compatibility --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 5 +++-- src/Windows/Avalonia.Win32/SystemDialogImpl.cs | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index e7401c19b9..4fed7a34f4 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -842,8 +842,9 @@ namespace Avalonia.Win32.Interop return SetClassLong64(hWnd, nIndex, dwNewLong); } - [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] - internal class FileOpenDialogRCW { } + [DllImport("ole32.dll", PreserveSig = true)] + internal static extern int CoCreateInstance(ref Guid clsid, + IntPtr ignore1, int ignore2, ref Guid iid, [MarshalAs(UnmanagedType.IUnknown), Out] out object pUnkOuter); [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] diff --git a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs index eef8a4d449..e08fecbbd9 100644 --- a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs +++ b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs @@ -157,7 +157,11 @@ namespace Avalonia.Win32 string result = string.Empty; var hWnd = parent?.Handle?.Handle ?? IntPtr.Zero; - var frm = (IFileDialog)(new UnmanagedMethods.FileOpenDialogRCW()); + var clsid = Guid.Parse("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7"); + var iid = Guid.Parse("42F85136-DB7E-439C-85F1-E4075D135FC8"); + + UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk); + var frm = (IFileDialog)unk; uint options; frm.GetOptions(out options); options |= (uint)(UnmanagedMethods.FOS.FOS_PICKFOLDERS | UnmanagedMethods.FOS.FOS_FORCEFILESYSTEM | UnmanagedMethods.FOS.FOS_NOVALIDATE | UnmanagedMethods.FOS.FOS_NOTESTFILECREATE | UnmanagedMethods.FOS.FOS_DONTADDTORECENT); From 0a77519906faf8833792087d5300f1c8a8e76c8d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 Sep 2017 17:09:07 +0300 Subject: [PATCH 66/75] Update build.md --- docs/guidelines/build.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/guidelines/build.md b/docs/guidelines/build.md index 822496814b..0cf91d913a 100644 --- a/docs/guidelines/build.md +++ b/docs/guidelines/build.md @@ -80,10 +80,11 @@ mono ./samples/ControlCatalog.Desktop/bin/Debug/ControlCatalog.Desktop.exe ### Building Avalonia in MonoDevelop -Unless you have a very current version of monodevelop (6.1.x or newer), it is necessary to manually -restore the Nuget depdendencies as [mentioned above](#restore-nuget-packages). You must then -disable MonoDevelop's inbuilt NuGet package manager add-in by going to `Tools -> Add-in Manager` or -it will complain that a newer version of NuGet is needed. +Flatpak version will *NOT* work. Version from https://github.com/cra0zy/monodevelop-run-installer/ might work if you are very lucky. Make sure that you have the latest version of Mono (from alpha update channel) and .NET Core SDK. Make sure to follow `FrameworkPathOverride` workaround from https://github.com/dotnet/sdk/issues/335 + +### Building and running Avalonia in Rider + +For Linux/OSX you'll probably need to apply workaround from https://github.com/dotnet/sdk/issues/335 + +Just add `export FrameworkPathOverride=/usr/lib/mono/4.6.1-api` (or `export FrameworkPathOverride=/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api` for OSX) -Finally, select the `Debug | Mono` or `Release | Mono` build configuration and you should be good to -go! From d4048de9947ace1ca953670a555516f626bc4b4e Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 Sep 2017 15:58:28 +0300 Subject: [PATCH 67/75] Don't try to load library with path == null --- src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs index cf065b86dc..764cbfd6b6 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs @@ -104,14 +104,16 @@ namespace Avalonia.Gtk3.Interop var path = Custom?.Lookup(dll); if (path == null && Custom?.BasePath != null) path = Path.Combine(Custom.BasePath, name); - - try - { - return loader.LoadLibrary(path); - } - catch (Exception e) + if (path != null) { - exceptions.Add(e); + try + { + return loader.LoadLibrary(path); + } + catch (Exception e) + { + exceptions.Add(e); + } } throw new AggregateException("Unable to load " + dll, exceptions); } From b013caee699756394053328ed504799eb6b8ef97 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 Sep 2017 15:59:13 +0300 Subject: [PATCH 68/75] Add .idea to .gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 1d74816204..5fa78979c3 100644 --- a/.gitignore +++ b/.gitignore @@ -159,6 +159,12 @@ $RECYCLE.BIN/ *.userprefs *.nugetreferenceswitcher + +################# +## Rider +################# +.idea + ################# ## Cake ################# From 48b5d8f593d8f2e733987b0af2ae9f379c0a2199 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Sep 2017 12:16:09 -0500 Subject: [PATCH 69/75] Update ReactiveUI version to a version available in NuGet --- build/ReactiveUI.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index de8d1d2104..1e2ad69046 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + From 8169851d7c3ba2cdb6ab7e99f8ee96f6320ff1db Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Sep 2017 12:39:16 -0500 Subject: [PATCH 70/75] Fix Rx version and clean up build warnings. Enable ReactiveUI in ControlCatalog.NetCore to ensure that everything works in the .NET Core build. --- build/Rx.props | 12 ++++----- samples/ControlCatalog.NetCore/Program.cs | 1 + .../Avalonia.ReactiveUI.csproj | 26 ------------------- 3 files changed, 7 insertions(+), 32 deletions(-) diff --git a/build/Rx.props b/build/Rx.props index e88ab1ec37..323026f5e2 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -1,10 +1,10 @@  - - - - - - + + + + + + diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 7c67a239e6..30df53980c 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -16,6 +16,7 @@ namespace ControlCatalog.NetCore else AppBuilder.Configure() .UsePlatformDetect() + .UseReactiveUI() .Start(); } diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index d523f71416..f9e7f26904 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -3,34 +3,8 @@ netstandard1.3 false - - true - full - false - bin\Debug\ - TRACE;DEBUG;MONO PORTABLE;NETSTANDARD1_1 - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE;MONO PORTABLE;RELEASE;NETSTANDARD1_1 - prompt - 4 - - - - - - - - - - From d1c85d7a3a06ec108359fc6110bd5b159f29aa34 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 6 Sep 2017 23:45:47 +0200 Subject: [PATCH 71/75] Prevent possible null ref warnings. --- .../MarkupExtensions/DynamicResourceExtension.cs | 2 +- .../MarkupExtensions/StaticResourceExtension.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 5e421b7e73..231778be09 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -59,7 +59,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions private T GetAnchor(ITypeDescriptorContext context) where T : class { - var schemaContext = context.GetService()?.SchemaContext; + var schemaContext = context.GetService().SchemaContext; var ambientProvider = context.GetService(); var xamlType = schemaContext.GetXamlType(typeof(T)); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index 4764677ede..9089a13656 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -29,7 +29,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions public override object ProvideValue(IServiceProvider serviceProvider) { var context = (ITypeDescriptorContext)serviceProvider; - var schemaContext = context.GetService()?.SchemaContext; + var schemaContext = context.GetService().SchemaContext; var ambientProvider = context.GetService(); var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceNode)); var ambientValues = ambientProvider.GetAllAmbientValues(resourceProviderType); From 743262ec1d7a56dee8cd1608717350547321f9cc Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Thu, 7 Sep 2017 10:43:19 +0200 Subject: [PATCH 72/75] Reformatted Native.D --- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 27 +++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index ba6a6ace92..ffc1d380a3 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -59,14 +59,17 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_widget_set_double_buffered(GtkWidget gtkWidget, bool value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_widget_set_events(GtkWidget gtkWidget, uint flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_screen_get_height(IntPtr screen); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_screen_get_width(IntPtr screen); @@ -75,6 +78,7 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height); @@ -90,35 +94,46 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_skip_taskbar_hint(GtkWindow gtkWindow, bool setting); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate bool gtk_window_get_skip_taskbar_hint(GtkWindow gtkWindow); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate bool gtk_window_get_skip_pager_hint(GtkWindow gtkWindow); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_get_size(GtkWindow gtkWindow, out int width, out int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_resize(GtkWindow gtkWindow, int width, int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_set_size_request(GtkWidget widget, int width, int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_set_default_size(GtkWindow gtkWindow, int width, int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_get_position(GtkWindow gtkWindow, out int x, out int y); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate GtkFileChooser gtk_file_chooser_dialog_new(Utf8Buffer title, GtkWindow parent, GtkFileChooserAction action, IntPtr ignore); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public unsafe delegate GSList* gtk_file_chooser_get_filenames(GtkFileChooser chooser); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_file_chooser_set_select_multiple(GtkFileChooser chooser, bool allow); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_file_chooser_set_filename(GtkFileChooser chooser, Utf8Buffer file); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id); @@ -215,7 +230,6 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_clipboard_clear(IntPtr clipboard); - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error); @@ -238,21 +252,27 @@ namespace Avalonia.Gtk3.Interop public delegate bool gdk_pixbuf_save_to_bufferv(Pixbuf pixbuf, out IntPtr buffer, out IntPtr buffer_size, Utf8Buffer type, IntPtr option_keys, IntPtr option_values, out IntPtr error); - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate void g_object_unref(IntPtr instance); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate void g_object_ref(GObject instance); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_handler_disconnect(GObject instance, ulong connectionId); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] public delegate ulong g_free(IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] public unsafe delegate void g_slist_free(GSList* data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)] public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback); @@ -328,8 +348,7 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_clipboard_request_text GtkClipboardRequestText; public static D.gtk_clipboard_set_text GtkClipboardSetText; public static D.gtk_clipboard_clear GtkClipboardRequestClear; - - + public static D.gtk_im_multicontext_new GtkImMulticontextNew; public static D.gtk_im_context_filter_keypress GtkImContextFilterKeypress; public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow; From 025d630f778be33a77b0bf2f8d5c9e0fa62ed307 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Thu, 7 Sep 2017 13:07:49 +0200 Subject: [PATCH 73/75] Renamed 'HasTaskbarIcon' to 'ShowInTaskbar' --- src/Avalonia.Controls/Window.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 37f7c6d47f..9c0245a714 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -69,8 +69,8 @@ namespace Avalonia.Controls /// /// Enables or disables the taskbar icon /// - public static readonly StyledProperty HasTaskbarIconProperty = - AvaloniaProperty.Register(nameof(HasTaskbarIcon), true); + public static readonly StyledProperty ShowInTaskbarProperty = + AvaloniaProperty.Register(nameof(ShowInTaskbar), true); /// /// Defines the property. @@ -98,7 +98,7 @@ namespace Avalonia.Controls HasSystemDecorationsProperty.Changed.AddClassHandler( (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); - HasTaskbarIconProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); + ShowInTaskbarProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); } @@ -173,10 +173,10 @@ namespace Avalonia.Controls /// Enables or disables the taskbar icon /// /// - public bool HasTaskbarIcon + public bool ShowInTaskbar { - get { return GetValue(HasTaskbarIconProperty); } - set { SetValue(HasTaskbarIconProperty, value); } + get { return GetValue(ShowInTaskbarProperty); } + set { SetValue(ShowInTaskbarProperty, value); } } /// From 8aa95d403e12a154f330bb18af7a3c8b0fe95430 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 7 Sep 2017 21:39:03 -0500 Subject: [PATCH 74/75] Fix compile error. --- samples/BindingTest/ViewModels/NestedCommandViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/BindingTest/ViewModels/NestedCommandViewModel.cs b/samples/BindingTest/ViewModels/NestedCommandViewModel.cs index 3eca481c68..886ecbed8e 100644 --- a/samples/BindingTest/ViewModels/NestedCommandViewModel.cs +++ b/samples/BindingTest/ViewModels/NestedCommandViewModel.cs @@ -12,7 +12,7 @@ namespace BindingTest.ViewModels { public NestedCommandViewModel() { - Command = ReactiveCommand.Create(); + Command = ReactiveCommand.Create(() => { }); } public ICommand Command { get; } From 2b71bf408af48b34db961f3859b24295e3698df1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 7 Sep 2017 22:48:17 -0500 Subject: [PATCH 75/75] Fix package spec for Avalonia.ReactiveUI --- packages.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages.cake b/packages.cake index 2f3ef62d69..82c21ae5aa 100644 --- a/packages.cake +++ b/packages.cake @@ -286,9 +286,9 @@ public class Packages }.Deps(new string[] {null}, "reactiveui"), Files = new [] { - new NuSpecContent { Source = "Avalonia.ReactiveUI.dll", Target = "lib/netstandard1.3" } + new NuSpecContent { Source = "Avalonia.ReactiveUI.dll", Target = "lib/netstandard2.0" } }, - BasePath = context.Directory("./src/Avalonia.ReactiveUI/bin/" + parameters.DirSuffix + "/netstandard1.3"), + BasePath = context.Directory("./src/Avalonia.ReactiveUI/bin/" + parameters.DirSuffix + "/netstandard2.0"), OutputDirectory = parameters.NugetRoot }, };