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
url = https://github.com/Perspex/HTML-Renderer.git
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>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex.Xaml.Base.UnitTest</RootNamespace>
<AssemblyName>Perspex.Xaml.Base.UnitTest</AssemblyName>
<RootNamespace>Perspex.Markup.Xaml.UnitTests</RootNamespace>
<AssemblyName>Perspex.Markup.Xaml.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@ -43,10 +43,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<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">
<HintPath>..\..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll</HintPath>
<Private>True</Private>
@ -54,13 +50,14 @@
<Reference Include="Octokit">
<HintPath>..\..\packages\Octokit.0.14.0\lib\net45\Octokit.dll</HintPath>
</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">
<HintPath>..\..\packages\Splat.1.6.1\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</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.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>
@ -102,7 +99,9 @@
<ItemGroup>
<Compile Include="BindingDefinitionBuilder.cs" />
<Compile Include="ChangeBranchTest.cs" />
<Compile Include="Converters\PerspexPropertyConverterTest.cs" />
<Compile Include="DataContextChangeSynchronizerTest.cs" />
<Compile Include="Parsers\SelectorGrammarTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SampleModel\Level1.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"?>
<packages>
<package id="Glass" version="0.1.0" 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-Interfaces" version="2.2.5" targetFramework="net451" />
@ -8,6 +7,7 @@
<package id="Rx-Main" 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="Sprache.SuperJMN" version="2.0.0.50" targetFramework="net451" />
<package id="xunit" 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" />

7
samples/XamlTestApplication/Views/MainWindow.xaml

@ -6,10 +6,15 @@
<TabControl Grid.Row="1">
<TabItem Header="Buttons">
<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" Background="#119EDA" ToolTip.Tip="Goodbye Cruel World!" />
<Button Content="Default" IsDefault="True" />
<Button Content="Disabled" IsEnabled="False" />
<Button Content="Disabled" IsEnabled="False" Classes="italic"/>
<Button Content="Disabled" IsEnabled="False" Background="#119eda" />
<ToggleButton Content="Toggle" />
<ToggleButton Content="Toggle" IsEnabled="False" />

8
samples/XamlTestApplication/XamlTestApplication.csproj

@ -36,14 +36,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<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">
<HintPath>..\..\packages\Serilog.1.5.9\lib\net45\Serilog.dll</HintPath>
<Private>True</Private>

2
samples/XamlTestApplication/packages.config

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<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-Interfaces" 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();
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);
}

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

@ -43,7 +43,7 @@ namespace Perspex.Markup.Xaml.Context
private IObjectAssembler GetObjectAssemblerForUndefinedRoot()
{
return new ObjectAssembler(_wiringContext, new TopDownMemberValueContext());
return new ObjectAssembler(_wiringContext, new TopDownValueContext());
}
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(Brush), new BrushTypeConverter()),
new TypeConverterRegistration(typeof(Classes), new ClassesConverter()),
new TypeConverterRegistration(typeof(ColumnDefinitions), new ColumnDefinitionsTypeConverter()),
new TypeConverterRegistration(typeof(GridLength), new GridLengthTypeConverter()),
new TypeConverterRegistration(typeof(PerspexProperty), new PerspexPropertyConverter()),
new TypeConverterRegistration(typeof(RowDefinitions), new RowDefinitionsTypeConverter()),
new TypeConverterRegistration(typeof(Thickness), new ThicknessTypeConverter()),
new TypeConverterRegistration(typeof(Selector), new SelectorConverter()),
};
typeConverterProvider.AddAll(converters);
@ -96,6 +99,7 @@ namespace Perspex.Markup.Xaml.Context
new ContentPropertyDefinition(typeof(Decorator), "Child"),
new ContentPropertyDefinition(typeof(ItemsControl), "Items"),
new ContentPropertyDefinition(typeof(Panel), "Children"),
new ContentPropertyDefinition(typeof(Style), "Setters"),
new ContentPropertyDefinition(typeof(TextBlock), "Text"),
new ContentPropertyDefinition(typeof(TextBox), "Text"),
new ContentPropertyDefinition(typeof(XamlDataTemplate), "Content"),

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

@ -4,9 +4,11 @@
using System;
using System.Reactive.Linq;
using Glass;
using OmniXaml.ObjectAssembler;
using OmniXaml.Typing;
using Perspex.Controls;
using Perspex.Markup.Xaml.DataBinding;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context
{
@ -23,30 +25,25 @@ namespace Perspex.Markup.Xaml.Context
public override void SetValue(object instance, object value)
{
if (ValueRequiresSpecialHandling(value))
if (value is XamlBindingDefinition)
{
HandleSpecialValue(instance, value);
}
else
{
base.SetValue(instance, value);
}
}
private void HandleSpecialValue(object instance, object value)
{
var definition = value as XamlBindingDefinition;
if (definition != null)
{
HandleXamlBindingDefinition(definition);
HandleXamlBindingDefinition((XamlBindingDefinition)value);
}
else if (IsPerspexProperty)
{
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
{
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">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Converters\ClassesConverter.cs" />
<Compile Include="Converters\RowDefinitionsTypeConverter.cs" />
<Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" />
<Compile Include="Converters\ThicknessTypeConverter.cs" />
<Compile Include="Converters\PerspexPropertyConverter.cs" />
<Compile Include="Converters\SelectorConverter.cs" />
<Compile Include="Context\PerspexWiringContext.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="Converters\BrushTypeConverter.cs" />
@ -51,6 +54,171 @@
<Compile Include="DataBinding\ChangeTracking\PropertyPath.cs" />
<Compile Include="DataBinding\ChangeTracking\TargettedProperty.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="DataBinding\DataContextChangeSynchronizer.cs" />
<Compile Include="DataBinding\IPerspexPropertyBinder.cs" />
@ -73,6 +241,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="OmniXAML\Source\Glass\ChangeTracking\packages.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@ -114,14 +283,6 @@
</ProjectReference>
</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">
<HintPath>..\..\..\packages\Serilog.1.5.9\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll</HintPath>
<Private>True</Private>

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

@ -3,7 +3,10 @@
using System.Reflection;
using Perspex.Metadata;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Perspex.Markup.Xaml")]
[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"?>
<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-Interfaces" 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 =
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>
/// The parent object that inherited values are inherited from.
/// </summary>
@ -154,7 +160,7 @@ namespace Perspex
_inheritanceParent.PropertyChanged -= ParentPropertyChanged;
}
var inherited = (from property in GetProperties(GetType())
var inherited = (from property in GetRegisteredProperties(GetType())
where property.Inherits
select new
{
@ -245,7 +251,7 @@ namespace Perspex
/// </summary>
/// <param name="type">The type.</param>
/// <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);
@ -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>
/// Registers a <see cref="PerspexProperty"/> on a type.
/// </summary>
@ -293,6 +316,20 @@ namespace Perspex
{
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>
@ -432,22 +469,7 @@ namespace Perspex
/// </returns>
public IEnumerable<PerspexProperty> GetRegisteredProperties()
{
Type type = 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;
}
return GetRegisteredProperties(GetType());
}
/// <summary>
@ -503,6 +525,7 @@ namespace Perspex
Contract.Requires<NullReferenceException>(property != null);
PriorityValue v;
var originalValue = value;
if (!IsRegistered(property))
{
@ -515,10 +538,10 @@ namespace Perspex
if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
{
throw new InvalidOperationException(string.Format(
"Invalid value for Property '{0}': {1} ({2})",
"Invalid value for Property '{0}': '{1}' ({2})",
property.Name,
value,
value?.GetType().FullName ?? "(null)"));
originalValue,
originalValue?.GetType().FullName ?? "(null)"));
}
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>(ownerType != null);
if (name.Contains("."))
{
throw new ArgumentException("'name' may not contain periods.");
}
Name = name;
PropertyType = valueType;
OwnerType = ownerType;

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

@ -9,9 +9,12 @@ namespace Perspex.Styling
public interface IStyle
{
/// <summary>
/// Attaches the style to a control if the style matches.
/// Attaches the style to a control if the style's selector matches.
/// </summary>
/// <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
{
/// <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
{
private readonly Func<IStyleable, SelectorMatch> _evaluate;

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

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

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

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
namespace Perspex.Styling
{
@ -41,16 +42,29 @@ namespace Perspex.Styling
/// Attaches the style to a control if the style's selector matches.
/// </summary>
/// <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();
var match = Selector.Match(control);
if (Selector != null)
{
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)
{
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>
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)
{
global.Styles.Attach(control);
global.Styles.Attach(control, 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)

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

@ -5,13 +5,23 @@ using Perspex.Collections;
namespace Perspex.Styling
{
/// <summary>
/// A style that consists of a number of child styles.
/// </summary>
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)
{
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;
p = Class1.FooProperty;
p = Class2.BarProperty;
p = AttachedOwner.AttachedProperty;
}
[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]
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
@ -56,5 +65,11 @@ namespace Perspex.Base.UnitTests
public static readonly PerspexProperty<double?> FredProperty =
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);
}
[Fact]
public void Name_Cannot_Contain_Periods()
{
Assert.Throws<ArgumentException>(() => new PerspexProperty<string>(
"Foo.Bar",
typeof(Class1),
"Foo",
false,
BindingMode.OneWay,
null));
}
[Fact]
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();
style.Attach(target);
style.Attach(target, null);
Assert.Equal("Foo", target.Foo);
}
@ -42,7 +42,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1();
style.Attach(target);
style.Attach(target, null);
Assert.Equal("foodefault", target.Foo);
target.Classes.Add("foo");
Assert.Equal("Foo", target.Foo);
@ -50,6 +50,43 @@ namespace Perspex.Styling.UnitTests
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]
public void LocalValue_Should_Override_Style()
{
@ -66,7 +103,7 @@ namespace Perspex.Styling.UnitTests
Foo = "Original",
};
style.Attach(target);
style.Attach(target, null);
Assert.Equal("Original", target.Foo);
}
@ -97,7 +134,7 @@ namespace Perspex.Styling.UnitTests
List<string> values = new List<string>();
target.GetObservable(Class1.FooProperty).Subscribe(x => values.Add(x));
styles.Attach(target);
styles.Attach(target, null);
target.Classes.Add("foo");
target.Classes.Remove("foo");
@ -119,7 +156,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1();
style.Attach(target);
style.Attach(target, null);
Assert.Equal("Foo", target.Foo);
}
@ -139,7 +176,7 @@ namespace Perspex.Styling.UnitTests
var target = new Class1();
style.Attach(target);
style.Attach(target, null);
Assert.Equal("foodefault", target.Foo);
target.Classes.Add("foo");

Loading…
Cancel
Save