Browse Source

Merge branch 'style-xaml'

Conflicts:
	src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
	src/Markup/Perspex.Markup.Xaml/Properties/AssemblyInfo.cs
pull/145/head
Steven Kirk 11 years ago
parent
commit
f6e1e658dd
  1. 3
      .gitmodules
  2. 63
      Tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs
  3. 189
      Tests/Perspex.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs
  4. 17
      Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj
  5. 2
      Tests/Perspex.Markup.Xaml.UnitTests/packages.config
  6. 7
      samples/XamlTestApplication/Views/MainWindow.xaml
  7. 8
      samples/XamlTestApplication/XamlTestApplication.csproj
  8. 2
      samples/XamlTestApplication/packages.config
  9. 2
      src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs
  10. 2
      src/Markup/Perspex.Markup.Xaml/Context/PerspexParserFactory.cs
  11. 4
      src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
  12. 29
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
  13. 33
      src/Markup/Perspex.Markup.Xaml/Converters/ClassesConverter.cs
  14. 72
      src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs
  15. 34
      src/Markup/Perspex.Markup.Xaml/Converters/SelectorConverter.cs
  16. 1
      src/Markup/Perspex.Markup.Xaml/OmniXAML
  17. 187
      src/Markup/Perspex.Markup.Xaml/Parsers/SelectorGrammar.cs
  18. 83
      src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs
  19. 177
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  20. 5
      src/Markup/Perspex.Markup.Xaml/Properties/AssemblyInfo.cs
  21. 2
      src/Markup/Perspex.Markup.Xaml/packages.config
  22. 65
      src/Perspex.Base/PerspexObject.cs
  23. 5
      src/Perspex.Base/PerspexProperty.cs
  24. 7
      src/Perspex.Styling/Styling/IStyle.cs
  25. 28
      src/Perspex.Styling/Styling/Selector.cs
  26. 2
      src/Perspex.Styling/Styling/Selectors.cs
  27. 33
      src/Perspex.Styling/Styling/Style.cs
  28. 4
      src/Perspex.Styling/Styling/Styler.cs
  29. 14
      src/Perspex.Styling/Styling/Styles.cs
  30. 27
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs
  31. 12
      tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs
  32. 49
      tests/Perspex.Styling.UnitTests/StyleTests.cs

3
.gitmodules

@ -5,3 +5,6 @@
path = src/Perspex.HtmlRenderer/external path = src/Perspex.HtmlRenderer/external
url = https://github.com/Perspex/HTML-Renderer.git url = https://github.com/Perspex/HTML-Renderer.git
branch = perspex-pcl branch = perspex-pcl
[submodule "src/Markup/Perspex.Markup.Xaml/OmniXAML"]
path = src/Markup/Perspex.Markup.Xaml/OmniXAML
url = https://github.com/SuperJMN/OmniXAML.git

63
Tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs

@ -0,0 +1,63 @@
// 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 Moq;
using OmniXaml;
using OmniXaml.TypeConversion;
using OmniXaml.Typing;
using Perspex.Markup.Xaml.Converters;
using Xunit;
namespace Perspex.Markup.Xaml.UnitTests.Converters
{
public class PerspexPropertyConverterTest
{
public PerspexPropertyConverterTest()
{
// Ensure properties are registered.
var foo = Class1.FooProperty;
var attached = AttachedOwner.AttachedProperty;
}
[Fact]
public void ConvertFrom_Finds_Fully_Qualified_Property()
{
var target = new PerspexPropertyConverter();
var context = CreateContext();
var result = target.ConvertFrom(context, null, "Class1.Foo");
}
[Fact]
public void ConvertFrom_Finds_Attached_Property()
{
var target = new PerspexPropertyConverter();
var context = CreateContext();
var result = target.ConvertFrom(context, null, "AttachedOwner.Attached");
}
private IXamlTypeConverterContext CreateContext()
{
var context = new Mock<IXamlTypeConverterContext>();
var typeRepository = new Mock<IXamlTypeRepository>();
var featureProvider = new Mock<ITypeFeatureProvider>();
var class1XamlType = new XamlType(typeof(Class1), typeRepository.Object, null, featureProvider.Object);
var attachedOwnerXamlType = new XamlType(typeof(AttachedOwner), typeRepository.Object, null, featureProvider.Object);
context.Setup(x => x.TypeRepository).Returns(typeRepository.Object);
typeRepository.Setup(x => x.GetByQualifiedName("Class1")).Returns(class1XamlType);
typeRepository.Setup(x => x.GetByQualifiedName("AttachedOwner")).Returns(attachedOwnerXamlType);
return context.Object;
}
private class Class1 : PerspexObject
{
public static readonly PerspexProperty<string> FooProperty =
PerspexProperty.Register<Class1, string>("Foo");
}
private class AttachedOwner
{
public static readonly PerspexProperty<string> AttachedProperty =
PerspexProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
}
}
}

189
Tests/Perspex.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs

@ -0,0 +1,189 @@
// 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.Linq;
using Perspex.Markup.Xaml.Parsers;
using Sprache;
using Xunit;
namespace Perspex.Xaml.Base.UnitTest.Parsers
{
public class SelectorGrammarTests
{
[Fact]
public void OfType()
{
var result = SelectorGrammar.Selector.Parse("Button").ToList();
Assert.Equal(
new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = null } },
result);
}
[Fact]
public void NamespacedOfType()
{
var result = SelectorGrammar.Selector.Parse("x|Button").ToList();
Assert.Equal(
new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = "x" } },
result);
}
[Fact]
public void Name()
{
var result = SelectorGrammar.Selector.Parse("#foo").ToList();
Assert.Equal(
new[] { new SelectorGrammar.NameSyntax { Name = "foo" }, },
result);
}
[Fact]
public void OfType_Name()
{
var result = SelectorGrammar.Selector.Parse("Button#foo").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.NameSyntax { Name = "foo" },
},
result);
}
[Fact]
public void Class()
{
var result = SelectorGrammar.Selector.Parse(".foo").ToList();
Assert.Equal(
new[] { new SelectorGrammar.ClassSyntax { Class = "foo" } },
result);
}
[Fact]
public void Pseudoclass()
{
var result = SelectorGrammar.Selector.Parse(":foo").ToList();
Assert.Equal(
new[] { new SelectorGrammar.ClassSyntax { Class = ":foo" } },
result);
}
[Fact]
public void OfType_Class()
{
var result = SelectorGrammar.Selector.Parse("Button.foo").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.ClassSyntax { Class = "foo" },
},
result);
}
[Fact]
public void OfType_Child_Class()
{
var result = SelectorGrammar.Selector.Parse("Button < .foo").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.ChildSyntax { },
new SelectorGrammar.ClassSyntax { Class = "foo" },
},
result);
}
[Fact]
public void OfType_Child_Class_No_Spaces()
{
var result = SelectorGrammar.Selector.Parse("Button<.foo").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.ChildSyntax { },
new SelectorGrammar.ClassSyntax { Class = "foo" },
},
result);
}
[Fact]
public void OfType_Descendent_Class()
{
var result = SelectorGrammar.Selector.Parse("Button .foo").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.DescendentSyntax { },
new SelectorGrammar.ClassSyntax { Class = "foo" },
},
result);
}
[Fact]
public void OfType_Template_Class()
{
var result = SelectorGrammar.Selector.Parse("Button /template/ .foo").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.TemplateSyntax { },
new SelectorGrammar.ClassSyntax { Class = "foo" },
},
result);
}
[Fact]
public void OfType_Property()
{
var result = SelectorGrammar.Selector.Parse("Button[Foo=bar]").ToList();
Assert.Equal(
new SelectorGrammar.ISyntax[]
{
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.PropertySyntax { Property = "Foo", Value = "bar" },
},
result);
}
[Fact]
public void Namespace_Alone_Fails()
{
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse("ns|").ToList());
}
[Fact]
public void Dot_Alone_Fails()
{
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse(". dot").ToList());
}
[Fact]
public void Invalid_Identifier_Fails()
{
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse("%foo").ToList());
}
[Fact]
public void Invalid_Class_Fails()
{
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse(".%foo").ToList());
}
}
}

17
Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj

@ -8,8 +8,8 @@
<ProjectGuid>{99135EAB-653D-47E4-A378-C96E1278CA44}</ProjectGuid> <ProjectGuid>{99135EAB-653D-47E4-A378-C96E1278CA44}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex.Xaml.Base.UnitTest</RootNamespace> <RootNamespace>Perspex.Markup.Xaml.UnitTests</RootNamespace>
<AssemblyName>Perspex.Xaml.Base.UnitTest</AssemblyName> <AssemblyName>Perspex.Markup.Xaml.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@ -43,10 +43,6 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Glass, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Glass.0.1.0\lib\portable-net45+win+Xamarin.iOS10+MonoAndroid10+MonoTouch10\Glass.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.2.1409.1722, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL"> <Reference Include="Moq, Version=4.2.1409.1722, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll</HintPath> <HintPath>..\..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -54,13 +50,14 @@
<Reference Include="Octokit"> <Reference Include="Octokit">
<HintPath>..\..\packages\Octokit.0.14.0\lib\net45\Octokit.dll</HintPath> <HintPath>..\..\packages\Octokit.0.14.0\lib\net45\Octokit.dll</HintPath>
</Reference> </Reference>
<Reference Include="OmniXaml">
<HintPath>..\..\packages\OmniXaml.0.1.0\lib\portable-net45+win+Xamarin.iOS10+MonoAndroid10+MonoTouch10\OmniXaml.dll</HintPath>
</Reference>
<Reference Include="Splat, Version=1.6.1.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Splat, Version=1.6.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Splat.1.6.1\lib\Net45\Splat.dll</HintPath> <HintPath>..\..\packages\Splat.1.6.1\lib\Net45\Splat.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Sprache, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Sprache.SuperJMN.2.0.0.50\lib\portable-net451+netcore451+wpa81\Sprache.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath> <HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
@ -102,7 +99,9 @@
<ItemGroup> <ItemGroup>
<Compile Include="BindingDefinitionBuilder.cs" /> <Compile Include="BindingDefinitionBuilder.cs" />
<Compile Include="ChangeBranchTest.cs" /> <Compile Include="ChangeBranchTest.cs" />
<Compile Include="Converters\PerspexPropertyConverterTest.cs" />
<Compile Include="DataContextChangeSynchronizerTest.cs" /> <Compile Include="DataContextChangeSynchronizerTest.cs" />
<Compile Include="Parsers\SelectorGrammarTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SampleModel\Level1.cs" /> <Compile Include="SampleModel\Level1.cs" />
<Compile Include="SampleModel\Level2.cs" /> <Compile Include="SampleModel\Level2.cs" />

2
Tests/Perspex.Markup.Xaml.UnitTests/packages.config

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Glass" version="0.1.0" targetFramework="net451" />
<package id="Moq" version="4.2.1409.1722" targetFramework="net451" /> <package id="Moq" version="4.2.1409.1722" targetFramework="net451" />
<package id="Rx-Core" version="2.2.5" targetFramework="net451" /> <package id="Rx-Core" version="2.2.5" targetFramework="net451" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net451" /> <package id="Rx-Interfaces" version="2.2.5" targetFramework="net451" />
@ -8,6 +7,7 @@
<package id="Rx-Main" version="2.2.5" targetFramework="net451" /> <package id="Rx-Main" version="2.2.5" targetFramework="net451" />
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net451" /> <package id="Rx-PlatformServices" version="2.2.5" targetFramework="net451" />
<package id="Splat" version="1.6.1" targetFramework="net451" /> <package id="Splat" version="1.6.1" targetFramework="net451" />
<package id="Sprache.SuperJMN" version="2.0.0.50" targetFramework="net451" />
<package id="xunit" version="2.0.0" targetFramework="net451" /> <package id="xunit" version="2.0.0" targetFramework="net451" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net451" /> <package id="xunit.abstractions" version="2.0.0" targetFramework="net451" />
<package id="xunit.assert" version="2.0.0" targetFramework="net451" /> <package id="xunit.assert" version="2.0.0" targetFramework="net451" />

7
samples/XamlTestApplication/Views/MainWindow.xaml

@ -6,10 +6,15 @@
<TabControl Grid.Row="1"> <TabControl Grid.Row="1">
<TabItem Header="Buttons"> <TabItem Header="Buttons">
<StackPanel HorizontalAlignment="Center" Width="200" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" Width="200" VerticalAlignment="Center">
<StackPanel.Styles>
<Style Selector="Button.italic">
<Setter Property="TextBlock.FontStyle" Value="Italic"/>
</Style>
</StackPanel.Styles>
<Button Content="Button" /> <Button Content="Button" />
<Button Content="Button" Background="#119EDA" ToolTip.Tip="Goodbye Cruel World!" /> <Button Content="Button" Background="#119EDA" ToolTip.Tip="Goodbye Cruel World!" />
<Button Content="Default" IsDefault="True" /> <Button Content="Default" IsDefault="True" />
<Button Content="Disabled" IsEnabled="False" /> <Button Content="Disabled" IsEnabled="False" Classes="italic"/>
<Button Content="Disabled" IsEnabled="False" Background="#119eda" /> <Button Content="Disabled" IsEnabled="False" Background="#119eda" />
<ToggleButton Content="Toggle" /> <ToggleButton Content="Toggle" />
<ToggleButton Content="Toggle" IsEnabled="False" /> <ToggleButton Content="Toggle" IsEnabled="False" />

8
samples/XamlTestApplication/XamlTestApplication.csproj

@ -36,14 +36,6 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Glass, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Glass.0.1.0\lib\portable-net45+win+Xamarin.iOS10+MonoAndroid10+MonoTouch10\Glass.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OmniXaml, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\OmniXaml.0.1.0\lib\portable-net45+win+Xamarin.iOS10+MonoAndroid10+MonoTouch10\OmniXaml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL"> <Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\..\packages\Serilog.1.5.9\lib\net45\Serilog.dll</HintPath> <HintPath>..\..\packages\Serilog.1.5.9\lib\net45\Serilog.dll</HintPath>
<Private>True</Private> <Private>True</Private>

2
samples/XamlTestApplication/packages.config

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Glass" version="0.1.0" targetFramework="net451" />
<package id="OmniXaml" version="0.1.0" targetFramework="net451" />
<package id="Rx-Core" version="2.2.5" targetFramework="net46" /> <package id="Rx-Core" version="2.2.5" targetFramework="net46" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net46" /> <package id="Rx-Interfaces" version="2.2.5" targetFramework="net46" />
<package id="Rx-Linq" version="2.2.5" targetFramework="net46" /> <package id="Rx-Linq" version="2.2.5" targetFramework="net46" />

2
src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs

@ -17,7 +17,7 @@ namespace Perspex.Markup.Xaml.Context
var mapping = new DeferredLoaderMapping(); var mapping = new DeferredLoaderMapping();
mapping.Map<XamlDataTemplate>(template => template.Content, new TemplateLoader()); mapping.Map<XamlDataTemplate>(template => template.Content, new TemplateLoader());
var assembler = new ObjectAssembler(wiringContext, new TopDownMemberValueContext(), objectAssemblerSettings); var assembler = new ObjectAssembler(wiringContext, new TopDownValueContext(), objectAssemblerSettings);
_objectAssembler = new TemplateHostingObjectAssembler(assembler, mapping); _objectAssembler = new TemplateHostingObjectAssembler(assembler, mapping);
} }

2
src/Markup/Perspex.Markup.Xaml/Context/PerspexParserFactory.cs

@ -43,7 +43,7 @@ namespace Perspex.Markup.Xaml.Context
private IObjectAssembler GetObjectAssemblerForUndefinedRoot() private IObjectAssembler GetObjectAssemblerForUndefinedRoot()
{ {
return new ObjectAssembler(_wiringContext, new TopDownMemberValueContext()); return new ObjectAssembler(_wiringContext, new TopDownValueContext());
} }
public IXamlParser CreateForReadingSpecificInstance(object rootInstance) public IXamlParser CreateForReadingSpecificInstance(object rootInstance)

4
src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs

@ -77,10 +77,13 @@ namespace Perspex.Markup.Xaml.Context
{ {
new TypeConverterRegistration(typeof(Bitmap), new BitmapTypeConverter()), new TypeConverterRegistration(typeof(Bitmap), new BitmapTypeConverter()),
new TypeConverterRegistration(typeof(Brush), new BrushTypeConverter()), new TypeConverterRegistration(typeof(Brush), new BrushTypeConverter()),
new TypeConverterRegistration(typeof(Classes), new ClassesConverter()),
new TypeConverterRegistration(typeof(ColumnDefinitions), new ColumnDefinitionsTypeConverter()), new TypeConverterRegistration(typeof(ColumnDefinitions), new ColumnDefinitionsTypeConverter()),
new TypeConverterRegistration(typeof(GridLength), new GridLengthTypeConverter()), new TypeConverterRegistration(typeof(GridLength), new GridLengthTypeConverter()),
new TypeConverterRegistration(typeof(PerspexProperty), new PerspexPropertyConverter()),
new TypeConverterRegistration(typeof(RowDefinitions), new RowDefinitionsTypeConverter()), new TypeConverterRegistration(typeof(RowDefinitions), new RowDefinitionsTypeConverter()),
new TypeConverterRegistration(typeof(Thickness), new ThicknessTypeConverter()), new TypeConverterRegistration(typeof(Thickness), new ThicknessTypeConverter()),
new TypeConverterRegistration(typeof(Selector), new SelectorConverter()),
}; };
typeConverterProvider.AddAll(converters); typeConverterProvider.AddAll(converters);
@ -96,6 +99,7 @@ namespace Perspex.Markup.Xaml.Context
new ContentPropertyDefinition(typeof(Decorator), "Child"), new ContentPropertyDefinition(typeof(Decorator), "Child"),
new ContentPropertyDefinition(typeof(ItemsControl), "Items"), new ContentPropertyDefinition(typeof(ItemsControl), "Items"),
new ContentPropertyDefinition(typeof(Panel), "Children"), new ContentPropertyDefinition(typeof(Panel), "Children"),
new ContentPropertyDefinition(typeof(Style), "Setters"),
new ContentPropertyDefinition(typeof(TextBlock), "Text"), new ContentPropertyDefinition(typeof(TextBlock), "Text"),
new ContentPropertyDefinition(typeof(TextBox), "Text"), new ContentPropertyDefinition(typeof(TextBox), "Text"),
new ContentPropertyDefinition(typeof(XamlDataTemplate), "Content"), new ContentPropertyDefinition(typeof(XamlDataTemplate), "Content"),

29
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs

@ -4,9 +4,11 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Glass; using Glass;
using OmniXaml.ObjectAssembler;
using OmniXaml.Typing; using OmniXaml.Typing;
using Perspex.Controls; using Perspex.Controls;
using Perspex.Markup.Xaml.DataBinding; using Perspex.Markup.Xaml.DataBinding;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context namespace Perspex.Markup.Xaml.Context
{ {
@ -23,30 +25,25 @@ namespace Perspex.Markup.Xaml.Context
public override void SetValue(object instance, object value) public override void SetValue(object instance, object value)
{ {
if (ValueRequiresSpecialHandling(value)) if (value is XamlBindingDefinition)
{ {
HandleSpecialValue(instance, value); HandleXamlBindingDefinition((XamlBindingDefinition)value);
}
else
{
base.SetValue(instance, value);
}
}
private void HandleSpecialValue(object instance, object value)
{
var definition = value as XamlBindingDefinition;
if (definition != null)
{
HandleXamlBindingDefinition(definition);
} }
else if (IsPerspexProperty) else if (IsPerspexProperty)
{ {
HandlePerspexProperty(instance, value); HandlePerspexProperty(instance, value);
} }
else if (instance is Setter && _xamlMember.Name == "Value")
{
var setter = (Setter)instance;
var targetType = setter.Property.PropertyType;
var valuePipeline = new ValuePipeline(_xamlMember.TypeRepository, null);
var xamlType = _xamlMember.TypeRepository.GetXamlType(targetType);
base.SetValue(instance, valuePipeline.ConvertValueIfNecessary(value, xamlType));
}
else else
{ {
throw new InvalidOperationException($"Cannot handle the value {value} for member {this} and the instance {instance}"); base.SetValue(instance, value);
} }
} }

33
src/Markup/Perspex.Markup.Xaml/Converters/ClassesConverter.cs

@ -0,0 +1,33 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using OmniXaml.TypeConversion;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Converters
{
public class ClassesConverter : ITypeConverter
{
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value)
{
return new Classes(((string)value).Split(' '));
}
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

72
src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs

@ -0,0 +1,72 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using System.Linq;
using OmniXaml;
using OmniXaml.TypeConversion;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Converters
{
public class PerspexPropertyConverter : ITypeConverter
{
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value)
{
var s = (string)value;
var lastDot = s.LastIndexOf('.');
if (lastDot == -1)
{
throw new NotSupportedException("PerspexProperties must currently be fully qualified.");
}
var typeName = s.Substring(0, lastDot);
var propertyName = s.Substring(lastDot + 1);
var type = context.TypeRepository.GetByQualifiedName(typeName)?.UnderlyingType;
var styleType = context.TypeRepository.GetXamlType(typeof(Style));
// ATTN: SuperJMN
//var style = ((XamlTypeConverterContext)context).TopDownValueContext.GetLastInstance(styleType);
if (type == null)
{
throw new XamlParseException($"Could not find type '{typeName}'.");
}
// First look for non-attached property on the type and then look for an attached property.
var property = PerspexObject.GetRegisteredProperties(type)
.FirstOrDefault(x => x.Name == propertyName && !x.IsAttached);
if (property == null)
{
property = PerspexObject.GetAttachedProperties(type)
.FirstOrDefault(x => x.Name == propertyName);
}
if (property == null)
{
throw new XamlParseException(
$"Could not find PerspexProperty '{typeName}'.{propertyName}.");
}
return property;
}
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

34
src/Markup/Perspex.Markup.Xaml/Converters/SelectorConverter.cs

@ -0,0 +1,34 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using OmniXaml.TypeConversion;
using Perspex.Markup.Xaml.Parsers;
namespace Perspex.Markup.Xaml.Converters
{
public class SelectorConverter : ITypeConverter
{
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value)
{
var parser = new SelectorParser((t, ns) => context.TypeRepository.GetByPrefix(ns ?? "", t).UnderlyingType);
return parser.Parse((string)value);
}
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

1
src/Markup/Perspex.Markup.Xaml/OmniXAML

@ -0,0 +1 @@
Subproject commit 59318a343cdb3e94e5ee33a05b5ffac2771e9640

187
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorGrammar.cs

@ -0,0 +1,187 @@
// 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 System.Globalization;
using System.Linq;
using Perspex.Styling;
using Sprache;
namespace Perspex.Markup.Xaml.Parsers
{
internal class SelectorGrammar
{
public static readonly Parser<char> CombiningCharacter = Parse.Char(
c =>
{
var cat = CharUnicodeInfo.GetUnicodeCategory(c);
return cat == UnicodeCategory.NonSpacingMark ||
cat == UnicodeCategory.SpacingCombiningMark;
},
"Connecting Character");
public static readonly Parser<char> ConnectingCharacter = Parse.Char(
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation,
"Connecting Character");
public static readonly Parser<char> FormattingCharacter = Parse.Char(
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Format,
"Connecting Character");
public static readonly Parser<char> IdentifierStart = Parse.Letter.Or(Parse.Char('_'));
public static readonly Parser<char> IdentifierChar = Parse
.LetterOrDigit
.Or(ConnectingCharacter)
.Or(CombiningCharacter)
.Or(FormattingCharacter);
public static readonly Parser<string> Identifier =
from start in IdentifierStart.Once().Text()
from @char in IdentifierChar.Many().Text()
select start + @char;
public static readonly Parser<string> Namespace =
from ns in Parse.Letter.Many().Text()
from bar in Parse.Char('|')
select ns;
public static readonly Parser<OfTypeSyntax> OfType =
from ns in Namespace.Optional()
from identifier in Identifier
select new OfTypeSyntax
{
TypeName = identifier,
Xmlns = ns.GetOrDefault(),
};
public static readonly Parser<NameSyntax> Name =
from hash in Parse.Char('#')
from identifier in Identifier
select new NameSyntax { Name = identifier };
public static readonly Parser<char> ClassStart = Parse.Char('_').Or(Parse.Letter);
public static readonly Parser<char> ClassChar = ClassStart.Or(Parse.Numeric);
public static readonly Parser<string> ClassIdentifier =
from start in ClassStart.Once().Text()
from @char in ClassChar.Many().Text()
select start + @char;
public static readonly Parser<ClassSyntax> StandardClass =
from dot in Parse.Char('.').Once()
from identifier in ClassIdentifier
select new ClassSyntax { Class = identifier };
public static readonly Parser<ClassSyntax> Pseduoclass =
from colon in Parse.Char(':').Once()
from identifier in ClassIdentifier
select new ClassSyntax { Class = ':' + identifier };
public static readonly Parser<ClassSyntax> Class = StandardClass.Or(Pseduoclass);
public static readonly Parser<PropertySyntax> Property =
from open in Parse.Char('[').Once()
from identifier in Identifier
from eq in Parse.Char('=').Once()
from value in Parse.CharExcept(']').Many().Text()
from close in Parse.Char(']').Once()
select new PropertySyntax { Property = identifier, Value = value };
public static readonly Parser<ChildSyntax> Child = Parse.Char('<').Token().Return(new ChildSyntax());
public static readonly Parser<DescendentSyntax> Descendent =
from child in Parse.WhiteSpace.Many()
select new DescendentSyntax();
public static readonly Parser<TemplateSyntax> Template =
from template in Parse.String("/template/").Token()
select new TemplateSyntax();
public static readonly Parser<ISyntax> SingleSelector =
OfType
.Or<ISyntax>(Name)
.Or<ISyntax>(Class)
.Or<ISyntax>(Property)
.Or<ISyntax>(Child)
.Or<ISyntax>(Template)
.Or<ISyntax>(Descendent);
public static readonly Parser<IEnumerable<ISyntax>> Selector = SingleSelector.Many().End();
public interface ISyntax
{
}
public class OfTypeSyntax : ISyntax
{
public string TypeName { get; set; }
public string Xmlns { get; set; }
public override bool Equals(object obj)
{
return obj is OfTypeSyntax && ((OfTypeSyntax)obj).TypeName == TypeName;
}
}
public class ClassSyntax : ISyntax
{
public string Class { get; set; }
public override bool Equals(object obj)
{
return obj is ClassSyntax && ((ClassSyntax)obj).Class == Class;
}
}
public class NameSyntax : ISyntax
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return obj is NameSyntax && ((NameSyntax)obj).Name == Name;
}
}
public class PropertySyntax : ISyntax
{
public string Property { get; set; }
public string Value { get; set; }
public override bool Equals(object obj)
{
return obj is PropertySyntax &&
((PropertySyntax)obj).Property == Property &&
((PropertySyntax)obj).Value == Value;
}
}
public class ChildSyntax : ISyntax
{
public override bool Equals(object obj)
{
return obj is ChildSyntax;
}
}
public class DescendentSyntax : ISyntax
{
public override bool Equals(object obj)
{
return obj is DescendentSyntax;
}
}
public class TemplateSyntax : ISyntax
{
public override bool Equals(object obj)
{
return obj is TemplateSyntax;
}
}
}
}

83
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs

@ -0,0 +1,83 @@
// 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.Styling;
using Sprache;
namespace Perspex.Markup.Xaml.Parsers
{
/// <summary>
/// Parses a <see cref="Selector"/> from text.
/// </summary>
public class SelectorParser
{
private Func<string, string, Type> _typeResolver;
/// <summary>
/// Initializes a new instance of the <see cref="SelectorParser"/> class.
/// </summary>
/// <param name="typeResolver">
/// The type resolver to use. The type resolver is a function which accepts two strings:
/// a type name and a XML namespace prefix and a type name, and should return the resolved
/// type or throw an exception.
/// </param>
public SelectorParser(Func<string, string, Type> typeResolver)
{
this._typeResolver = typeResolver;
}
/// <summary>
/// Parses a <see cref="Selector"/> from a string.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The parsed selector.</returns>
public Selector Parse(string s)
{
var syntax = SelectorGrammar.Selector.Parse(s);
var result = new Selector();
foreach (var i in syntax)
{
var ofType = i as SelectorGrammar.OfTypeSyntax;
var @class = i as SelectorGrammar.ClassSyntax;
var name = i as SelectorGrammar.NameSyntax;
var property = i as SelectorGrammar.PropertySyntax;
var child = i as SelectorGrammar.ChildSyntax;
var descendent = i as SelectorGrammar.DescendentSyntax;
var template = i as SelectorGrammar.TemplateSyntax;
if (ofType != null)
{
result = result.OfType(_typeResolver(ofType.TypeName, ofType.Xmlns));
}
else if (@class != null)
{
result = result.Class(@class.Class);
}
else if (name != null)
{
result = result.Name(name.Name);
}
else if (property != null)
{
throw new NotImplementedException();
}
else if (child != null)
{
result = result.Child();
}
else if (descendent != null)
{
result = result.Descendent();
}
else if (template != null)
{
result = result.Template();
}
}
return result;
}
}
}

177
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -38,9 +38,12 @@
<Compile Include="..\..\Shared\SharedAssemblyInfo.cs"> <Compile Include="..\..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link> <Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile> </Compile>
<Compile Include="Converters\ClassesConverter.cs" />
<Compile Include="Converters\RowDefinitionsTypeConverter.cs" /> <Compile Include="Converters\RowDefinitionsTypeConverter.cs" />
<Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" /> <Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" />
<Compile Include="Converters\ThicknessTypeConverter.cs" /> <Compile Include="Converters\ThicknessTypeConverter.cs" />
<Compile Include="Converters\PerspexPropertyConverter.cs" />
<Compile Include="Converters\SelectorConverter.cs" />
<Compile Include="Context\PerspexWiringContext.cs" /> <Compile Include="Context\PerspexWiringContext.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" /> <Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="Converters\BrushTypeConverter.cs" /> <Compile Include="Converters\BrushTypeConverter.cs" />
@ -51,6 +54,171 @@
<Compile Include="DataBinding\ChangeTracking\PropertyPath.cs" /> <Compile Include="DataBinding\ChangeTracking\PropertyPath.cs" />
<Compile Include="DataBinding\ChangeTracking\TargettedProperty.cs" /> <Compile Include="DataBinding\ChangeTracking\TargettedProperty.cs" />
<Compile Include="Context\PerspexParserFactory.cs" /> <Compile Include="Context\PerspexParserFactory.cs" />
<Compile Include="OmniXAML\Source\Glass\ChangeTracking\ObservableProperty.cs" />
<Compile Include="OmniXAML\Source\Glass\ChangeTracking\ObservablePropertyChain.cs" />
<Compile Include="OmniXAML\Source\Glass\ChangeTracking\Property.cs" />
<Compile Include="OmniXAML\Source\Glass\ChangeTracking\PropertyChain.cs" />
<Compile Include="OmniXAML\Source\Glass\ChangeTracking\ValueTypePropertyChain.cs" />
<Compile Include="OmniXAML\Source\Glass\DependencySorter.cs" />
<Compile Include="OmniXAML\Source\Glass\Extensions.cs" />
<Compile Include="OmniXAML\Source\Glass\Guard.cs" />
<Compile Include="OmniXAML\Source\Glass\IAdd.cs" />
<Compile Include="OmniXAML\Source\Glass\IDependency.cs" />
<Compile Include="OmniXAML\Source\Glass\ReflectionExtensions.cs" />
<Compile Include="OmniXAML\Source\Glass\StackingLinkedList.cs" />
<Compile Include="OmniXAML\Source\Glass\StackingLinkedListMixin.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\ContentPropertyAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\DependsOnAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\XmlnsDefinitionAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\AddressPack.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\AssemblyNameConfig.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\ConfiguredAssembly.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\ConfiguredAssemblyWithNamespaces.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\ContentProperties.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\ContentPropertyDefinition.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\Converters.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\Route.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\TypeConverterRegistration.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\XamlInstructionBuilder.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Builder\XamlNamespace.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ContentPropertyProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\DefaultObjectAssemblerFactory.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\DeferredLoaderMapping.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\DependencySortingVisitor.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IContentPropertyProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IDeferredLoader.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IMarkupExtension.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\INameScope.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\InstructionNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\InstructionTreeBuilder.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IObjectAssembler.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IObjectAssemblerFactory.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ITypeContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ITypeFactory.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ITypeFeatureProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ITypeProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IValueConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IWiringContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IXamlLoader.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IXamlParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\IXamlParserFactory.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\LookaheadBuffer.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\MarkupExtension.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\MarkupExtensionContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\MemberDependencyNodeSorter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\MemberReverserVisitor.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\NamespaceDeclaration.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\NamespacePrefix.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Command.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\EndMemberCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\EndObjectCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\GetObjectCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\ITopDownValueContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\NamespaceDeclarationCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\StartMemberCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\StartObjectCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\ValueCommand.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\ConstructionArgument.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\CurrentLevelWrapper.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\InstanceProperties.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Level.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\NullLevel.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\ObjectAssembler.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\ObjectAssemblerSettings.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\PreviousLevelWrapper.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\StateCommuter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\TopDownValueContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\TypeOperations.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\ValuePipeline.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\ValueProcessingMode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\XamlSetValueEventArgs.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\Inject.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\IParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\AssignmentNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\IdentifierNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\MarkupExtensionNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\MarkupExtensionNodeToXamlNodesConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\MarkupExtensionParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\Option.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\OptionsCollection.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\PositionalOption.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\PropertyOption.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\StringNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\MarkupExtensions\TreeNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\AttributeAssignment.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\AttributeFeed.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\AttributeParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\DirectiveAssignment.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\IProtoParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\IXmlReader.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\NodeType.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\NsPrefix.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\ProtoXamlInstructionExtensions.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\UnprocessedAttributeBase.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\XamlProtoInstructionParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\ProtoParser\XmlCompatibilityReader.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\XamlInstructions\IXamlInstructionParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\XamlInstructions\OrderAwareXamlInstructionParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Parsers\XamlInstructions\XamlInstructionParser.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\PhaseParserKit.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\PrefixRegistrationMode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ProtoInstructionBuilder.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ProtoXamlInstruction.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Sequence.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TemplateHostingObjectAssembler.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeContextBuilder.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\BuiltInConverters\BooleanConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\BuiltInConverters\DoubleTypeConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\BuiltInConverters\IntTypeConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\BuiltInConverters\StringTypeConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\BuiltInConverters\TypeTypeConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\ITypeConverter.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\ITypeConverterProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\IXamlTypeConverterContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\TypeConverterAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\TypeConverterProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeConversion\XamlTypeConverterContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeFactory.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeFactoryMixin.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeFeatureProvider.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeFeatureProviderBuilder.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\TypeNotFoundException.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\AttachableXamlMember.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\ClrNamespace.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\CoreTypes.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\IXamlMemberValuePlugin.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\IXamlNamespaceRegistry.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\IXamlTypeRepository.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\MemberValuePlugin.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\MutableXamlMember.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\Namespace.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\PrefixRegistration.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\PropertyLocator.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlDirective.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlMember.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlMemberBase.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlName.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlNamespaceRegistry.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlQualifiedName.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlType.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlTypeName.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Typing\XamlTypeRepository.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\ValueConversionException.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Visualization\IVisitor.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Visualization\NodeType.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Visualization\NodeVisualizer.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Visualization\VisualizationNode.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Visualization\VisualizationTag.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\WiringContext.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\XamlInstruction.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\XamlInstructionType.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\XamlLoader.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\XamlLoaderExtensions.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\XamlParseException.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\XamlXmlParser.cs" />
<Compile Include="Parsers\SelectorParser.cs" />
<Compile Include="Parsers\SelectorGrammar.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DataBinding\DataContextChangeSynchronizer.cs" /> <Compile Include="DataBinding\DataContextChangeSynchronizer.cs" />
<Compile Include="DataBinding\IPerspexPropertyBinder.cs" /> <Compile Include="DataBinding\IPerspexPropertyBinder.cs" />
@ -73,6 +241,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />
<None Include="OmniXAML\Source\Glass\ChangeTracking\packages.config" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -114,14 +283,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Glass, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Glass.0.1.0\lib\portable-net45+win+Xamarin.iOS10+MonoAndroid10+MonoTouch10\Glass.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OmniXaml, Version=0.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\OmniXaml.0.1.0\lib\portable-net45+win+Xamarin.iOS10+MonoAndroid10+MonoTouch10\OmniXaml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL"> <Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Serilog.1.5.9\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll</HintPath> <HintPath>..\..\..\packages\Serilog.1.5.9\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll</HintPath>
<Private>True</Private> <Private>True</Private>

5
src/Markup/Perspex.Markup.Xaml/Properties/AssemblyInfo.cs

@ -3,7 +3,10 @@
using System.Reflection; using System.Reflection;
using Perspex.Metadata; using Perspex.Metadata;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Perspex.Markup.Xaml")] [assembly: AssemblyTitle("Perspex.Markup.Xaml")]
[assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Markup.Xaml.MarkupExtensions")] [assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Markup.Xaml.MarkupExtensions")]
[assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Markup.Xaml.Templates")] [assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Markup.Xaml.Templates")]
[assembly: InternalsVisibleTo("Perspex.Markup.Xaml.UnitTests")]

2
src/Markup/Perspex.Markup.Xaml/packages.config

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Glass" version="0.1.0" targetFramework="portable46-net451+win81" />
<package id="OmniXaml" version="0.1.0" targetFramework="portable46-net451+win81" />
<package id="Rx-Core" version="2.2.5" targetFramework="portable46-net451+win81" /> <package id="Rx-Core" version="2.2.5" targetFramework="portable46-net451+win81" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable46-net451+win81" /> <package id="Rx-Interfaces" version="2.2.5" targetFramework="portable46-net451+win81" />
<package id="Rx-Linq" version="2.2.5" targetFramework="portable46-net451+win81" /> <package id="Rx-Linq" version="2.2.5" targetFramework="portable46-net451+win81" />

65
src/Perspex.Base/PerspexObject.cs

@ -71,6 +71,12 @@ namespace Perspex
private static readonly Dictionary<Type, List<PerspexProperty>> s_registered = private static readonly Dictionary<Type, List<PerspexProperty>> s_registered =
new Dictionary<Type, List<PerspexProperty>>(); new Dictionary<Type, List<PerspexProperty>>();
/// <summary>
/// The registered attached properties by owner type.
/// </summary>
private static readonly Dictionary<Type, List<PerspexProperty>> s_attached =
new Dictionary<Type, List<PerspexProperty>>();
/// <summary> /// <summary>
/// The parent object that inherited values are inherited from. /// The parent object that inherited values are inherited from.
/// </summary> /// </summary>
@ -154,7 +160,7 @@ namespace Perspex
_inheritanceParent.PropertyChanged -= ParentPropertyChanged; _inheritanceParent.PropertyChanged -= ParentPropertyChanged;
} }
var inherited = (from property in GetProperties(GetType()) var inherited = (from property in GetRegisteredProperties(GetType())
where property.Inherits where property.Inherits
select new select new
{ {
@ -245,7 +251,7 @@ namespace Perspex
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns> /// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
public static IEnumerable<PerspexProperty> GetProperties(Type type) public static IEnumerable<PerspexProperty> GetRegisteredProperties(Type type)
{ {
Contract.Requires<NullReferenceException>(type != null); Contract.Requires<NullReferenceException>(type != null);
@ -267,6 +273,23 @@ namespace Perspex
} }
} }
/// <summary>
/// Gets all attached <see cref="PerspexProperty"/>s registered by an owner.
/// </summary>
/// <param name="ownerType">The owner type.</param>
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
public static IEnumerable<PerspexProperty> GetAttachedProperties(Type ownerType)
{
List<PerspexProperty> list;
if (s_attached.TryGetValue(ownerType, out list))
{
return list;
}
return Enumerable.Empty<PerspexProperty>();
}
/// <summary> /// <summary>
/// Registers a <see cref="PerspexProperty"/> on a type. /// Registers a <see cref="PerspexProperty"/> on a type.
/// </summary> /// </summary>
@ -293,6 +316,20 @@ namespace Perspex
{ {
list.Add(property); list.Add(property);
} }
if (property.IsAttached)
{
if (!s_attached.TryGetValue(property.OwnerType, out list))
{
list = new List<PerspexProperty>();
s_attached.Add(property.OwnerType, list);
}
if (!list.Contains(property))
{
list.Add(property);
}
}
} }
/// <summary> /// <summary>
@ -432,22 +469,7 @@ namespace Perspex
/// </returns> /// </returns>
public IEnumerable<PerspexProperty> GetRegisteredProperties() public IEnumerable<PerspexProperty> GetRegisteredProperties()
{ {
Type type = GetType(); return GetRegisteredProperties(GetType());
while (type != null)
{
List<PerspexProperty> list;
if (s_registered.TryGetValue(type, out list))
{
foreach (var p in list)
{
yield return p;
}
}
type = type.GetTypeInfo().BaseType;
}
} }
/// <summary> /// <summary>
@ -503,6 +525,7 @@ namespace Perspex
Contract.Requires<NullReferenceException>(property != null); Contract.Requires<NullReferenceException>(property != null);
PriorityValue v; PriorityValue v;
var originalValue = value;
if (!IsRegistered(property)) if (!IsRegistered(property))
{ {
@ -515,10 +538,10 @@ namespace Perspex
if (!TypeUtilities.TryCast(property.PropertyType, value, out value)) if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
{ {
throw new InvalidOperationException(string.Format( throw new InvalidOperationException(string.Format(
"Invalid value for Property '{0}': {1} ({2})", "Invalid value for Property '{0}': '{1}' ({2})",
property.Name, property.Name,
value, originalValue,
value?.GetType().FullName ?? "(null)")); originalValue?.GetType().FullName ?? "(null)"));
} }
if (!_values.TryGetValue(property, out v)) if (!_values.TryGetValue(property, out v))

5
src/Perspex.Base/PerspexProperty.cs

@ -68,6 +68,11 @@ namespace Perspex
Contract.Requires<NullReferenceException>(valueType != null); Contract.Requires<NullReferenceException>(valueType != null);
Contract.Requires<NullReferenceException>(ownerType != null); Contract.Requires<NullReferenceException>(ownerType != null);
if (name.Contains("."))
{
throw new ArgumentException("'name' may not contain periods.");
}
Name = name; Name = name;
PropertyType = valueType; PropertyType = valueType;
OwnerType = ownerType; OwnerType = ownerType;

7
src/Perspex.Styling/Styling/IStyle.cs

@ -9,9 +9,12 @@ namespace Perspex.Styling
public interface IStyle public interface IStyle
{ {
/// <summary> /// <summary>
/// Attaches the style to a control if the style matches. /// Attaches the style to a control if the style's selector matches.
/// </summary> /// </summary>
/// <param name="control">The control to attach to.</param> /// <param name="control">The control to attach to.</param>
void Attach(IStyleable control); /// <param name="container">
/// The control that contains this style. May be null.
/// </param>
void Attach(IStyleable control, IStyleHost container);
} }
} }

28
src/Perspex.Styling/Styling/Selector.cs

@ -6,6 +6,34 @@ using System.Collections.Generic;
namespace Perspex.Styling namespace Perspex.Styling
{ {
/// <summary>
/// A selector in a <see cref="Style"/>.
/// </summary>
/// <remarks>
/// Selectors represented in markup using a CSS-like syntax, e.g. "Button &lt; .dark" which
/// means "A child of a Button with the 'dark' class applied. The preceeding example would be
/// stored in 3 <see cref="Selector"/> objects, linked by the <see cref="Previous"/> property:
/// <list type="number">
/// <item>
/// <term>.dark</term>
/// <description>
/// A selector that selects a control with the 'dark' class applied.
/// </description>
/// </item>
/// <item>
/// <term>&lt;</term>
/// <description>
/// A selector that selects a child of the previous selector.
/// </description>
/// </item>
/// <item>
/// <term>Button</term>
/// <description>
/// A selector that selects a Button type.
/// </description>
/// </item>
/// </list>
/// </remarks>
public class Selector public class Selector
{ {
private readonly Func<IStyleable, SelectorMatch> _evaluate; private readonly Func<IStyleable, SelectorMatch> _evaluate;

2
src/Perspex.Styling/Styling/Selectors.cs

@ -78,7 +78,7 @@ namespace Perspex.Styling
return new Selector( return new Selector(
previous, previous,
x => MatchTemplate(x, previous), x => MatchTemplate(x, previous),
" /deep/ ", " /template/ ",
inTemplate: true, inTemplate: true,
stopTraversal: true); stopTraversal: true);
} }

33
src/Perspex.Styling/Styling/Style.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Linq;
namespace Perspex.Styling namespace Perspex.Styling
{ {
@ -41,16 +42,29 @@ namespace Perspex.Styling
/// Attaches the style to a control if the style's selector matches. /// Attaches the style to a control if the style's selector matches.
/// </summary> /// </summary>
/// <param name="control">The control to attach to.</param> /// <param name="control">The control to attach to.</param>
public void Attach(IStyleable control) /// <param name="container">
/// The control that contains this style. May be null.
/// </param>
public void Attach(IStyleable control, IStyleHost container)
{ {
var description = "Style " + Selector.ToString(); if (Selector != null)
var match = Selector.Match(control); {
var description = "Style " + Selector.ToString();
var match = Selector.Match(control);
if (match.ImmediateResult != false) if (match.ImmediateResult != false)
{
foreach (var setter in Setters)
{
setter.Apply(this, control, match.ObservableResult);
}
}
}
else if (control == container)
{ {
foreach (var setter in Setters) foreach (var setter in Setters)
{ {
setter.Apply(this, control, match.ObservableResult); setter.Apply(this, control, null);
} }
} }
} }
@ -61,7 +75,14 @@ namespace Perspex.Styling
/// <returns>A string representation of the style.</returns> /// <returns>A string representation of the style.</returns>
public override string ToString() public override string ToString()
{ {
return "Style: " + Selector.ToString(); if (Selector != null)
{
return "Style: " + Selector.ToString();
}
else
{
return "Style";
}
} }
} }
} }

4
src/Perspex.Styling/Styling/Styler.cs

@ -21,7 +21,7 @@ namespace Perspex.Styling
if (global != null) if (global != null)
{ {
global.Styles.Attach(control); global.Styles.Attach(control, null);
} }
if (styleContainer != null) if (styleContainer != null)
@ -50,7 +50,7 @@ namespace Perspex.Styling
} }
} }
container.Styles.Attach(control); container.Styles.Attach(control, container);
} }
private IStyleHost GetParentContainer(IStyleHost container) private IStyleHost GetParentContainer(IStyleHost container)

14
src/Perspex.Styling/Styling/Styles.cs

@ -5,13 +5,23 @@ using Perspex.Collections;
namespace Perspex.Styling namespace Perspex.Styling
{ {
/// <summary>
/// A style that consists of a number of child styles.
/// </summary>
public class Styles : PerspexList<IStyle>, IStyle public class Styles : PerspexList<IStyle>, IStyle
{ {
public void Attach(IStyleable control) /// <summary>
/// Attaches the style to a control if the style's selector matches.
/// </summary>
/// <param name="control">The control to attach to.</param>
/// <param name="container">
/// The control that contains this style. May be null.
/// </param>
public void Attach(IStyleable control, IStyleHost container)
{ {
foreach (IStyle style in this) foreach (IStyle style in this)
{ {
style.Attach(control); style.Attach(control, container);
} }
} }
} }

27
tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs

@ -15,22 +15,31 @@ namespace Perspex.Base.UnitTests
PerspexProperty p; PerspexProperty p;
p = Class1.FooProperty; p = Class1.FooProperty;
p = Class2.BarProperty; p = Class2.BarProperty;
p = AttachedOwner.AttachedProperty;
} }
[Fact] [Fact]
public void GetProperties_Returns_Registered_Properties() public void GetRegisteredProperties_Returns_Registered_Properties()
{ {
string[] names = PerspexObject.GetProperties(typeof(Class1)).Select(x => x.Name).ToArray(); string[] names = PerspexObject.GetRegisteredProperties(typeof(Class1)).Select(x => x.Name).ToArray();
Assert.Equal(new[] { "Foo", "Baz", "Qux" }, names); Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names);
} }
[Fact] [Fact]
public void GetProperties_Returns_Registered_Properties_For_Base_Types() public void GetRegisteredProperties_Returns_Registered_Properties_For_Base_Types()
{ {
string[] names = PerspexObject.GetProperties(typeof(Class2)).Select(x => x.Name).ToArray(); string[] names = PerspexObject.GetRegisteredProperties(typeof(Class2)).Select(x => x.Name).ToArray();
Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux" }, names); Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names);
}
[Fact]
public void GetAttachedProperties_Returns_Registered_Properties_For_Base_Types()
{
string[] names = PerspexObject.GetAttachedProperties(typeof(AttachedOwner)).Select(x => x.Name).ToArray();
Assert.Equal(new[] { "Attached" }, names);
} }
private class Class1 : PerspexObject private class Class1 : PerspexObject
@ -56,5 +65,11 @@ namespace Perspex.Base.UnitTests
public static readonly PerspexProperty<double?> FredProperty = public static readonly PerspexProperty<double?> FredProperty =
PerspexProperty.Register<Class2, double?>("Fred"); PerspexProperty.Register<Class2, double?>("Fred");
} }
private class AttachedOwner
{
public static readonly PerspexProperty<string> AttachedProperty =
PerspexProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
}
} }
} }

12
tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs

@ -25,6 +25,18 @@ namespace Perspex.Base.UnitTests
Assert.Equal(false, target.Inherits); Assert.Equal(false, target.Inherits);
} }
[Fact]
public void Name_Cannot_Contain_Periods()
{
Assert.Throws<ArgumentException>(() => new PerspexProperty<string>(
"Foo.Bar",
typeof(Class1),
"Foo",
false,
BindingMode.OneWay,
null));
}
[Fact] [Fact]
public void GetDefaultValue_Returns_Registered_Value() public void GetDefaultValue_Returns_Registered_Value()
{ {

49
tests/Perspex.Styling.UnitTests/StyleTests.cs

@ -24,7 +24,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1(); var target = new Class1();
style.Attach(target); style.Attach(target, null);
Assert.Equal("Foo", target.Foo); Assert.Equal("Foo", target.Foo);
} }
@ -42,7 +42,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1(); var target = new Class1();
style.Attach(target); style.Attach(target, null);
Assert.Equal("foodefault", target.Foo); Assert.Equal("foodefault", target.Foo);
target.Classes.Add("foo"); target.Classes.Add("foo");
Assert.Equal("Foo", target.Foo); Assert.Equal("Foo", target.Foo);
@ -50,6 +50,43 @@ namespace Perspex.Styling.UnitTests
Assert.Equal("foodefault", target.Foo); Assert.Equal("foodefault", target.Foo);
} }
[Fact]
public void Style_With_No_Selector_Should_Apply_To_Containing_Control()
{
Style style = new Style
{
Setters = new[]
{
new Setter(Class1.FooProperty, "Foo"),
},
};
var target = new Class1();
style.Attach(target, target);
Assert.Equal("Foo", target.Foo);
}
[Fact]
public void Style_With_No_Selector_Should_Not_Apply_To_Other_Control()
{
Style style = new Style
{
Setters = new[]
{
new Setter(Class1.FooProperty, "Foo"),
},
};
var target = new Class1();
var other = new Class1();
style.Attach(target, other);
Assert.Equal("foodefault", target.Foo);
}
[Fact] [Fact]
public void LocalValue_Should_Override_Style() public void LocalValue_Should_Override_Style()
{ {
@ -66,7 +103,7 @@ namespace Perspex.Styling.UnitTests
Foo = "Original", Foo = "Original",
}; };
style.Attach(target); style.Attach(target, null);
Assert.Equal("Original", target.Foo); Assert.Equal("Original", target.Foo);
} }
@ -97,7 +134,7 @@ namespace Perspex.Styling.UnitTests
List<string> values = new List<string>(); List<string> values = new List<string>();
target.GetObservable(Class1.FooProperty).Subscribe(x => values.Add(x)); target.GetObservable(Class1.FooProperty).Subscribe(x => values.Add(x));
styles.Attach(target); styles.Attach(target, null);
target.Classes.Add("foo"); target.Classes.Add("foo");
target.Classes.Remove("foo"); target.Classes.Remove("foo");
@ -119,7 +156,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1(); var target = new Class1();
style.Attach(target); style.Attach(target, null);
Assert.Equal("Foo", target.Foo); Assert.Equal("Foo", target.Foo);
} }
@ -139,7 +176,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1(); var target = new Class1();
style.Attach(target); style.Attach(target, null);
Assert.Equal("foodefault", target.Foo); Assert.Equal("foodefault", target.Foo);
target.Classes.Add("foo"); target.Classes.Add("foo");

Loading…
Cancel
Save