diff --git a/Perspex.sln b/Perspex.sln
index bc7e0560af..9cbaca445d 100644
--- a/Perspex.sln
+++ b/Perspex.sln
@@ -25,8 +25,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Styling", "src\Pers
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Themes.Default", "src\Perspex.Themes.Default\Perspex.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Application", "src\Perspex.Application\Perspex.Application.csproj", "{799A7BB5-3C2C-48B6-85A7-406A12C420DA}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Diagnostics", "src\Perspex.Diagnostics\Perspex.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Animation", "src\Perspex.Animation\Perspex.Animation.csproj", "{D211E587-D8BC-45B9-95A4-F297C8FA5200}"
@@ -99,6 +97,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Markup.UnitTests", "tests\Perspex.Markup.UnitTests\Perspex.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BindingTest", "samples\BindingTest\BindingTest.csproj", "{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9} = {B61B66A3-B82D-4875-8001-89D3394FE0C9}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlTestApplicationPcl", "samples\XamlTestApplicationPcl\XamlTestApplicationPcl.csproj", "{EA113F1A-D8D7-4142-9948-353270E7EBAE}"
EndProject
@@ -142,6 +143,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.UnitTests", "tests\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Benchmarks", "tests\Perspex.Benchmarks\Perspex.Benchmarks.csproj", "{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Logging.Serilog", "src\Perspex.Logging.Serilog\Perspex.Logging.Serilog.csproj", "{B61B66A3-B82D-4875-8001-89D3394FE0C9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.DesignerSupport", "src\Perspex.DesignerSupport\Perspex.DesignerSupport.csproj", "{799A7BB5-3C2C-48B6-85A7-406A12C420DA}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
@@ -150,20 +154,20 @@ Global
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4
- src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
+ src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{78345174-5b52-4a14-b9fd-d5f2428137f0}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{ff69b927-c545-49ae-8e16-3d14d621aa12}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 4
- src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
+ src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{8c923867-8a8f-4f6b-8b80-47d9e8436166}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{e3a1060b-50d0-44e8-88b6-f44ef2e5bd72}*SharedItemsImports = 4
- src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
+ src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{e1aa3dbf-9056-4530-9376-18119a7a3ffe}*SharedItemsImports = 4
EndGlobalSection
@@ -422,30 +426,6 @@ Global
{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhone.Build.0 = Release|Any CPU
{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.Build.0 = Debug|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.Build.0 = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -1321,6 +1301,54 @@ Global
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhone.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhone.Build.0 = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.Build.0 = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/docs/docfx.json b/docs/docfx.json
index 8775904c6a..4e908b97da 100644
--- a/docs/docfx.json
+++ b/docs/docfx.json
@@ -50,6 +50,6 @@
"_appTitle": "Perspex Website"
},
"dest": "_site",
- "template": "default"
+ "template": [ "default", "template"]
}
}
diff --git a/docs/spec/logging.md b/docs/spec/logging.md
new file mode 100644
index 0000000000..91d35023e4
--- /dev/null
+++ b/docs/spec/logging.md
@@ -0,0 +1,54 @@
+# Perspex Logging
+
+Perspex uses [Serilog](https://github.com/serilog/serilog) for logging via
+the Perspex.Logging.Serilog assembly.
+
+The following method should be present in your App.xaml.cs file:
+
+```C#
+private void InitializeLogging()
+{
+#if DEBUG
+ SerilogLogger.Initialize(new LoggerConfiguration()
+ .MinimumLevel.Warning()
+ .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
+ .CreateLogger());
+#endif
+}
+```
+
+By default, this logging setup will write log messages with a severity of
+`Warning` or higher to `System.Diagnostics.Trace`. See the [Serilog
+documentation](https://github.com/serilog/serilog/wiki/Configuration-Basics)
+for more information on the options here.
+
+## Areas
+
+Each Perspex log message has an "Area" that can be used to filter the log to
+include only the type of events that you are interested in. These are currently:
+
+- Property
+- Binding
+- Visual
+- Layout
+- Control
+
+To limit the log output to a specific area you can add a filter; for example
+to enable verbose logging but only about layout:
+
+```C#
+SerilogLogger.Initialize(new LoggerConfiguration()
+ .Filter.ByIncludingOnly(Matching.WithProperty("Area", LogArea.Layout))
+ .MinimumLevel.Verbose()
+ .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
+ .CreateLogger());
+```
+
+## Removing Serilog
+
+If you don't want a dependency on Serilog in your application, simply remove
+the reference to Perspex.Logging.Serilog and the code that initializes it. If
+you do however still want some kinda of logging, there are two steps:
+
+- Implement `Perspex.Logging.ILogSink`
+- Assign your implementation to `Logger.Sink`
diff --git a/docs/spec/styles.md b/docs/spec/styles.md
index 89d5348397..053d91b1e0 100644
--- a/docs/spec/styles.md
+++ b/docs/spec/styles.md
@@ -49,6 +49,16 @@ As in CSS, controls can be given *style classes* which can be used in selectors:
+Each control can be given 0 or more style classes. This is different to WPF
+where only a single style can be applied to a control: in Perspex any number
+of separate styles can be applied to a control. If more than one style affects
+a particular property, the style closest to the control will take precedence.
+
+Style classes can also be manipulated in code using the `Classes` collection:
+
+ control.Classes.Add("blue");
+ control.Classes.Remove("red");
+
## Pseudoclasses
Also as in CSS, controls can have pseudoclasses; these are classes that are
diff --git a/docs/spec/toc.yml b/docs/spec/toc.yml
index e5c40d7544..3075a03619 100644
--- a/docs/spec/toc.yml
+++ b/docs/spec/toc.yml
@@ -5,4 +5,6 @@
- name: Defining Properties
href: defining-properties.md
- name: Working with Properties
- href: working-with-properties.md
\ No newline at end of file
+ href: working-with-properties.md
+- name: Logging
+ href: logging.md
diff --git a/docs/template/partials/footer.tmpl.partial b/docs/template/partials/footer.tmpl.partial
new file mode 100644
index 0000000000..a94f934e92
--- /dev/null
+++ b/docs/template/partials/footer.tmpl.partial
@@ -0,0 +1,13 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
diff --git a/docs/tutorial/from-wpf.md b/docs/tutorial/from-wpf.md
new file mode 100644
index 0000000000..0b4fd82646
--- /dev/null
+++ b/docs/tutorial/from-wpf.md
@@ -0,0 +1,176 @@
+# Perspex for WPF Developers
+
+Perspex is in general very similar to WPF, but you will find differences. Here
+are the most common:
+
+## Styling
+
+The most obvious difference from other XAML frameworks is that Perspex uses a
+[CSS-like styling system](../spec/styles.md). Styles aren't stored in a
+`Resources` collection as in WPF, they are stored in a separate `Styles`
+collection:
+
+
+
+
+
+
+ Header
+
+
+## DataTemplates
+
+As styles aren't stored in `Resources`, neither are `DataTemplates` ([in fact
+there is no `Resources` collection](#resources)). Instead, `DataTemplates` are
+placed in a `DataTemplates` collection on each control (and on `Application`):
+
+
+
+
+
+
+
+
+
+
+
+
+
+`ItemsControl`s don't currently have an `ItemTemplate` property: instead just
+place the template for your items into the control's `DataTemplates`, e.g.
+
+
+
+
+
+
+
+
+
+Data templates in Perspex can also target interfaces and derived classes (which
+cannot be done in WPF) and so the order of `DataTemplate`s can be important:
+`DataTemplate`s within the same collection are evaluated in declaration order
+so you need to place them from most-specific to least-specific as you would in
+code.
+
+## HierachicalDataTemplate
+
+WPF's `HierarchicalDataTemplate` is called `TreeDataTemplate` in Perspex (as the
+former is difficult to type!). The two are almost entirely equivalent except
+that the `ItemTemplate` property is not present in Perspex.
+
+## UIElement, FrameworkElement and Control
+
+WPF's `UIElement` and `FrameworkElement` are non-templated control base classes,
+which roughly equate to the Perspex `Control` class. WPF's `Control` class on
+the other hand is a templated control - Perspex's equivalent of this is
+`TemplatedControl`.
+
+So to recap:
+
+- `UIElement`: `Control`
+- `FrameworkElement`: `Control`
+- `Control`: `TemplatedControl`
+
+## DependencyProperty
+
+The Perspex equivalent of `DependencyProperty` is `StyledProperty`, however
+Perspex [has a richer property system than WPF](../spec/defining-properties.md),
+and includes `DirectProperty` for turning standard CLR properties into Perspex
+properties. The common base class of `StyledProperty` and `DirectProperty`
+is `PerspexProperty`.
+
+# Resources
+
+There is no `Resources` collection on controls in Perspex, however `Style`s
+do have a `Resources` collection for style-related resources. These can be
+referred to using the `{StyleResource}` markup extension both inside and outside
+styles.
+
+For non-style-related resources, we suggest defining them in code and referring
+to them in markup using the `{Static}` markup extension. There are [various
+reasons](http://www.codemag.com/article/1501091) for this, but briefly:
+
+- Resources have to be parsed
+- The tree has to be traversed to find them
+- XAML doesn't handle immutable objects
+- XAML syntax can be long-winded compared to C#
+
+## Grid
+
+Column and row definitions can be specified in Perspex using strings, avoiding
+the clunky syntax in WPF:
+
+
+
+A common use of `Grid` in WPF is to stack two controls on top of each other.
+For this purpose in Perspex you can just use a `Panel` which is more lightweight
+than `Grid`.
+
+We don't yet support `SharedSizeScope` in `Grid`.
+
+## ItemsControl
+
+In WPF, `ItemsControl` and derived classes such as `ListBox` have two separate
+items properties: `Items` and `ItemsSource`. Perspex however just has a single
+one: `Items`.
+
+## Tunnelling Events
+
+Perspex has tunnelling events (unlike UWP!) but they're not exposed via
+separate `Preview` CLR event handlers. To subscribe to a tunnelling event you
+must call `AddHandler` with `RoutingStrategies.Tunnel`:
+
+```
+target.AddHandler(InputElement.KeyDownEvent, OnPreviewKeyDown, RoutingStrategies.Tunnel);
+
+void OnPreviewKeyDown(object sender, KeyEventArgs e)
+{
+ // Handler code
+}
+```
+
+## Class Handlers
+
+In WPF, class handlers for events can be added by calling
+[EventManager.RegisterClassHandler](https://msdn.microsoft.com/en-us/library/ms597875.aspx).
+An example of registering a class handler in WPF might be:
+
+ static MyControl()
+ {
+ EventManager.RegisterClassHandler(typeof(MyControl), MyEvent, HandleMyEvent));
+ }
+
+ private static void HandleMyEvent(object sender, RoutedEventArgs e)
+ {
+ }
+
+The equivalent of this in Perspex would be:
+
+ static MyControl()
+ {
+ MyEvent.AddClassHandler(x => x.HandleMyEvent);
+ }
+
+ private void HandleMyEvent(object sender, RoutedEventArgs e)
+ {
+ }
+
+Notice that in WPF you have to add the class handler as a static method, whereas
+in Perspex the class handler is not static: the notification is automatically
+directed to the correct instance.
+
+## PropertyChangedCallback
+
+Listening to changes on DependencyProperties in WPF can be complex. When you
+register a `DependencyProperty` you can supply a static `PropertyChangedCallback`
+but if you want to listen to changes from elsewhere [things can get complicated
+and error-prone](http://stackoverflow.com/questions/23682232).
+
+In Perspex, there is no `PropertyChangedCallback` at the time of registration,
+instead a class listener is [added to the control's static constructor in much
+the same way that event class listeners are added](../spec/working-with-properties.md#subscribing-to-a-property-on-any-object).
diff --git a/nuget/build-version.ps1 b/nuget/build-version.ps1
index 59031527d5..5561631858 100644
--- a/nuget/build-version.ps1
+++ b/nuget/build-version.ps1
@@ -27,12 +27,12 @@ mkdir $ios
Copy-Item ..\src\Perspex.Animation\bin\Release\Perspex.Animation.dll $lib
Copy-Item ..\src\Perspex.Animation\bin\Release\Perspex.Animation.xml $lib
-Copy-Item ..\src\Perspex.Application\bin\Release\Perspex.Application.dll $lib
-Copy-Item ..\src\Perspex.Application\bin\Release\Perspex.Application.xml $lib
Copy-Item ..\src\Perspex.Base\bin\Release\Perspex.Base.dll $lib
Copy-Item ..\src\Perspex.Base\bin\Release\Perspex.Base.xml $lib
Copy-Item ..\src\Perspex.Controls\bin\Release\Perspex.Controls.dll $lib
Copy-Item ..\src\Perspex.Controls\bin\Release\Perspex.Controls.xml $lib
+Copy-Item ..\src\Perspex.DesignerSupport\bin\Release\Perspex.DesignerSupport.dll $lib
+Copy-Item ..\src\Perspex.DesignerSupport\bin\Release\Perspex.DesignerSupport.xml $lib
Copy-Item ..\src\Perspex.Diagnostics\bin\Release\\Perspex.Diagnostics.dll $lib
Copy-Item ..\src\Perspex.Diagnostics\bin\Release\\Perspex.Diagnostics.xml $lib
Copy-Item ..\src\Perspex.Input\bin\Release\Perspex.Input.dll $lib
@@ -41,6 +41,8 @@ Copy-Item ..\src\Perspex.Interactivity\bin\Release\Perspex.Interactivity.dll $li
Copy-Item ..\src\Perspex.Interactivity\bin\Release\Perspex.Interactivity.xml $lib
Copy-Item ..\src\Perspex.Layout\bin\Release\Perspex.Layout.dll $lib
Copy-Item ..\src\Perspex.Layout\bin\Release\Perspex.Layout.xml $lib
+Copy-Item ..\src\Perspex.Logging.Serilog\bin\Release\Perspex.Logging.Serilog.dll $lib
+Copy-Item ..\src\Perspex.Logging.Serilog\bin\Release\Perspex.Logging.Serilog.xml $lib
Copy-Item ..\src\Perspex.SceneGraph\bin\Release\Perspex.SceneGraph.dll $lib
Copy-Item ..\src\Perspex.SceneGraph\bin\Release\Perspex.SceneGraph.xml $lib
Copy-Item ..\src\Perspex.Styling\bin\Release\Perspex.Styling.dll $lib
diff --git a/samples/BindingTest/App.config b/samples/BindingTest/App.config
index 8324aa6ff1..d1428ad712 100644
--- a/samples/BindingTest/App.config
+++ b/samples/BindingTest/App.config
@@ -1,6 +1,6 @@
-
+
-
+
-
\ No newline at end of file
+
diff --git a/samples/BindingTest/App.xaml.cs b/samples/BindingTest/App.xaml.cs
index 80fcaf8433..5ffac249e9 100644
--- a/samples/BindingTest/App.xaml.cs
+++ b/samples/BindingTest/App.xaml.cs
@@ -2,9 +2,9 @@
using Perspex;
using Perspex.Controls;
using Perspex.Diagnostics;
+using Perspex.Logging.Serilog;
using Perspex.Markup.Xaml;
using Serilog;
-using Serilog.Filters;
namespace BindingTest
{
@@ -15,13 +15,7 @@ namespace BindingTest
RegisterServices();
InitializeSubsystems((int)Environment.OSVersion.Platform);
InitializeComponent();
-
- Log.Logger = new LoggerConfiguration()
- .Filter.ByIncludingOnly(Matching.WithProperty("Area", "Property"))
- .Filter.ByIncludingOnly(Matching.WithProperty("Property", "Text"))
- .MinimumLevel.Verbose()
- .WriteTo.Trace(outputTemplate: "[{Id:X8}] [{SourceContext}] {Message}")
- .CreateLogger();
+ InitializeLogging();
}
public static void AttachDevTools(Window window)
@@ -41,5 +35,15 @@ namespace BindingTest
{
PerspexXamlLoader.Load(this);
}
+
+ private void InitializeLogging()
+ {
+#if DEBUG
+ SerilogLogger.Initialize(new LoggerConfiguration()
+ .MinimumLevel.Warning()
+ .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
+ .CreateLogger());
+#endif
+ }
}
}
diff --git a/samples/BindingTest/BindingTest.csproj b/samples/BindingTest/BindingTest.csproj
index 406c5b1cfb..ed4900a402 100644
--- a/samples/BindingTest/BindingTest.csproj
+++ b/samples/BindingTest/BindingTest.csproj
@@ -9,9 +9,10 @@
Properties
BindingTest
BindingTest
- v4.6
+ v4.5
512
true
+
AnyCPU
@@ -111,10 +112,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base
@@ -123,6 +120,10 @@
{d2221c82-4a25-4583-9b43-d791e3f6820c}
Perspex.Controls
+
+ {799a7bb5-3c2c-48b6-85a7-406a12c420da}
+ Perspex.DesignerSupport
+
{7062ae20-5dcc-4442-9645-8195bdece63e}
Perspex.Diagnostics
@@ -139,6 +140,10 @@
{42472427-4774-4c81-8aff-9f27b8e31721}
Perspex.Layout
+
+ {b61b66a3-b82d-4875-8001-89d3394fe0c9}
+ Perspex.Logging.Serilog
+
{6417b24e-49c2-4985-8db2-3ab9d898ec91}
Perspex.ReactiveUI
diff --git a/samples/ControlCatalog/App.config b/samples/ControlCatalog/App.config
index 2e292d1c8a..7ef9d715f8 100644
--- a/samples/ControlCatalog/App.config
+++ b/samples/ControlCatalog/App.config
@@ -1,14 +1,14 @@
-
+
-
+
-
-
+
+
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index ac9f563c8c..2930fa746a 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/samples/ControlCatalog/App.xaml.cs
@@ -4,6 +4,7 @@ using Perspex;
using Perspex.Controls;
using Perspex.Diagnostics;
using Perspex.Markup.Xaml;
+using Perspex.Logging.Serilog;
using Serilog;
namespace ControlCatalog
@@ -41,10 +42,10 @@ namespace ControlCatalog
private void InitializeLogging()
{
#if DEBUG
- Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Error()
- .WriteTo.Trace(outputTemplate: "{Message}")
- .CreateLogger();
+ SerilogLogger.Initialize(new LoggerConfiguration()
+ .MinimumLevel.Warning()
+ .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
+ .CreateLogger());
#endif
}
diff --git a/samples/ControlCatalog/Assets/github_icon.png b/samples/ControlCatalog/Assets/github_icon.png
new file mode 100644
index 0000000000..ed4f82f847
Binary files /dev/null and b/samples/ControlCatalog/Assets/github_icon.png differ
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index be2b312204..087998c805 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -9,9 +9,10 @@
Properties
ControlCatalog
ControlCatalog
- v4.6
+ v4.5
512
true
+
AnyCPU
@@ -60,6 +61,27 @@
MainWindow.xaml
+
+ ImagePage.xaml
+
+
+ ExpanderPage.xaml
+
+
+ RadioButtonPage.xaml
+
+
+ MenuPage.xaml
+
+
+ ToolTipPage.xaml
+
+
+ TextBoxPage.xaml
+
+
+ LayoutTransformControlPage.xaml
+
CheckBoxPage.xaml
@@ -136,10 +158,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base
@@ -148,6 +166,10 @@
{d2221c82-4a25-4583-9b43-d791e3f6820c}
Perspex.Controls
+
+ {799a7bb5-3c2c-48b6-85a7-406a12c420da}
+ Perspex.DesignerSupport
+
{7062ae20-5dcc-4442-9645-8195bdece63e}
Perspex.Diagnostics
@@ -168,6 +190,10 @@
{42472427-4774-4c81-8aff-9f27b8e31721}
Perspex.Layout
+
+ {b61b66a3-b82d-4875-8001-89d3394fe0c9}
+ Perspex.Logging.Serilog
+
{6417b24e-49c2-4985-8db2-3ab9d898ec91}
Perspex.ReactiveUI
@@ -200,6 +226,46 @@
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
+
+ PreserveNewest
+
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
\ No newline at end of file
diff --git a/src/Perspex.Logging.Serilog/Properties/AssemblyInfo.cs b/src/Perspex.Logging.Serilog/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..91c95c4982
--- /dev/null
+++ b/src/Perspex.Logging.Serilog/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Perspex.Serilog")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Perspex.Serilog")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Perspex.Logging.Serilog/SerilogLogger.cs b/src/Perspex.Logging.Serilog/SerilogLogger.cs
new file mode 100644
index 0000000000..beb5989d55
--- /dev/null
+++ b/src/Perspex.Logging.Serilog/SerilogLogger.cs
@@ -0,0 +1,44 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System.Collections.Generic;
+using Serilog;
+using PerspexLogEventLevel = Perspex.Logging.LogEventLevel;
+using SerilogLogEventLevel = Serilog.Events.LogEventLevel;
+
+namespace Perspex.Logging.Serilog
+{
+ public class SerilogLogger : ILogSink
+ {
+ private readonly ILogger _output;
+ private readonly Dictionary _areas = new Dictionary();
+
+ public SerilogLogger(ILogger output)
+ {
+ _output = output;
+ }
+
+ public static void Initialize(ILogger output)
+ {
+ Logger.Sink = new SerilogLogger(output);
+ }
+
+ public void Log(
+ PerspexLogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ params object[] propertyValues)
+ {
+ ILogger areaLogger;
+
+ if (!_areas.TryGetValue(area, out areaLogger))
+ {
+ areaLogger = _output.ForContext("Area", area);
+ _areas.Add(area, areaLogger);
+ }
+
+ areaLogger.Write((SerilogLogEventLevel)level, messageTemplate, propertyValues);
+ }
+ }
+}
diff --git a/src/Perspex.Logging.Serilog/packages.config b/src/Perspex.Logging.Serilog/packages.config
new file mode 100644
index 0000000000..dbc72b5331
--- /dev/null
+++ b/src/Perspex.Logging.Serilog/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Animation/CrossFade.cs b/src/Perspex.SceneGraph/Animation/CrossFade.cs
index 722082801d..65697d4245 100644
--- a/src/Perspex.SceneGraph/Animation/CrossFade.cs
+++ b/src/Perspex.SceneGraph/Animation/CrossFade.cs
@@ -89,7 +89,10 @@ namespace Perspex.Animation
from.Opacity = 1;
}
- to.Opacity = 1;
+ if (to != null)
+ {
+ to.Opacity = 1;
+ }
}
///
diff --git a/src/Perspex.SceneGraph/Matrix.cs b/src/Perspex.SceneGraph/Matrix.cs
index baf38fb767..121ad1667d 100644
--- a/src/Perspex.SceneGraph/Matrix.cs
+++ b/src/Perspex.SceneGraph/Matrix.cs
@@ -53,6 +53,11 @@ namespace Perspex
///
public bool IsIdentity => Equals(Identity);
+ ///
+ /// HasInverse Property - returns true if this matrix is invertable, false otherwise.
+ ///
+ public bool HasInverse => GetDeterminant() != 0;
+
///
/// The first element of the first row
///
@@ -209,6 +214,7 @@ namespace Perspex
return (_m11 * _m22) - (_m12 * _m21);
}
+
///
/// Returns a boolean indicating whether the matrix is equal to the other given matrix.
///
diff --git a/src/Perspex.SceneGraph/Media/Brush.cs b/src/Perspex.SceneGraph/Media/Brush.cs
index 6cf90502e3..6d53dd4092 100644
--- a/src/Perspex.SceneGraph/Media/Brush.cs
+++ b/src/Perspex.SceneGraph/Media/Brush.cs
@@ -10,7 +10,7 @@ namespace Perspex.Media
///
/// Describes how an area is painted.
///
- public abstract class Brush : PerspexObject
+ public abstract class Brush : PerspexObject, IBrush
{
///
/// Defines the property.
@@ -32,7 +32,7 @@ namespace Perspex.Media
///
/// The brush string.
/// The .
- public static Brush Parse(string s)
+ public static IBrush Parse(string s)
{
if (s[0] == '#')
{
@@ -46,7 +46,7 @@ namespace Perspex.Media
if (member != null)
{
- return (Brush)member.GetValue(null);
+ return (IBrush)member.GetValue(null);
}
else
{
diff --git a/src/Perspex.SceneGraph/Media/DrawingContext.cs b/src/Perspex.SceneGraph/Media/DrawingContext.cs
index 41a599858e..80d4be26ca 100644
--- a/src/Perspex.SceneGraph/Media/DrawingContext.cs
+++ b/src/Perspex.SceneGraph/Media/DrawingContext.cs
@@ -83,7 +83,7 @@ namespace Perspex.Media
/// The fill brush.
/// The stroke pen.
/// The geometry.
- public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) => _impl.DrawGeometry(brush, pen, geometry);
+ public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry) => _impl.DrawGeometry(brush, pen, geometry);
///
/// Draws the outline of a rectangle.
@@ -100,7 +100,7 @@ namespace Perspex.Media
/// The foreground brush.
/// The upper-left corner of the text.
/// The text.
- public void DrawText(Brush foreground, Point origin, FormattedText text)
+ public void DrawText(IBrush foreground, Point origin, FormattedText text)
=> _impl.DrawText(foreground, origin, text);
///
@@ -109,7 +109,7 @@ namespace Perspex.Media
/// The brush.
/// The rectangle bounds.
/// The corner radius.
- public void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0.0f)
+ public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f)
=> _impl.FillRectangle(brush, rect, cornerRadius);
public struct PushedState : IDisposable
@@ -178,7 +178,7 @@ namespace Perspex.Media
/// The opacity.
/// A disposable used to undo the opacity.
public PushedState PushOpacity(double opacity)
- //TODO: Elimintate platform-specific push opacity call
+ //TODO: Eliminate platform-specific push opacity call
{
_impl.PushOpacity(opacity);
return new PushedState(this, PushedState.PushedStateType.Opacity);
diff --git a/src/Perspex.SceneGraph/Media/FormattedText.cs b/src/Perspex.SceneGraph/Media/FormattedText.cs
index e2db938612..b727a4fab9 100644
--- a/src/Perspex.SceneGraph/Media/FormattedText.cs
+++ b/src/Perspex.SceneGraph/Media/FormattedText.cs
@@ -206,7 +206,7 @@ namespace Perspex.Media
/// The brush.
/// The start of the text range.
/// The length of the text range.
- public void SetForegroundBrush(Brush brush, int startIndex, int length)
+ public void SetForegroundBrush(IBrush brush, int startIndex, int length)
{
CheckDisposed();
PlatformImpl.SetForegroundBrush(brush, startIndex, length);
diff --git a/src/Perspex.SceneGraph/Media/IBrush.cs b/src/Perspex.SceneGraph/Media/IBrush.cs
new file mode 100644
index 0000000000..368b01c43a
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/IBrush.cs
@@ -0,0 +1,16 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+namespace Perspex.Media
+{
+ ///
+ /// Describes how an area is painted.
+ ///
+ public interface IBrush
+ {
+ ///
+ /// Gets the opacity of the brush.
+ ///
+ double Opacity { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/IDrawingContext.cs b/src/Perspex.SceneGraph/Media/IDrawingContext.cs
index 4d144b33e0..2667856dc3 100644
--- a/src/Perspex.SceneGraph/Media/IDrawingContext.cs
+++ b/src/Perspex.SceneGraph/Media/IDrawingContext.cs
@@ -39,7 +39,7 @@ namespace Perspex.Media
/// The fill brush.
/// The stroke pen.
/// The geometry.
- void DrawGeometry(Brush brush, Pen pen, Geometry geometry);
+ void DrawGeometry(IBrush brush, Pen pen, Geometry geometry);
///
/// Draws the outline of a rectangle.
@@ -55,7 +55,7 @@ namespace Perspex.Media
/// The foreground brush.
/// The upper-left corner of the text.
/// The text.
- void DrawText(Brush foreground, Point origin, FormattedText text);
+ void DrawText(IBrush foreground, Point origin, FormattedText text);
///
/// Draws a filled rectangle.
@@ -63,7 +63,7 @@ namespace Perspex.Media
/// The brush.
/// The rectangle bounds.
/// The corner radius.
- void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0.0f);
+ void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f);
///
/// Pushes a clip rectange.
diff --git a/src/Perspex.SceneGraph/Media/ISolidColorBrush.cs b/src/Perspex.SceneGraph/Media/ISolidColorBrush.cs
new file mode 100644
index 0000000000..56bd1b6f93
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/ISolidColorBrush.cs
@@ -0,0 +1,16 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+namespace Perspex.Media
+{
+ ///
+ /// Fills an area with a solid color.
+ ///
+ public interface ISolidColorBrush : IBrush
+ {
+ ///
+ /// Gets the color of the brush.
+ ///
+ Color Color { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/Mutable/SolidColorBrush.cs b/src/Perspex.SceneGraph/Media/Mutable/SolidColorBrush.cs
index 73272e9ee6..928a9bd22c 100644
--- a/src/Perspex.SceneGraph/Media/Mutable/SolidColorBrush.cs
+++ b/src/Perspex.SceneGraph/Media/Mutable/SolidColorBrush.cs
@@ -10,7 +10,7 @@ namespace Perspex.Media.Mutable
/// This is a mutable version of the normal immutable
/// for use in XAML. XAML really needs support for immutable data...
///
- public class SolidColorBrush : Brush
+ public class SolidColorBrush : Brush, ISolidColorBrush
{
public static readonly DirectProperty ColorProperty =
PerspexProperty.RegisterDirect(
diff --git a/src/Perspex.SceneGraph/Media/Pen.cs b/src/Perspex.SceneGraph/Media/Pen.cs
index 489860a87a..d881ef53f4 100644
--- a/src/Perspex.SceneGraph/Media/Pen.cs
+++ b/src/Perspex.SceneGraph/Media/Pen.cs
@@ -20,7 +20,7 @@ namespace Perspex.Media
/// The line join.
/// The miter limit.
public Pen(
- Brush brush,
+ IBrush brush,
double thickness = 1.0,
DashStyle dashStyle = null,
PenLineCap dashCap = PenLineCap.Flat,
@@ -73,7 +73,7 @@ namespace Perspex.Media
///
/// Gets the brush used to draw the stroke.
///
- public Brush Brush { get; }
+ public IBrush Brush { get; }
///
/// Gets the stroke thickness.
diff --git a/src/Perspex.SceneGraph/Media/ScaleTransform.cs b/src/Perspex.SceneGraph/Media/ScaleTransform.cs
new file mode 100644
index 0000000000..9e0f1ecb3a
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/ScaleTransform.cs
@@ -0,0 +1,69 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Perspex.Media
+{
+ ///
+ /// Scale an .
+ ///
+ public class ScaleTransform : Transform
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty ScaleXProperty =
+ PerspexProperty.Register(nameof(ScaleX), 1);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty ScaleYProperty =
+ PerspexProperty.Register(nameof(ScaleY), 1);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ScaleTransform()
+ {
+ this.GetObservable(ScaleXProperty).Subscribe(_ => RaiseChanged());
+ this.GetObservable(ScaleYProperty).Subscribe(_ => RaiseChanged());
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// ScaleX
+ /// ScaleY
+ public ScaleTransform(double scaleX, double scaleY)
+ : this()
+ {
+ ScaleX = scaleX;
+ ScaleY = scaleY;
+ }
+
+ ///
+ /// Gets or sets the ScaleX property.
+ ///
+ public double ScaleX
+ {
+ get { return GetValue(ScaleXProperty); }
+ set { SetValue(ScaleXProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the ScaleY property.
+ ///
+ public double ScaleY
+ {
+ get { return GetValue(ScaleYProperty); }
+ set { SetValue(ScaleYProperty, value); }
+ }
+
+ ///
+ /// Gets the tranform's .
+ ///
+ public override Matrix Value => Matrix.CreateScale(ScaleX, ScaleY);
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/SolidColorBrush.cs b/src/Perspex.SceneGraph/Media/SolidColorBrush.cs
index 48423de385..4be96eadd7 100644
--- a/src/Perspex.SceneGraph/Media/SolidColorBrush.cs
+++ b/src/Perspex.SceneGraph/Media/SolidColorBrush.cs
@@ -6,15 +6,17 @@ namespace Perspex.Media
///
/// Fills an area with a solid color.
///
- public class SolidColorBrush : Brush
+ public class SolidColorBrush : ISolidColorBrush
{
///
/// Initializes a new instance of the class.
///
/// The color to use.
- public SolidColorBrush(Color color)
+ /// The opacity of the brush.
+ public SolidColorBrush(Color color, double opacity = 1)
{
Color = color;
+ Opacity = opacity;
}
///
@@ -29,10 +31,12 @@ namespace Perspex.Media
///
/// Gets the color of the brush.
///
- public Color Color
- {
- get;
- }
+ public Color Color { get; }
+
+ ///
+ /// Gets the opacity of the brush.
+ ///
+ public double Opacity { get; }
///
/// Returns a string representation of the brush.
diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
index 17ee3e98db..ea84ae710e 100644
--- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
+++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
@@ -72,6 +72,8 @@
+
+
@@ -80,6 +82,7 @@
+
@@ -139,10 +142,6 @@
-
- ..\..\packages\Serilog.1.5.14\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll
- True
-
..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll
diff --git a/src/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs b/src/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs
index 85a674c330..306e710f3f 100644
--- a/src/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs
+++ b/src/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs
@@ -61,6 +61,6 @@ namespace Perspex.Platform
/// The brush.
/// The start of the text range.
/// The length of the text range.
- void SetForegroundBrush(Brush brush, int startIndex, int length);
+ void SetForegroundBrush(IBrush brush, int startIndex, int length);
}
}
diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs
index 63b7bf14f1..3dfe3412c8 100644
--- a/src/Perspex.SceneGraph/Visual.cs
+++ b/src/Perspex.SceneGraph/Visual.cs
@@ -2,19 +2,17 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
-using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Animation;
using Perspex.Collections;
using Perspex.Data;
+using Perspex.Logging;
using Perspex.Media;
using Perspex.Platform;
using Perspex.Rendering;
using Perspex.VisualTree;
-using Serilog;
-using Serilog.Core.Enrichers;
namespace Perspex
{
@@ -79,7 +77,6 @@ namespace Perspex
private Rect _bounds;
private IVisual _visualParent;
- private readonly ILogger _visualLogger;
///
/// Initializes static members of the class.
@@ -95,13 +92,6 @@ namespace Perspex
///
public Visual()
{
- _visualLogger = Log.ForContext(new[]
- {
- new PropertyEnricher("Area", "Visual"),
- new PropertyEnricher("SourceContext", GetType()),
- new PropertyEnricher("Id", GetHashCode()),
- });
-
var visualChildren = new PerspexList();
visualChildren.ResetBehavior = ResetBehavior.Remove;
visualChildren.Validate = ValidateLogicalChild;
@@ -231,7 +221,7 @@ namespace Perspex
///
/// Gets the root of the visual tree, if the control is attached to a visual tree.
///
- IRenderRoot IVisual.VisualRoot { get; }
+ IRenderRoot IVisual.VisualRoot => VisualRoot;
///
/// Invalidates the visual and queues a repaint.
@@ -292,38 +282,74 @@ namespace Perspex
}
///
- /// Called when the control is added to a visual tree.
+ /// Calls the method
+ /// for this control and all of its visual descendents.
///
/// The event args.
- ///
- /// It is vital that if you override this method you call the base implementation;
- /// failing to do so will cause numerous features to not work as expected.
- ///
- protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
+ Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
+
+ VisualRoot = e.Root;
+
if (RenderTransform != null)
{
RenderTransform.Changed += RenderTransformChanged;
}
- AttachedToVisualTree?.Invoke(this, e);
+ OnAttachedToVisualTree(e);
+
+ if (VisualChildren != null)
+ {
+ foreach (Visual child in VisualChildren.OfType())
+ {
+ child.OnAttachedToVisualTreeCore(e);
+ }
+ }
}
///
- /// Called when the control is removed from a visual tree.
+ /// Calls the method
+ /// for this control and all of its visual descendents.
///
/// The event args.
- ///
- /// It is vital that if you override this method you call the base implementation;
- /// failing to do so will cause numerous features to not work as expected.
- ///
- protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
+ Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
+
+ VisualRoot = null;
+
if (RenderTransform != null)
{
RenderTransform.Changed -= RenderTransformChanged;
}
+ OnDetachedFromVisualTree(e);
+
+ if (VisualChildren != null)
+ {
+ foreach (Visual child in VisualChildren.OfType())
+ {
+ child.OnDetachedFromVisualTreeCore(e);
+ }
+ }
+ }
+
+ ///
+ /// Called when the control is added to a visual tree.
+ ///
+ /// The event args.
+ protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ AttachedToVisualTree?.Invoke(this, e);
+ }
+
+ ///
+ /// Called when the control is removed from a visual tree.
+ ///
+ /// The event args.
+ protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
DetachedFromVisualTree?.Invoke(this, e);
}
@@ -427,14 +453,14 @@ namespace Perspex
if (VisualRoot != null)
{
var e = new VisualTreeAttachmentEventArgs(VisualRoot);
- NotifyDetachedFromVisualTree(e);
+ OnDetachedFromVisualTreeCore(e);
}
if (_visualParent is IRenderRoot || _visualParent?.IsAttachedToVisualTree == true)
{
var root = this.GetVisualAncestors().OfType().FirstOrDefault();
var e = new VisualTreeAttachmentEventArgs(root);
- NotifyAttachedToVisualTree(e);
+ OnAttachedToVisualTreeCore(e);
}
RaisePropertyChanged(VisualParentProperty, old, value, BindingPriority.LocalValue);
@@ -466,47 +492,5 @@ namespace Perspex
break;
}
}
-
- ///
- /// Calls the method
- /// for this control and all of its visual descendents.
- ///
- /// The event args.
- private void NotifyAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- _visualLogger.Verbose("Attached to visual tree");
-
- VisualRoot = e.Root;
- OnAttachedToVisualTree(e);
-
- if (VisualChildren != null)
- {
- foreach (Visual child in VisualChildren.OfType())
- {
- child.NotifyAttachedToVisualTree(e);
- }
- }
- }
-
- ///
- /// Calls the method
- /// for this control and all of its visual descendents.
- ///
- /// The event args.
- private void NotifyDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
- {
- _visualLogger.Verbose("Detached from visual tree");
-
- VisualRoot = null;
- OnDetachedFromVisualTree(e);
-
- if (VisualChildren != null)
- {
- foreach (Visual child in VisualChildren.OfType())
- {
- child.NotifyDetachedFromVisualTree(e);
- }
- }
- }
}
}
diff --git a/src/Perspex.SceneGraph/VisualExtensions.cs b/src/Perspex.SceneGraph/VisualExtensions.cs
index cb6362b7af..3228fa4b81 100644
--- a/src/Perspex.SceneGraph/VisualExtensions.cs
+++ b/src/Perspex.SceneGraph/VisualExtensions.cs
@@ -13,8 +13,6 @@ using Perspex.Media;
using Perspex.Platform;
using Perspex.Rendering;
using Perspex.VisualTree;
-using Serilog;
-using Serilog.Core.Enrichers;
namespace Perspex
{
diff --git a/src/Perspex.SceneGraph/packages.config b/src/Perspex.SceneGraph/packages.config
index c1c997f8a7..d5af0dcd81 100644
--- a/src/Perspex.SceneGraph/packages.config
+++ b/src/Perspex.SceneGraph/packages.config
@@ -5,5 +5,4 @@
-
\ No newline at end of file
diff --git a/src/Perspex.Styling/Styling/ISetter.cs b/src/Perspex.Styling/Styling/ISetter.cs
index 105a8ec6ac..8e5db104de 100644
--- a/src/Perspex.Styling/Styling/ISetter.cs
+++ b/src/Perspex.Styling/Styling/ISetter.cs
@@ -16,6 +16,6 @@ namespace Perspex.Styling
/// The style that is being applied.
/// The control.
/// An optional activator.
- void Apply(IStyle style, IStyleable control, IObservable activator);
+ IDisposable Apply(IStyle style, IStyleable control, IObservable activator);
}
}
\ No newline at end of file
diff --git a/src/Perspex.Styling/Styling/IStyleable.cs b/src/Perspex.Styling/Styling/IStyleable.cs
index 36b092ff1d..aa1e749dad 100644
--- a/src/Perspex.Styling/Styling/IStyleable.cs
+++ b/src/Perspex.Styling/Styling/IStyleable.cs
@@ -3,7 +3,6 @@
using System;
using Perspex.Collections;
-using System.Reactive;
namespace Perspex.Styling
{
@@ -13,9 +12,9 @@ namespace Perspex.Styling
public interface IStyleable : IPerspexObject, INamed
{
///
- /// Raised when the control's style should be removed.
+ /// Signalled when the control's style should be removed.
///
- IObservable StyleDetach { get; }
+ IObservable StyleDetach { get; }
///
/// Gets the list of classes for the control.
diff --git a/src/Perspex.Styling/Styling/Setter.cs b/src/Perspex.Styling/Styling/Setter.cs
index 3c4592fa81..23b5aa7d7e 100644
--- a/src/Perspex.Styling/Styling/Setter.cs
+++ b/src/Perspex.Styling/Styling/Setter.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Reactive.Disposables;
using System.Reactive.Subjects;
using Perspex.Data;
using Perspex.Metadata;
@@ -63,7 +64,7 @@ namespace Perspex.Styling
/// The style that is being applied.
/// The control.
/// An optional activator.
- public void Apply(IStyle style, IStyleable control, IObservable activator)
+ public IDisposable Apply(IStyle style, IStyleable control, IObservable activator)
{
Contract.Requires(control != null);
@@ -80,16 +81,12 @@ namespace Perspex.Styling
{
if (activator == null)
{
- control.SetValue(Property, Value, BindingPriority.Style);
+ return control.Bind(Property, ObservableEx.SingleValue(Value), BindingPriority.Style);
}
else
{
var activated = new ActivatedValue(activator, Value, description);
- var instanced = new InstancedBinding(
- activated,
- BindingMode.OneWay,
- BindingPriority.StyleTrigger);
- BindingOperations.Apply(control, Property, instanced, null);
+ return control.Bind(Property, activated, BindingPriority.StyleTrigger);
}
}
else
@@ -99,9 +96,11 @@ namespace Perspex.Styling
if (source != null)
{
var cloned = Clone(source, style, activator);
- BindingOperations.Apply(control, Property, cloned, null);
+ return BindingOperations.Apply(control, Property, cloned, null);
}
}
+
+ return Disposable.Empty;
}
private InstancedBinding Clone(InstancedBinding sourceInstance, IStyle style, IObservable activator)
diff --git a/src/Perspex.Styling/Styling/Style.cs b/src/Perspex.Styling/Styling/Style.cs
index 7f0cb00e4b..73e5c19467 100644
--- a/src/Perspex.Styling/Styling/Style.cs
+++ b/src/Perspex.Styling/Styling/Style.cs
@@ -13,7 +13,9 @@ namespace Perspex.Styling
///
public class Style : IStyle
{
- private static readonly IObservable True = Observable.Never().StartWith(true);
+ private static Dictionary> _applied =
+ new Dictionary>();
+
private Dictionary _resources;
///
@@ -85,20 +87,23 @@ namespace Perspex.Styling
if (match.ImmediateResult != false)
{
- var activator = (match.ObservableResult ?? True)
- .TakeUntil(control.StyleDetach);
+ var subs = GetSubscriptions(control);
foreach (var setter in Setters)
{
- setter.Apply(this, control, activator);
+ var sub = setter.Apply(this, control, match.ObservableResult);
+ subs.Add(sub);
}
}
}
else if (control == container)
{
+ var subs = GetSubscriptions(control);
+
foreach (var setter in Setters)
{
- setter.Apply(this, control, null);
+ var sub = setter.Apply(this, control, null);
+ subs.Add(sub);
}
}
}
@@ -139,5 +144,36 @@ namespace Perspex.Styling
return "Style";
}
}
+
+ private static List GetSubscriptions(IStyleable control)
+ {
+ List subscriptions;
+
+ if (!_applied.TryGetValue(control, out subscriptions))
+ {
+ subscriptions = new List(2);
+ subscriptions.Add(control.StyleDetach.Subscribe(ControlDetach));
+ _applied.Add(control, subscriptions);
+ }
+
+ return subscriptions;
+ }
+
+ ///
+ /// Called when a control's is signalled to remove
+ /// all applied styles.
+ ///
+ /// The control.
+ private static void ControlDetach(IStyleable control)
+ {
+ var subscriptions = _applied[control];
+
+ foreach (var subscription in subscriptions)
+ {
+ subscription.Dispose();
+ }
+
+ _applied.Remove(control);
+ }
}
}
diff --git a/src/Perspex.Styling/Styling/StyleActivator.cs b/src/Perspex.Styling/Styling/StyleActivator.cs
index dd2601e0bb..05b97a11ec 100644
--- a/src/Perspex.Styling/Styling/StyleActivator.cs
+++ b/src/Perspex.Styling/Styling/StyleActivator.cs
@@ -17,18 +17,40 @@ namespace Perspex.Styling
public static class StyleActivator
{
- public static IObservable And(IEnumerable> inputs)
+ public static IObservable And(IList> inputs)
{
- return inputs.CombineLatest()
- .Select(values => values.All(x => x))
- .DistinctUntilChanged();
+ if (inputs.Count == 0)
+ {
+ throw new ArgumentException("StyleActivator.And inputs may not be empty.");
+ }
+ else if (inputs.Count == 1)
+ {
+ return inputs[0];
+ }
+ else
+ {
+ return inputs.CombineLatest()
+ .Select(values => values.All(x => x))
+ .DistinctUntilChanged();
+ }
}
- public static IObservable Or(IEnumerable> inputs)
+ public static IObservable Or(IList> inputs)
{
- return inputs.CombineLatest()
+ if (inputs.Count == 0)
+ {
+ throw new ArgumentException("StyleActivator.Or inputs may not be empty.");
+ }
+ else if (inputs.Count == 1)
+ {
+ return inputs[0];
+ }
+ else
+ {
+ return inputs.CombineLatest()
.Select(values => values.Any(x => x))
.DistinctUntilChanged();
+ }
}
}
}
diff --git a/src/Perspex.Themes.Default/DefaultTheme.xaml b/src/Perspex.Themes.Default/DefaultTheme.xaml
index b25c85bbd1..f536a523f3 100644
--- a/src/Perspex.Themes.Default/DefaultTheme.xaml
+++ b/src/Perspex.Themes.Default/DefaultTheme.xaml
@@ -8,6 +8,7 @@
+
diff --git a/src/Perspex.Themes.Default/DropDownItem.xaml b/src/Perspex.Themes.Default/DropDownItem.xaml
index 26bbe9959b..4900c961e4 100644
--- a/src/Perspex.Themes.Default/DropDownItem.xaml
+++ b/src/Perspex.Themes.Default/DropDownItem.xaml
@@ -1,6 +1,8 @@
\ No newline at end of file
diff --git a/src/Perspex.Themes.Default/Perspex.Themes.Default.csproj b/src/Perspex.Themes.Default/Perspex.Themes.Default.csproj
index e954fd0419..c26bb74ef0 100644
--- a/src/Perspex.Themes.Default/Perspex.Themes.Default.csproj
+++ b/src/Perspex.Themes.Default/Perspex.Themes.Default.csproj
@@ -200,6 +200,11 @@
Designer
+
+
+ Designer
+
+
+
\ No newline at end of file
diff --git a/src/Skia/Perspex.Skia.iOS.TestApp/Perspex.Skia.iOS.TestApp.csproj b/src/Skia/Perspex.Skia.iOS.TestApp/Perspex.Skia.iOS.TestApp.csproj
index 8b107002c3..950a0e729a 100644
--- a/src/Skia/Perspex.Skia.iOS.TestApp/Perspex.Skia.iOS.TestApp.csproj
+++ b/src/Skia/Perspex.Skia.iOS.TestApp/Perspex.Skia.iOS.TestApp.csproj
@@ -108,10 +108,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base
diff --git a/src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj b/src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj
index a583ceb5b2..731d3d5528 100644
--- a/src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj
+++ b/src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj
@@ -56,10 +56,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base
@@ -72,6 +68,10 @@
{62024b2d-53eb-4638-b26b-85eeaa54866e}
Perspex.Input
+
+ {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}
+ Perspex.Interactivity
+
{42472427-4774-4c81-8aff-9f27b8e31721}
Perspex.Layout
diff --git a/src/Skia/Perspex.Skia/DrawingContextImpl.cs b/src/Skia/Perspex.Skia/DrawingContextImpl.cs
index 61f4bc4a4d..0139932160 100644
--- a/src/Skia/Perspex.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Perspex.Skia/DrawingContextImpl.cs
@@ -34,7 +34,7 @@ namespace Perspex.Skia
}
static readonly NativeBrushContainer _dummy = new NativeBrushContainer(null);
- public void DrawGeometry(Brush brush, Pen pen, Geometry geometry)
+ public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
{
var impl = ((StreamGeometryImpl) geometry.PlatformImpl);
var size = geometry.Bounds.Size;
@@ -46,7 +46,7 @@ namespace Perspex.Skia
}
}
- unsafe NativeBrushContainer CreateBrush(Brush brush, Size targetSize)
+ unsafe NativeBrushContainer CreateBrush(IBrush brush, Size targetSize)
{
var rv = NativeBrushPool.Instance.Get();
rv.Brush->Opacity = brush.Opacity;
@@ -141,7 +141,7 @@ namespace Perspex.Skia
}
}
- public void FillRectangle(Brush pbrush, Rect rect, float cornerRadius = 0)
+ public void FillRectangle(IBrush pbrush, Rect rect, float cornerRadius = 0)
{
using (var brush = CreateBrush(pbrush, rect.Size))
{
@@ -150,7 +150,7 @@ namespace Perspex.Skia
}
}
- public void DrawText(Brush foreground, Point origin, FormattedText text)
+ public void DrawText(IBrush foreground, Point origin, FormattedText text)
{
using (var br = CreateBrush(foreground, text.Measure()))
MethodTable.Instance.DrawFormattedText(Handle, br.Brush, ((FormattedTextImpl) text.PlatformImpl).Handle,
diff --git a/src/Skia/Perspex.Skia/FormattedTextImpl.cs b/src/Skia/Perspex.Skia/FormattedTextImpl.cs
index 808935e99e..13888334ad 100644
--- a/src/Skia/Perspex.Skia/FormattedTextImpl.cs
+++ b/src/Skia/Perspex.Skia/FormattedTextImpl.cs
@@ -73,7 +73,7 @@ namespace Perspex.Skia
return _size;
}
- public void SetForegroundBrush(Brush brush, int startIndex, int length)
+ public void SetForegroundBrush(IBrush brush, int startIndex, int length)
{
}
diff --git a/src/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs b/src/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs
index 0c8f8d1373..ed4e44a7aa 100644
--- a/src/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs
+++ b/src/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs
@@ -8,11 +8,11 @@ namespace Perspex.Direct2D1.Media
{
internal class BrushWrapper : ComObject
{
- public BrushWrapper(Brush brush)
+ public BrushWrapper(IBrush brush)
{
Brush = brush;
}
- public Brush Brush { get; private set; }
+ public IBrush Brush { get; private set; }
}
}
diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
index 6031d4a8e9..196588b5ad 100644
--- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
+++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
@@ -114,7 +114,7 @@ namespace Perspex.Direct2D1.Media
/// The fill brush.
/// The stroke pen.
/// The geometry.
- public void DrawGeometry(Perspex.Media.Brush brush, Pen pen, Perspex.Media.Geometry geometry)
+ public void DrawGeometry(IBrush brush, Pen pen, Perspex.Media.Geometry geometry)
{
if (brush != null)
{
@@ -181,7 +181,7 @@ namespace Perspex.Direct2D1.Media
/// The foreground brush.
/// The upper-left corner of the text.
/// The text.
- public void DrawText(Perspex.Media.Brush foreground, Point origin, FormattedText text)
+ public void DrawText(IBrush foreground, Point origin, FormattedText text)
{
if (!string.IsNullOrEmpty(text.Text))
{
@@ -204,7 +204,7 @@ namespace Perspex.Direct2D1.Media
/// The brush.
/// The rectangle bounds.
/// The corner radius.
- public void FillRectangle(Perspex.Media.Brush brush, Rect rect, float cornerRadius)
+ public void FillRectangle(IBrush brush, Rect rect, float cornerRadius)
{
using (var b = CreateBrush(brush, rect.Size))
{
@@ -291,10 +291,9 @@ namespace Perspex.Direct2D1.Media
/// The perspex brush.
/// The size of the brush's target area.
/// The Direct2D brush wrapper.
- public BrushImpl CreateBrush(Perspex.Media.Brush brush, Size destinationSize)
+ public BrushImpl CreateBrush(IBrush brush, Size destinationSize)
{
- var solidColorBrush = brush as Perspex.Media.SolidColorBrush;
- var mutableSolidColorBrush = brush as Perspex.Media.Mutable.SolidColorBrush;
+ var solidColorBrush = brush as Perspex.Media.ISolidColorBrush;
var linearGradientBrush = brush as Perspex.Media.LinearGradientBrush;
var radialGradientBrush = brush as Perspex.Media.RadialGradientBrush;
var imageBrush = brush as Perspex.Media.ImageBrush;
@@ -304,10 +303,6 @@ namespace Perspex.Direct2D1.Media
{
return new SolidColorBrushImpl(solidColorBrush, _renderTarget);
}
- if (mutableSolidColorBrush != null)
- {
- return new SolidColorBrushImpl(mutableSolidColorBrush, _renderTarget);
- }
else if (linearGradientBrush != null)
{
return new LinearGradientBrushImpl(linearGradientBrush, _renderTarget, destinationSize);
diff --git a/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs
index 22f8b94491..4daeb685fb 100644
--- a/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs
+++ b/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs
@@ -118,7 +118,7 @@ namespace Perspex.Direct2D1.Media
return new Size(width, TextLayout.Metrics.Height);
}
- public void SetForegroundBrush(Brush brush, int startIndex, int count)
+ public void SetForegroundBrush(IBrush brush, int startIndex, int count)
{
TextLayout.SetDrawingEffect(
new BrushWrapper(brush),
diff --git a/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs
index 88d5413843..09012934b0 100644
--- a/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs
+++ b/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs
@@ -1,24 +1,13 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using Perspex.Media;
+
namespace Perspex.Direct2D1.Media
{
public class SolidColorBrushImpl : BrushImpl
{
- public SolidColorBrushImpl(Perspex.Media.SolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target)
- {
- PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush(
- target,
- brush?.Color.ToDirect2D() ?? new SharpDX.Mathematics.Interop.RawColor4(),
- new SharpDX.Direct2D1.BrushProperties
- {
- Opacity = brush != null ? (float)brush.Opacity : 1.0f,
- Transform = target.Transform
- }
- );
- }
-
- public SolidColorBrushImpl(Perspex.Media.Mutable.SolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target)
+ public SolidColorBrushImpl(ISolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target)
{
PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush(
target,
diff --git a/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs
index ab803e473e..2818e68627 100644
--- a/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs
+++ b/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs
@@ -1,13 +1,9 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
-using System;
-using Perspex.Layout;
using Perspex.Media;
-using SharpDX;
-using SharpDX.Direct2D1;
using Perspex.RenderHelpers;
-
+using SharpDX.Direct2D1;
namespace Perspex.Direct2D1.Media
{
@@ -38,7 +34,6 @@ namespace Perspex.Direct2D1.Media
}
}
-
private static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect)
{
var tileTransform =
diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
index c091a58c34..dc1e582bf1 100644
--- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
+++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
@@ -107,6 +107,18 @@
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}
Perspex.Base
+
+ {d2221c82-4a25-4583-9b43-d791e3f6820c}
+ Perspex.Controls
+
+
+ {62024b2d-53eb-4638-b26b-85eeaa54866e}
+ Perspex.Input
+
+
+ {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}
+ Perspex.Interactivity
+
{42472427-4774-4C81-8AFF-9F27B8E31721}
Perspex.Layout
@@ -115,6 +127,10 @@
{EB582467-6ABB-43A1-B052-E981BA910E3A}
Perspex.SceneGraph
+
+ {f1baa01a-f176-4c6a-b39d-5b40bb1b148f}
+ Perspex.Styling
+
diff --git a/src/Windows/Perspex.Direct2D1/RenderTarget.cs b/src/Windows/Perspex.Direct2D1/RenderTarget.cs
index c835fc03e6..153b3be221 100644
--- a/src/Windows/Perspex.Direct2D1/RenderTarget.cs
+++ b/src/Windows/Perspex.Direct2D1/RenderTarget.cs
@@ -2,10 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
-using Perspex.Direct2D1.Media;
-using Perspex.Media;
using Perspex.Platform;
-using Perspex.Rendering;
using Perspex.Win32.Interop;
using SharpDX;
using SharpDX.Direct2D1;
@@ -18,14 +15,13 @@ namespace Perspex.Direct2D1
{
private readonly IntPtr _hwnd;
private Size2 _savedSize;
+ private Size2F _savedDpi;
///
/// The render target.
///
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
-
-
///
/// Initializes a new instance of the class.
///
@@ -53,13 +49,6 @@ namespace Perspex.Direct2D1
hwndProperties);
}
- Size2 GetWindowSize()
- {
- UnmanagedMethods.RECT rc;
- UnmanagedMethods.GetClientRect(_hwnd, out rc);
- return new Size2(rc.right - rc.left, rc.bottom - rc.top);
- }
-
///
/// Initializes a new instance of the class.
///
@@ -94,11 +83,21 @@ namespace Perspex.Direct2D1
public DrawingContext CreateDrawingContext()
{
var window = _renderTarget as WindowRenderTarget;
+
if (window != null)
{
var size = GetWindowSize();
+ var dpi = GetWindowDpi();
+
if (size != _savedSize)
+ {
window.Resize(_savedSize = size);
+ }
+
+ if (dpi != _savedDpi)
+ {
+ window.DotsPerInch = _savedDpi = dpi;
+ }
}
return new DrawingContext(new Media.DrawingContext(_renderTarget, DirectWriteFactory));
@@ -108,5 +107,35 @@ namespace Perspex.Direct2D1
{
_renderTarget.Dispose();
}
+
+ private Size2F GetWindowDpi()
+ {
+ if (UnmanagedMethods.ShCoreAvailable)
+ {
+ uint dpix, dpiy;
+
+ var monitor = UnmanagedMethods.MonitorFromWindow(
+ _hwnd,
+ UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
+
+ if (UnmanagedMethods.GetDpiForMonitor(
+ monitor,
+ UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
+ out dpix,
+ out dpiy) == 0)
+ {
+ return new Size2F(dpix, dpiy);
+ }
+ }
+
+ return new Size2F(96, 96);
+ }
+
+ private Size2 GetWindowSize()
+ {
+ UnmanagedMethods.RECT rc;
+ UnmanagedMethods.GetClientRect(_hwnd, out rc);
+ return new Size2(rc.right - rc.left, rc.bottom - rc.top);
+ }
}
}
diff --git a/src/Windows/Perspex.Win32/Perspex.Win32.csproj b/src/Windows/Perspex.Win32/Perspex.Win32.csproj
index 0cd573d7b8..458e1de004 100644
--- a/src/Windows/Perspex.Win32/Perspex.Win32.csproj
+++ b/src/Windows/Perspex.Win32/Perspex.Win32.csproj
@@ -88,10 +88,6 @@
{D211E587-D8BC-45B9-95A4-F297C8FA5200}
Perspex.Animation
-
- {799A7BB5-3C2C-48B6-85A7-406A12C420DA}
- Perspex.Application
-
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}
Perspex.Base
diff --git a/src/Windows/Perspex.Win32/WindowImpl.cs b/src/Windows/Perspex.Win32/WindowImpl.cs
index 6711663270..f07839c09d 100644
--- a/src/Windows/Perspex.Win32/WindowImpl.cs
+++ b/src/Windows/Perspex.Win32/WindowImpl.cs
@@ -80,13 +80,14 @@ namespace Perspex.Win32
{
UnmanagedMethods.RECT rect;
UnmanagedMethods.GetClientRect(_hwnd, out rect);
- return new Size(rect.right, rect.bottom);
+ return new Size(rect.right, rect.bottom) / Scaling;
}
set
{
if (value != ClientSize)
{
+ value *= Scaling;
value += BorderThickness;
UnmanagedMethods.SetWindowPos(
@@ -119,10 +120,10 @@ namespace Perspex.Win32
{
get
{
- return new Size(
+ return (new Size(
UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXMAXTRACK),
UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYMAXTRACK))
- - BorderThickness;
+ - BorderThickness) / Scaling;
}
}
@@ -221,12 +222,13 @@ namespace Perspex.Win32
public void Invalidate(Rect rect)
{
+ var f = Scaling;
var r = new UnmanagedMethods.RECT
{
- left = (int)rect.X,
- top = (int)rect.Y,
- right = (int)rect.Right,
- bottom = (int)rect.Bottom,
+ left = (int)(rect.X * f),
+ top = (int)(rect.Y * f),
+ right = (int)(rect.Right * f),
+ bottom = (int)(rect.Bottom * f),
};
UnmanagedMethods.InvalidateRect(_hwnd, ref r, false);
@@ -236,11 +238,12 @@ namespace Perspex.Win32
{
var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
UnmanagedMethods.ScreenToClient(_hwnd, ref p);
- return new Point(p.X, p.Y);
+ return new Point(p.X, p.Y) / Scaling;
}
public Point PointToScreen(Point point)
{
+ point *= Scaling;
var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
UnmanagedMethods.ClientToScreen(_hwnd, ref p);
return new Point(p.X, p.Y);
@@ -459,7 +462,7 @@ namespace Perspex.Win32
: msg == (int)UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN
? RawMouseEventType.RightButtonDown
: RawMouseEventType.MiddleButtonDown,
- PointFromLParam(lParam), GetMouseModifiers(wParam));
+ DipFromLParam(lParam), GetMouseModifiers(wParam));
break;
case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
@@ -474,7 +477,7 @@ namespace Perspex.Win32
: msg == (int) UnmanagedMethods.WindowsMessage.WM_RBUTTONUP
? RawMouseEventType.RightButtonUp
: RawMouseEventType.MiddleButtonUp,
- PointFromLParam(lParam), GetMouseModifiers(wParam));
+ DipFromLParam(lParam), GetMouseModifiers(wParam));
break;
case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE:
@@ -496,7 +499,7 @@ namespace Perspex.Win32
timestamp,
_owner,
RawMouseEventType.Move,
- PointFromLParam(lParam), GetMouseModifiers(wParam));
+ DipFromLParam(lParam), GetMouseModifiers(wParam));
break;
@@ -505,7 +508,7 @@ namespace Perspex.Win32
WindowsMouseDevice.Instance,
timestamp,
_owner,
- ScreenToClient(PointFromLParam(lParam)),
+ ScreenToClient(DipFromLParam(lParam)),
new Vector(0, ((int)wParam >> 16) / wheelDelta), GetMouseModifiers(wParam));
break;
@@ -528,7 +531,8 @@ namespace Perspex.Win32
{
UnmanagedMethods.RECT r;
UnmanagedMethods.GetUpdateRect(_hwnd, out r, false);
- Paint(new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top));
+ var f = Scaling;
+ Paint(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, (r.bottom - r.top) / f));
UnmanagedMethods.EndPaint(_hwnd, ref ps);
}
}
@@ -539,7 +543,7 @@ namespace Perspex.Win32
if (Resized != null)
{
var clientSize = new Size((int)lParam & 0xffff, (int)lParam >> 16);
- Resized(clientSize);
+ Resized(clientSize / Scaling);
}
return IntPtr.Zero;
@@ -605,13 +609,14 @@ namespace Perspex.Win32
Handle = new PlatformHandle(_hwnd, PlatformConstants.WindowHandleType);
- var monitor = UnmanagedMethods.MonitorFromWindow(
- _hwnd,
- UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
-
if (UnmanagedMethods.ShCoreAvailable)
{
uint dpix, dpiy;
+
+ var monitor = UnmanagedMethods.MonitorFromWindow(
+ _hwnd,
+ UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
+
if (UnmanagedMethods.GetDpiForMonitor(
monitor,
UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
@@ -623,6 +628,11 @@ namespace Perspex.Win32
}
}
+ private Point DipFromLParam(IntPtr lParam)
+ {
+ return new Point((short)((int)lParam & 0xffff), (short)((int)lParam >> 16)) / Scaling;
+ }
+
private Point PointFromLParam(IntPtr lParam)
{
return new Point((short)((int)lParam & 0xffff), (short)((int)lParam >> 16));
diff --git a/src/iOS/Perspex.iOS/Perspex.iOS.csproj b/src/iOS/Perspex.iOS/Perspex.iOS.csproj
index 0d670ac5ed..b0ce948a3c 100644
--- a/src/iOS/Perspex.iOS/Perspex.iOS.csproj
+++ b/src/iOS/Perspex.iOS/Perspex.iOS.csproj
@@ -49,10 +49,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base
diff --git a/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj b/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj
index 42c0c2eccd..63a5fc4e96 100644
--- a/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj
+++ b/src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj
@@ -139,10 +139,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base
diff --git a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
index 08903a4dc2..879022611c 100644
--- a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
+++ b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
@@ -39,23 +39,31 @@
4
-
- ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll
- True
-
-
- ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll
+
+ ..\..\packages\Rx-Testing.2.2.5\lib\net45\Microsoft.Reactive.Testing.dll
True
+
-
+
..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll
+ True
-
+
..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll
+ True
-
+
..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll
+ True
+
+
+ ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll
+ True
+
+
+ ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll
+ True
..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
index 40f542ae76..caa36f38a8 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
@@ -4,6 +4,7 @@
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
+using Microsoft.Reactive.Testing;
using Perspex.Data;
using Xunit;
@@ -81,6 +82,22 @@ namespace Perspex.Base.UnitTests
Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
}
+ [Fact]
+ public void Observable_Is_Unsubscribed_When_Subscription_Disposed()
+ {
+ var scheduler = new TestScheduler();
+ var source = scheduler.CreateColdObservable
-
+
..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll
+ True
-
+
..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll
+ True
-
+
..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll
+ True
-
+
..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll
+ True
..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll
diff --git a/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs b/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs
index 55eff92311..465610b045 100644
--- a/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs
+++ b/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs
@@ -105,7 +105,7 @@ namespace Perspex.Styling.UnitTests
public ITemplatedControl TemplatedParent { get; }
- IObservable IStyleable.StyleDetach { get; }
+ IObservable IStyleable.StyleDetach { get; }
IPerspexReadOnlyList IStyleable.Classes => Classes;
diff --git a/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs
index 07bb16ee14..286a5ec269 100644
--- a/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs
+++ b/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs
@@ -138,7 +138,7 @@ namespace Perspex.Styling.UnitTests
IPerspexReadOnlyList IStyleable.Classes => Classes;
- IObservable IStyleable.StyleDetach { get; }
+ IObservable IStyleable.StyleDetach { get; }
public object GetValue(PerspexProperty property)
{
diff --git a/tests/Perspex.Styling.UnitTests/TestControlBase.cs b/tests/Perspex.Styling.UnitTests/TestControlBase.cs
index 3a0f0e5434..d5bd85f1dd 100644
--- a/tests/Perspex.Styling.UnitTests/TestControlBase.cs
+++ b/tests/Perspex.Styling.UnitTests/TestControlBase.cs
@@ -35,7 +35,7 @@ namespace Perspex.Styling.UnitTests
IPerspexReadOnlyList IStyleable.Classes => Classes;
- IObservable IStyleable.StyleDetach { get; }
+ IObservable IStyleable.StyleDetach { get; }
public object GetValue(PerspexProperty property)
{
diff --git a/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs b/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs
index 847a124066..3e16e8a18c 100644
--- a/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs
+++ b/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs
@@ -35,7 +35,7 @@ namespace Perspex.Styling.UnitTests
IPerspexReadOnlyList IStyleable.Classes => Classes;
- IObservable IStyleable.StyleDetach { get; }
+ IObservable IStyleable.StyleDetach { get; }
public object GetValue(PerspexProperty property)
{
diff --git a/tests/Perspex.Markup.UnitTests/Data/NotifyingBase.cs b/tests/Perspex.UnitTests/NotifyingBase.cs
similarity index 90%
rename from tests/Perspex.Markup.UnitTests/Data/NotifyingBase.cs
rename to tests/Perspex.UnitTests/NotifyingBase.cs
index 384c3ede02..0d2e1c968c 100644
--- a/tests/Perspex.Markup.UnitTests/Data/NotifyingBase.cs
+++ b/tests/Perspex.UnitTests/NotifyingBase.cs
@@ -4,7 +4,7 @@
using System.ComponentModel;
using System.Linq;
-namespace Perspex.Markup.UnitTests.Data
+namespace Perspex.UnitTests
{
public class NotifyingBase : INotifyPropertyChanged
{
@@ -34,7 +34,7 @@ namespace Perspex.Markup.UnitTests.Data
private set;
}
- protected void RaisePropertyChanged(string propertyName)
+ public void RaisePropertyChanged(string propertyName)
{
_propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
diff --git a/tests/Perspex.UnitTests/Perspex.UnitTests.csproj b/tests/Perspex.UnitTests/Perspex.UnitTests.csproj
index 675485d81b..d06a4e98da 100644
--- a/tests/Perspex.UnitTests/Perspex.UnitTests.csproj
+++ b/tests/Perspex.UnitTests/Perspex.UnitTests.csproj
@@ -62,6 +62,7 @@
+
@@ -82,10 +83,6 @@
{d211e587-d8bc-45b9-95a4-f297c8fa5200}
Perspex.Animation
-
- {799a7bb5-3c2c-48b6-85a7-406a12c420da}
- Perspex.Application
-
{b09b78d8-9b26-48b0-9149-d64a2f120f3f}
Perspex.Base