Browse Source

Merge branch 'style-resources'

pull/464/head
Steven Kirk 11 years ago
parent
commit
165702c73e
  1. 6
      samples/BindingTest/App.paml
  2. 9
      samples/BindingTest/App.paml.cs
  3. 8
      samples/BindingTest/BindingTest.csproj
  4. 1
      samples/BindingTest/MainWindow.paml
  5. 3
      samples/ControlCatalog/App.paml
  6. 2
      samples/ControlCatalog/App.paml.cs
  7. 3
      samples/TestApplicationShared/App.cs
  8. 1
      samples/XamlTestApplicationPcl/XamlTestApp.cs
  9. 4
      samples/XamlTestApplicationPcl/XamlTestApp.paml
  10. 31
      src/Markup/Perspex.Markup.Xaml/Context/PerspexLifeCycleListener.cs
  11. 4
      src/Markup/Perspex.Markup.Xaml/Context/PerspexNamespaceRegistry.cs
  12. 1
      src/Markup/Perspex.Markup.Xaml/Context/PerspexTypeFeatureProvider.cs
  13. 50
      src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs
  14. 2
      src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs
  15. 33
      src/Markup/Perspex.Markup.Xaml/Converters/SolidColorBrushTypeConverter.cs
  16. 35
      src/Markup/Perspex.Markup.Xaml/Data/Binding.cs
  17. 95
      src/Markup/Perspex.Markup.Xaml/Data/DelayedBinding.cs
  18. 33
      src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs
  19. 65
      src/Markup/Perspex.Markup.Xaml/Data/StyleResourceBinding.cs
  20. 27
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs
  21. 2
      src/Markup/Perspex.Markup.Xaml/OmniXAML
  22. 11
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  23. 21
      src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs
  24. 34
      src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs
  25. 1
      src/Markup/Perspex.Markup/ControlLocator.cs
  26. 10
      src/Perspex.Application/Application.cs
  27. 13
      src/Perspex.Application/Designer/DesignerAssist.cs
  28. 71
      src/Perspex.Base/Data/BindingOperations.cs
  29. 29
      src/Perspex.Base/Data/IBinding.cs
  30. 108
      src/Perspex.Base/Data/InstancedBinding.cs
  31. 22
      src/Perspex.Base/ISupportInitialize.cs
  32. 3
      src/Perspex.Base/Perspex.Base.csproj
  33. 3
      src/Perspex.Base/PerspexObject.cs
  34. 65
      src/Perspex.Base/PerspexObjectExtensions.cs
  35. 95
      src/Perspex.Controls/Control.cs
  36. 1
      src/Perspex.Controls/DropDown.cs
  37. 1
      src/Perspex.Controls/Expander.cs
  38. 2
      src/Perspex.Controls/IControl.cs
  39. 2
      src/Perspex.Controls/ISetLogicalParent.cs
  40. 1
      src/Perspex.Controls/ItemsControl.cs
  41. 1
      src/Perspex.Controls/Mixins/ContentControlMixin.cs
  42. 1
      src/Perspex.Controls/Mixins/SelectableMixin.cs
  43. 1
      src/Perspex.Controls/Panel.cs
  44. 1
      src/Perspex.Controls/Perspex.Controls.csproj
  45. 1
      src/Perspex.Controls/Presenters/ContentPresenter.cs
  46. 11
      src/Perspex.Controls/Presenters/TextPresenter.cs
  47. 42
      src/Perspex.Controls/Primitives/Track.cs
  48. 1
      src/Perspex.Controls/RequestBringIntoViewEventArgs.cs
  49. 1
      src/Perspex.Controls/TextBlock.cs
  50. 1
      src/Perspex.Diagnostics/Debug.cs
  51. 1
      src/Perspex.Diagnostics/ViewModels/LogicalTreeNode.cs
  52. 1
      src/Perspex.Input/IInputElement.cs
  53. 2
      src/Perspex.Input/IMainMenu.cs
  54. 1
      src/Perspex.Input/IPointerDevice.cs
  55. 1
      src/Perspex.Input/KeyboardDevice.cs
  56. 1
      src/Perspex.Input/PointerEventArgs.cs
  57. 1
      src/Perspex.Interactivity/Interactive.cs
  58. 2
      src/Perspex.Layout/ILayoutable.cs
  59. 1
      src/Perspex.SceneGraph/Animation/CrossFade.cs
  60. 1
      src/Perspex.SceneGraph/Animation/IPageTransition.cs
  61. 1
      src/Perspex.SceneGraph/Animation/PageSlide.cs
  62. 35
      src/Perspex.SceneGraph/Media/Mutable/SolidColorBrush.cs
  63. 2
      src/Perspex.SceneGraph/Media/VisualBrush.cs
  64. 3
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  65. 3
      src/Perspex.SceneGraph/Properties/AssemblyInfo.cs
  66. 1
      src/Perspex.SceneGraph/Rendering/IRenderQueueManager.cs
  67. 1
      src/Perspex.SceneGraph/Rendering/RenderQueueManager.cs
  68. 1
      src/Perspex.SceneGraph/Rendering/RendererMixin.cs
  69. 43
      src/Perspex.SceneGraph/Visual.cs
  70. 3
      src/Perspex.SceneGraph/VisualTree/IVisual.cs
  71. 13
      src/Perspex.Styling/LogicalTree/ILogical.cs
  72. 6
      src/Perspex.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs
  73. 4
      src/Perspex.Styling/Perspex.Styling.csproj
  74. 3
      src/Perspex.Styling/Styling/ActivatedObservable.cs
  75. 2
      src/Perspex.Styling/Styling/ISetter.cs
  76. 9
      src/Perspex.Styling/Styling/IStyle.cs
  77. 1
      src/Perspex.Styling/Styling/Selectors.cs
  78. 76
      src/Perspex.Styling/Styling/Setter.cs
  79. 49
      src/Perspex.Styling/Styling/Style.cs
  80. 39
      src/Perspex.Styling/Styling/StyleExtensions.cs
  81. 23
      src/Perspex.Styling/Styling/Styles.cs
  82. 29
      src/Perspex.Themes.Default/Accents/BaseLight.paml
  83. 16
      src/Perspex.Themes.Default/Button.paml
  84. 11
      src/Perspex.Themes.Default/CheckBox.paml
  85. 18
      src/Perspex.Themes.Default/DropDown.paml
  86. 8
      src/Perspex.Themes.Default/GridSplitter.paml
  87. 6
      src/Perspex.Themes.Default/ListBox.paml
  88. 26
      src/Perspex.Themes.Default/MenuItem.paml
  89. 3
      src/Perspex.Themes.Default/Perspex.Themes.Default.csproj
  90. 1
      src/Perspex.Themes.Default/PopupRoot.paml
  91. 4
      src/Perspex.Themes.Default/ProgressBar.paml
  92. 12
      src/Perspex.Themes.Default/RadioButton.paml
  93. 4
      src/Perspex.Themes.Default/ScrollBar.paml
  94. 8
      src/Perspex.Themes.Default/Separator.paml
  95. 6
      src/Perspex.Themes.Default/TabStripItem.paml
  96. 20
      src/Perspex.Themes.Default/TextBox.paml
  97. 18
      src/Perspex.Themes.Default/ToggleButton.paml
  98. 5
      src/Perspex.Themes.Default/ToolTip.paml
  99. 9
      src/Perspex.Themes.Default/TreeView.paml
  100. 2
      src/Perspex.Themes.Default/TreeViewItem.paml

6
samples/BindingTest/App.paml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/perspex">
<Application.Styles>
<StyleInclude Source="resm:Perspex.Themes.Default.DefaultTheme.paml?assembly=Perspex.Themes.Default"/>
<StyleInclude Source="resm:Perspex.Themes.Default.Accents.BaseLight.paml?assembly=Perspex.Themes.Default"/>
</Application.Styles>
</Application>

9
samples/BindingTest/App.cs → samples/BindingTest/App.paml.cs

@ -2,7 +2,7 @@
using Perspex;
using Perspex.Controls;
using Perspex.Diagnostics;
using Perspex.Themes.Default;
using Perspex.Markup.Xaml;
using Serilog;
using Serilog.Filters;
@ -14,7 +14,7 @@ namespace BindingTest
{
RegisterServices();
InitializeSubsystems((int)Environment.OSVersion.Platform);
Styles = new DefaultTheme();
InitializeComponent();
Log.Logger = new LoggerConfiguration()
.Filter.ByIncludingOnly(Matching.WithProperty("Area", "Property"))
@ -36,5 +36,10 @@ namespace BindingTest
window.Show();
app.Run(window);
}
private void InitializeComponent()
{
PerspexXamlLoader.Load(this);
}
}
}

8
samples/BindingTest/BindingTest.csproj

@ -74,7 +74,12 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="App.cs" />
<EmbeddedResource Include="App.paml">
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="App.paml.cs">
<DependentUpon>App.paml</DependentUpon>
</Compile>
<Compile Include="MainWindow.paml.cs">
<DependentUpon>MainWindow.paml</DependentUpon>
</Compile>
@ -90,7 +95,6 @@
<None Include="App.config" />
<EmbeddedResource Include="MainWindow.paml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
<None Include="packages.config" />
<EmbeddedResource Include="TestUserControl.paml" />

1
samples/BindingTest/MainWindow.paml

@ -10,7 +10,6 @@
<TextBox Watermark="Two Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue}" Name="first"/>
<TextBox Watermark="One Way" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneWay}"/>
<TextBox Watermark="One Time" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneTime}"/>
<TextBox Watermark="One Way To Source" UseFloatingWatermark="True" Text="{Binding Path=StringValue, Mode=OneWayToSource}"/>
</StackPanel>
<StackPanel Margin="18" Gap="4" Width="200">
<TextBlock FontSize="16" Text="Collection Bindings"/>

3
samples/ControlCatalog/App.paml

@ -1,5 +1,8 @@
<Application xmlns="https://github.com/perspex">
<Application.Styles>
<StyleInclude Source="resm:Perspex.Themes.Default.DefaultTheme.paml?assembly=Perspex.Themes.Default"/>
<StyleInclude Source="resm:Perspex.Themes.Default.Accents.BaseLight.paml?assembly=Perspex.Themes.Default"/>
<Style Selector="TextBlock.h1">
<Setter Property="Foreground" Value="#212121"/>
<Setter Property="FontSize" Value="20"/>

2
samples/ControlCatalog/App.paml.cs

@ -4,7 +4,6 @@ using Perspex;
using Perspex.Controls;
using Perspex.Diagnostics;
using Perspex.Markup.Xaml;
using Perspex.Themes.Default;
using Serilog;
namespace ControlCatalog
@ -16,7 +15,6 @@ namespace ControlCatalog
RegisterServices();
InitializeSubsystems(GetPlatformId());
InitializeLogging();
Styles = new DefaultTheme();
InitializeComponent();
}

3
samples/TestApplicationShared/App.cs

@ -5,6 +5,7 @@ using System;
using Perspex;
using Perspex.Controls;
using Perspex.Controls.Templates;
using Perspex.Styling;
using Perspex.Themes.Default;
namespace TestApplication
@ -15,7 +16,7 @@ namespace TestApplication
{
RegisterServices();
InitializeSubsystems((int)Environment.OSVersion.Platform);
Styles = new DefaultTheme();
Styles.Add(new DefaultTheme());
Styles.Add(new SampleTabStyle());
DataTemplates = new DataTemplates
{

1
samples/XamlTestApplicationPcl/XamlTestApp.cs

@ -12,7 +12,6 @@ namespace XamlTestApplication
{
RegisterServices();
RegisterPlatform();
Styles = new DefaultTheme();
InitializeComponent();
}

4
samples/XamlTestApplicationPcl/XamlTestApp.paml

@ -1,4 +1,8 @@
<Application x:Class="XamlTestApplicationPcl.XamlTestApp"
xmlns="https://github.com/perspex"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Styles>
<StyleInclude Source="resm:Perspex.Themes.Default.DefaultTheme.paml?assembly=Perspex.Themes.Default"/>
<StyleInclude Source="resm:Perspex.Themes.Default.Accents.BaseLight.paml?assembly=Perspex.Themes.Default"/>
</Application.Styles>
</Application>

31
src/Markup/Perspex.Markup.Xaml/Context/PerspexLifeCycleListener.cs

@ -0,0 +1,31 @@
// 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 OmniXaml;
namespace Perspex.Markup.Xaml.Context
{
public class PerspexLifeCycleListener : IInstanceLifeCycleListener
{
public void OnAfterProperties(object instance)
{
}
public void OnAssociatedToParent(object instance)
{
}
public void OnBegin(object instance)
{
var isi = instance as ISupportInitialize;
isi?.BeginInit();
}
public void OnEnd(object instance)
{
var isi = instance as ISupportInitialize;
isi?.EndInit();
}
}
}

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

@ -58,11 +58,11 @@ namespace Perspex.Markup.Xaml.Context
if (!IsClrNamespace(name))
{
ScanNewAssemblies();
result = _namespaces.FirstOrDefault(x => x.Name == name);
if (result == null)
{
ScanNewAssemblies();
result = _namespaces.FirstOrDefault(x => x.Name == name);
}
}
@ -148,7 +148,7 @@ namespace Perspex.Markup.Xaml.Context
_namespaces.Add(xamlNamespace);
}
var clrNamespaces = namespaces.SelectMany(x => x).Select(x => x.ClrNamespace);
var clrNamespaces = nsa.Select(x => x.ClrNamespace);
xamlNamespace.Addresses.Add(new ConfiguredAssemblyWithNamespaces(assembly, clrNamespaces));
}

1
src/Markup/Perspex.Markup.Xaml/Context/PerspexTypeFeatureProvider.cs

@ -166,6 +166,7 @@ namespace Perspex.Markup.Xaml.Context
RegisterTypeConverter(typeof(RelativeRect), new RelativeRectTypeConverter());
RegisterTypeConverter(typeof(RowDefinitions), new RowDefinitionsTypeConverter());
RegisterTypeConverter(typeof(Selector), new SelectorTypeConverter());
RegisterTypeConverter(typeof(SolidColorBrush), new SolidColorBrushTypeConverter());
RegisterTypeConverter(typeof(Thickness), new ThicknessTypeConverter());
RegisterTypeConverter(typeof(TimeSpan), new TimeSpanTypeConverter());
RegisterTypeConverter(typeof(Uri), new UriTypeConverter());

50
src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs

@ -8,7 +8,9 @@ using System.Runtime.CompilerServices;
using OmniXaml.ObjectAssembler;
using OmniXaml.TypeConversion;
using OmniXaml.Typing;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context
@ -25,7 +27,7 @@ namespace Perspex.Markup.Xaml.Context
if (value is IBinding)
{
SetBinding(instance, member, perspexProperty, (IBinding)value);
SetBinding(instance, member, perspexProperty, context, (IBinding)value);
}
else if (perspexProperty != null)
{
@ -71,9 +73,11 @@ namespace Perspex.Markup.Xaml.Context
object instance,
MutableMember member,
PerspexProperty property,
IValueContext context,
IBinding binding)
{
if (!(AssignBinding(instance, member, binding) || ApplyBinding(instance, property, binding)))
if (!(AssignBinding(instance, member, binding) ||
ApplyBinding(instance, property, context, binding)))
{
throw new InvalidOperationException(
$"Cannot assign to '{member.Name}' on '{instance.GetType()}");
@ -107,15 +111,47 @@ namespace Perspex.Markup.Xaml.Context
return false;
}
private static bool ApplyBinding(object instance, PerspexProperty property, IBinding binding)
private static bool ApplyBinding(
object instance,
PerspexProperty property,
IValueContext context,
IBinding binding)
{
if (property != null)
if (property == null)
{
((IPerspexObject)instance).Bind(property, binding);
return true;
return false;
}
return false;
var control = instance as IControl;
if (control != null)
{
DelayedBinding.Add(control, property, binding);
}
else
{
// The target is not a control, so we need to find an anchor that will let us look
// up named controls and style resources. First look for the closest IControl in
// the TopDownValueContext.
object anchor = context.TopDownValueContext.StoredInstances
.Select(x => x.Instance)
.OfType<IControl>()
.LastOrDefault();
// If a control was not found, then try to find the highest-level style as the XAML
// file could be a XAML file containing only styles.
if (anchor == null)
{
anchor = context.TopDownValueContext.StoredInstances
.Select(x => x.Instance)
.OfType<IStyle>()
.FirstOrDefault();
}
((IPerspexObject)instance).Bind(property, binding, anchor);
}
return true;
}
}
}

2
src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs

@ -36,7 +36,7 @@ namespace Perspex.Markup.Xaml.Converters
{
var styleType = context.TypeRepository.GetByType(typeof(Style));
var style = (Style)context.TopDownValueContext.GetLastInstance(styleType);
type = style.Selector.TargetType;
type = style.Selector?.TargetType;
if (type == null)
{

33
src/Markup/Perspex.Markup.Xaml/Converters/SolidColorBrushTypeConverter.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.Media;
namespace Perspex.Markup.Xaml.Converters
{
public class SolidColorBrushTypeConverter : ITypeConverter
{
public bool CanConvertFrom(IValueContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IValueContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IValueContext context, CultureInfo culture, object value)
{
return Brush.Parse((string)value);
}
public object ConvertTo(IValueContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

35
src/Markup/Perspex.Markup.Xaml/Data/Binding.cs

@ -1,17 +1,16 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Data;
namespace Perspex.Markup.Xaml.Data
{
using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Controls;
using Perspex.Data;
using Markup.Data;
/// <summary>
/// A XAML binding.
/// </summary>
@ -62,15 +61,11 @@ namespace Perspex.Markup.Xaml.Data
/// </summary>
public object Source { get; set; }
/// <summary>
/// Creates a subject that can be used to get and set the value of the binding.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetProperty">The target property. May be null.</param>
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
public ISubject<object> CreateSubject(
/// <inheritdoc/>
public InstancedBinding Initiate(
IPerspexObject target,
PerspexProperty targetProperty)
PerspexProperty targetProperty,
object anchor = null)
{
Contract.Requires<ArgumentNullException>(target != null);
@ -82,7 +77,7 @@ namespace Perspex.Markup.Xaml.Data
if (pathInfo.ElementName != null || ElementName != null)
{
observer = CreateElementObserver(
(IControl)target,
(target as IControl) ?? (anchor as IControl),
pathInfo.ElementName ?? ElementName,
pathInfo.Path);
}
@ -106,12 +101,14 @@ namespace Perspex.Markup.Xaml.Data
throw new NotSupportedException();
}
return new ExpressionSubject(
var subject = new ExpressionSubject(
observer,
targetProperty?.PropertyType ?? typeof(object),
Converter ?? DefaultValueConverter.Instance,
ConverterParameter,
FallbackValue);
return new InstancedBinding(subject, Mode, Priority);
}
private static PathInfo ParsePath(string path)

95
src/Markup/Perspex.Markup.Xaml/Data/DelayedBinding.cs

@ -0,0 +1,95 @@
// 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.Collections.Generic;
using System.Runtime.CompilerServices;
using Perspex.Controls;
using Perspex.Data;
namespace Perspex.Markup.Xaml.Data
{
/// <summary>
/// Provides delayed bindings for controls.
/// </summary>
/// <remarks>
/// The XAML engine applies its bindings in a delayed manner where bindings are only applied
/// when a control is added to the visual tree. This was done because applying bindings as soon
/// as controls are created means that long-form bindings (i.e. bindings that don't use the
/// `{Binding}` markup extension) don't work as the binding is applied to the property before
/// the binding properties are set, and looking at WPF it uses a similar mechanism for bindings
/// that come from XAML.
/// </remarks>
public static class DelayedBinding
{
private static ConditionalWeakTable<IControl, List<Entry>> _entries =
new ConditionalWeakTable<IControl, List<Entry>>();
/// <summary>
/// Adds a delayed binding to a control.
/// </summary>
/// <param name="target">The control.</param>
/// <param name="property">The property on the control to bind to.</param>
/// <param name="binding">The binding.</param>
public static void Add(IControl target, PerspexProperty property, IBinding binding)
{
if (target.IsAttachedToVisualTree)
{
target.Bind(property, binding);
}
else
{
List<Entry> bindings;
if (!_entries.TryGetValue(target, out bindings))
{
bindings = new List<Entry>();
_entries.Add(target, bindings);
// TODO: Make this a weak event listener.
target.AttachedToVisualTree += ApplyBindings;
}
bindings.Add(new Entry(binding, property));
}
}
/// <summary>
/// Applies any delayed bindings to a control.
/// </summary>
/// <param name="control">The control.</param>
public static void ApplyBindings(IControl control)
{
List<Entry> bindings;
if (_entries.TryGetValue(control, out bindings))
{
foreach (var binding in bindings)
{
control.Bind(binding.Property, binding.Binding);
}
_entries.Remove(control);
}
}
private static void ApplyBindings(object sender, VisualTreeAttachmentEventArgs e)
{
var target = (IControl)sender;
ApplyBindings(target);
target.AttachedToVisualTree -= ApplyBindings;
}
private class Entry
{
public Entry(IBinding binding, PerspexProperty property)
{
Binding = binding;
Property = property;
}
public IBinding Binding { get; }
public PerspexProperty Property { get; }
}
}
}

33
src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs

@ -49,28 +49,11 @@ namespace Perspex.Markup.Xaml.Data
/// </summary>
public RelativeSource RelativeSource { get; set; }
/// <summary>
/// Applies the binding to a property on an instance.
/// </summary>
/// <param name="instance">The target instance.</param>
/// <param name="property">The target property.</param>
public void Bind(IPerspexObject instance, PerspexProperty property)
{
var subject = CreateSubject(instance, property);
if (subject != null)
{
Bind(instance, property, subject);
}
}
/// <summary>
/// Creates a subject that can be used to get and set the value of the binding.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetProperty">The target property.</param>
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
public ISubject<object> CreateSubject(IPerspexObject target, PerspexProperty targetProperty)
/// <inheritdoc/>
public InstancedBinding Initiate(
IPerspexObject target,
PerspexProperty targetProperty,
object anchor = null)
{
if (Converter == null)
{
@ -79,10 +62,10 @@ namespace Perspex.Markup.Xaml.Data
var targetType = targetProperty?.PropertyType ?? typeof(object);
var result = new BehaviorSubject<object>(PerspexProperty.UnsetValue);
var children = Bindings.Select(x => x.CreateSubject(target, null));
var input = children.CombineLatest().Select(x => ConvertValue(x, targetType));
var children = Bindings.Select(x => x.Initiate(target, null));
var input = children.Select(x => x.Subject).CombineLatest().Select(x => ConvertValue(x, targetType));
input.Subscribe(result);
return result;
return new InstancedBinding(result, Mode, Priority);
}
/// <summary>

65
src/Markup/Perspex.Markup.Xaml/Data/StyleResourceBinding.cs

@ -0,0 +1,65 @@
// 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.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Data
{
public class StyleResourceBinding : IBinding
{
/// <summary>
/// Initializes a new instance of the <see cref="StyleResourceBinding"/> class.
/// </summary>
/// <param name="name">The resource name.</param>
public StyleResourceBinding(string name)
{
Name = name;
}
/// <inheritdoc/>
public BindingMode Mode => BindingMode.OneTime;
/// <summary>
/// Gets the resource name.
/// </summary>
public string Name { get; }
/// <inheritdoc/>
public BindingPriority Priority => BindingPriority.LocalValue;
/// <inheritdoc/>
public InstancedBinding Initiate(
IPerspexObject target,
PerspexProperty targetProperty,
object anchor = null)
{
var host = (target as IControl) ?? (anchor as IControl);
var style = anchor as IStyle;
var resource = PerspexProperty.UnsetValue;
if (host != null)
{
resource = host.FindStyleResource(Name);
}
else if (style != null)
{
resource = style.FindResource(Name);
}
if (resource != PerspexProperty.UnsetValue)
{
return new InstancedBinding(resource, Priority);
}
else
{
return null;
}
}
}
}

27
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs

@ -0,0 +1,27 @@
// 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.Reactive.Linq;
using OmniXaml;
using Perspex.LogicalTree;
using Perspex.Markup.Xaml.Data;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.MarkupExtensions
{
public class StyleResourceExtension : MarkupExtension
{
public StyleResourceExtension(string name)
{
Name = name;
}
public override object ProvideValue(MarkupExtensionContext extensionContext)
{
return new StyleResourceBinding(this.Name);
}
public string Name { get; set; }
}
}

2
src/Markup/Perspex.Markup.Xaml/OmniXAML

@ -1 +1 @@
Subproject commit 307361d65a187d404da1d4e15a553d918c3ed79c
Subproject commit 75e0dc32fe9a6d97f5b59d2b7d689db2475f444f

11
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -42,6 +42,7 @@
</Compile>
<Compile Include="Context\NameScopeWrapper.cs" />
<Compile Include="Context\PerspexAttachableXamlMember.cs" />
<Compile Include="Context\PerspexLifeCycleListener.cs" />
<Compile Include="Context\PerspexMemberValuePlugin.cs" />
<Compile Include="Context\PerspexNamespaceRegistry.cs" />
<Compile Include="Context\PerspexObjectAssembler.cs" />
@ -55,6 +56,7 @@
<Compile Include="Converters\BitmapTypeConverter.cs" />
<Compile Include="Converters\BrushTypeConverter.cs" />
<Compile Include="Converters\ClassesTypeConverter.cs" />
<Compile Include="Converters\SolidColorBrushTypeConverter.cs" />
<Compile Include="Converters\ColorTypeConverter.cs" />
<Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" />
<Compile Include="Converters\CursorTypeConverter.cs" />
@ -74,21 +76,19 @@
<Compile Include="Converters\TimeSpanTypeConverter.cs" />
<Compile Include="Converters\UriTypeConverter.cs" />
<Compile Include="Data\Binding.cs" />
<Compile Include="Data\DelayedBinding.cs" />
<Compile Include="Data\MultiBinding.cs" />
<Compile Include="Data\RelativeSource.cs" />
<Compile Include="Data\SourceBindingEndpoint.cs" />
<Compile Include="Data\StyleResourceBinding.cs" />
<Compile Include="Data\TargetBindingEndpoint.cs" />
<Compile Include="MarkupExtensions\StyleResourceExtension.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="MarkupExtensions\StaticExtension.cs" />
<Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />
<Compile Include="MarkupExtensions\TypeExtension.cs" />
<Compile Include="OmniXAML\Source\Glass\AutoKeyDictionary.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\EnumExtensions.cs" />
<Compile Include="OmniXAML\Source\Glass\Extensions.cs" />
@ -276,7 +276,6 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="OmniXAML\Source\Glass\ChangeTracking\packages.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>

21
src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs

@ -20,6 +20,7 @@ namespace Perspex.Markup.Xaml
public class PerspexXamlLoader : XmlLoader
{
private static PerspexParserFactory s_parserFactory;
private static IInstanceLifeCycleListener s_lifeCycleListener = new PerspexLifeCycleListener();
/// <summary>
/// Initializes a new instance of the <see cref="PerspexXamlLoader"/> class.
@ -66,21 +67,26 @@ namespace Perspex.Markup.Xaml
// in certain situations, so we try to load .xaml and if that's not found we try .paml.
// Ideally we'd be able to use .xaml everywhere
var assetLocator = PerspexLocator.Current.GetService<IAssetLoader>();
if (assetLocator == null)
{
throw new InvalidOperationException(
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
foreach (var uri in GetUrisFor(type))
{
if (assetLocator.Exists(uri))
{
using (var stream = assetLocator.Open(uri))
{
return Load(stream, new Settings { RootInstance = rootInstance });
var initialize = rootInstance as ISupportInitialize;
initialize?.BeginInit();
return Load(stream, rootInstance);
}
}
}
throw new FileNotFoundException("Unable to find view for " + type.FullName);
}
@ -106,7 +112,7 @@ namespace Perspex.Markup.Xaml
using (var stream = assetLocator.Open(uri))
{
return Load(stream, new Settings { RootInstance = rootInstance });
return Load(stream, rootInstance);
}
}
@ -124,7 +130,7 @@ namespace Perspex.Markup.Xaml
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
{
return Load(stream, new Settings { RootInstance = rootInstance });
return Load(stream, rootInstance);
}
}
@ -151,5 +157,14 @@ namespace Perspex.Markup.Xaml
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm);
}
private object Load(Stream stream, object rootInstance)
{
return base.Load(stream, new Settings
{
RootInstance = rootInstance,
InstanceLifeCycleListener = s_lifeCycleListener,
});
}
}
}

34
src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs

@ -11,6 +11,8 @@ namespace Perspex.Markup.Xaml.Styling
/// </summary>
public class StyleInclude : IStyle
{
private IStyle _loaded;
/// <summary>
/// Gets or sets the source URL.
/// </summary>
@ -19,21 +21,39 @@ namespace Perspex.Markup.Xaml.Styling
/// <summary>
/// Gets the loaded style.
/// </summary>
public IStyle Loaded { get; private set; }
/// <inheritdoc/>
public void Attach(IStyleable control, IStyleHost container)
public IStyle Loaded
{
if (Source != null)
get
{
if (Loaded == null)
if (_loaded == null)
{
var loader = new PerspexXamlLoader();
Loaded = (IStyle)loader.Load(Source);
_loaded = (IStyle)loader.Load(Source);
}
return _loaded;
}
}
/// <inheritdoc/>
public void Attach(IStyleable control, IStyleHost container)
{
if (Source != null)
{
Loaded.Attach(control, container);
}
}
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="PerspexProperty.UnsetValue"/>.
/// </returns>
public object FindResource(string name)
{
return Loaded.FindResource(name);
}
}
}

1
src/Markup/Perspex.Markup/ControlLocator.cs

@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Controls;
using Perspex.LogicalTree;
namespace Perspex.Markup
{

10
src/Perspex.Application/Application.cs

@ -43,10 +43,6 @@ namespace Perspex
private readonly Lazy<IClipboard> _clipboard =
new Lazy<IClipboard>(() => (IClipboard)PerspexLocator.Current.GetService(typeof(IClipboard)));
/// <summary>
/// The styler that will be used to apply styles to controls.
/// </summary>
private readonly Styler _styler = new Styler();
/// <summary>
@ -123,11 +119,7 @@ namespace Perspex
/// <remarks>
/// Global styles apply to all windows in the application.
/// </remarks>
public Styles Styles
{
get;
protected set;
}
public Styles Styles { get; } = new Styles();
/// <summary>
/// Gets the styling parent of the application, which is null.

13
src/Perspex.Application/Designer/DesignerAssist.cs

@ -1,22 +1,20 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using OmniXaml;
using OmniXaml.ObjectAssembler;
using Perspex.Controls;
using Perspex.Controls.Platform;
using Perspex.Markup.Xaml;
using Perspex.Platform;
using Perspex.Styling;
using Perspex.Themes.Default;
namespace Perspex.DesignerSupport
{
using OmniXaml.ObjectAssembler;
class DesignerAssist
{
class DesignerApp : Application
@ -26,7 +24,12 @@ namespace Perspex.DesignerSupport
RegisterServices();
//For now we only support windows
InitializeSubsystems(2);
Styles = new DefaultTheme();
Styles.Add(new DefaultTheme());
var loader = new PerspexXamlLoader();
var baseLight = (IStyle)loader.Load(
new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.paml?assembly=Perspex.Themes.Default"));
Styles.Add(baseLight);
}
}

71
src/Perspex.Base/Data/BindingOperations.cs

@ -0,0 +1,71 @@
// 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.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
namespace Perspex.Data
{
public static class BindingOperations
{
/// <summary>
/// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IPerspexObject"/>.
/// </summary>
/// <param name="target">The target object.</param>
/// <param name="property">The property to bind.</param>
/// <param name="binding">The instanced binding.</param>
/// <param name="anchor">
/// An optional anchor from which to locate required context. When binding to objects that
/// are not in the logical tree, certain types of binding need an anchor into the tree in
/// order to locate named controls or resources. The <paramref name="anchor"/> parameter
/// can be used to provice this context.
/// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Apply(
IPerspexObject target,
PerspexProperty property,
InstancedBinding binding,
object anchor)
{
Contract.Requires<ArgumentNullException>(target != null);
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(binding != null);
var mode = binding.Mode;
if (mode == BindingMode.Default)
{
mode = property.GetMetadata(target.GetType()).DefaultBindingMode;
}
switch (mode)
{
case BindingMode.Default:
case BindingMode.OneWay:
return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority);
case BindingMode.TwoWay:
return new CompositeDisposable(
target.Bind(property, binding.Subject, binding.Priority),
target.GetObservable(property).Subscribe(binding.Subject));
case BindingMode.OneTime:
var source = binding.Subject ?? binding.Observable;
if (source != null)
{
return source.Take(1).Subscribe(x => target.SetValue(property, x, binding.Priority));
}
else
{
target.SetValue(property, binding.Value, binding.Priority);
return Disposable.Empty;
}
case BindingMode.OneWayToSource:
return target.GetObservable(property).Subscribe(binding.Subject);
default:
throw new ArgumentException("Invalid binding mode.");
}
}
}
}

29
src/Perspex.Base/Data/IBinding.cs

@ -1,8 +1,6 @@
// 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.Reactive.Subjects;
namespace Perspex.Data
{
/// <summary>
@ -11,23 +9,22 @@ namespace Perspex.Data
public interface IBinding
{
/// <summary>
/// Gets the binding mode.
/// </summary>
BindingMode Mode { get; }
/// <summary>
/// Gets the binding priority.
/// </summary>
BindingPriority Priority { get; }
/// <summary>
/// Creates a subject that can be used to get and set the value of the binding.
/// Initiates the binding on a target object.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetProperty">The target property. May be null.</param>
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
ISubject<object> CreateSubject(
/// <param name="anchor">
/// An optional anchor from which to locate required context. When binding to objects that
/// are not in the logical tree, certain types of binding need an anchor into the tree in
/// order to locate named controls or resources. The <paramref name="anchor"/> parameter
/// can be used to provice this context.
/// </param>
/// <returns>
/// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
/// </returns>
InstancedBinding Initiate(
IPerspexObject target,
PerspexProperty targetProperty);
PerspexProperty targetProperty,
object anchor = null);
}
}

108
src/Perspex.Base/Data/InstancedBinding.cs

@ -0,0 +1,108 @@
// 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.Reactive.Subjects;
namespace Perspex.Data
{
/// <summary>
/// Holds the result of calling <see cref="IBinding.Initiate"/>.
/// </summary>
/// <remarks>
/// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
/// property on a control's DataContext"; this class represents a binding that has been
/// *instanced* by calling <see cref="IBinding.Initiate(IPerspexObject, PerspexProperty, object)"/>
/// on a target object.
///
/// When a binding is initiated, it can return one of 3 possible sources for the binding:
/// - An <see cref="ISubject{Object}"/> which can be used for any type of binding.
/// - An <see cref="IObservable{Object}"/> which can be used for all types of bindings except
/// <see cref="BindingMode.OneWayToSource"/> and <see cref="BindingMode.TwoWay"/>.
/// - A plain object, which can only represent a <see cref="BindingMode.OneTime"/> binding.
/// </remarks>
public class InstancedBinding
{
/// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="value">
/// The value used for the <see cref="BindingMode.OneTime"/> binding.
/// </param>
/// <param name="priority">The binding priority.</param>
public InstancedBinding(object value, BindingPriority priority = BindingPriority.LocalValue)
{
Mode = BindingMode.OneTime;
Priority = priority;
Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="observable">The observable for a one-way binding.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The binding priority.</param>
public InstancedBinding(
IObservable<object> observable,
BindingMode mode = BindingMode.OneWay,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(observable != null);
if (mode == BindingMode.OneWayToSource || mode == BindingMode.TwoWay)
{
throw new ArgumentException(
"Invalid BindingResult mode: OneWayToSource and TwoWay bindings" +
"require a Subject.");
}
Mode = mode;
Priority = priority;
Observable = observable;
}
/// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="subject">The subject for a two-way binding.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The binding priority.</param>
public InstancedBinding(
ISubject<object> subject,
BindingMode mode = BindingMode.OneWay,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(subject != null);
Mode = mode;
Priority = priority;
Subject = subject;
}
/// <summary>
/// Gets the binding mode with which the binding was initiated.
/// </summary>
public BindingMode Mode { get; }
/// <summary>
/// Gets the binding priority.
/// </summary>
public BindingPriority Priority { get; }
/// <summary>
/// Gets the value used for a <see cref="BindingMode.OneTime"/> binding.
/// </summary>
public object Value { get; }
/// <summary>
/// Gets the observable for a one-way binding.
/// </summary>
public IObservable<object> Observable { get; }
/// <summary>
/// Gets the subject for a two-way binding.
/// </summary>
public ISubject<object> Subject { get; }
}
}

22
src/Perspex.Base/ISupportInitialize.cs

@ -0,0 +1,22 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Perspex
{
/// <summary>
/// Specifies that this object supports a simple, transacted notification for batch
/// initialization.
/// </summary>
public interface ISupportInitialize
{
/// <summary>
/// Signals the object that initialization is starting.
/// </summary>
void BeginInit();
/// <summary>
/// Signals the object that initialization is complete.
/// </summary>
void EndInit();
}
}

3
src/Perspex.Base/Perspex.Base.csproj

@ -44,6 +44,8 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Data\AssignBindingAttribute.cs" />
<Compile Include="Data\BindingOperations.cs" />
<Compile Include="Data\InstancedBinding.cs" />
<Compile Include="Data\IBinding.cs" />
<Compile Include="Data\IndexerDescriptor.cs" />
<Compile Include="Collections\PerspexDictionary.cs" />
@ -55,6 +57,7 @@
<Compile Include="DirectProperty.cs" />
<Compile Include="IPerspexObject.cs" />
<Compile Include="IStyledPropertyMetadata.cs" />
<Compile Include="ISupportInitialize.cs" />
<Compile Include="Metadata\DependsOnAttribute.cs" />
<Compile Include="Metadata\ContentAttribute.cs" />
<Compile Include="PerspexDisposable.cs" />

3
src/Perspex.Base/PerspexObject.cs

@ -188,7 +188,8 @@ namespace Perspex
break;
case BindingMode.TwoWay:
var subject = sourceBinding.Source.GetSubject(sourceBinding.Property, sourceBinding.Priority);
this.Bind(binding.Property, subject, BindingMode.TwoWay, sourceBinding.Priority);
var instanced = new InstancedBinding(subject, BindingMode.TwoWay, sourceBinding.Priority);
BindingOperations.Apply(this, binding.Property, instanced, null);
break;
}
}

65
src/Perspex.Base/PerspexObjectExtensions.cs

@ -167,68 +167,35 @@ namespace Perspex
/// <summary>
/// Binds a property on an <see cref="IPerspexObject"/> to an <see cref="IBinding"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="target">The object.</param>
/// <param name="property">The property to bind.</param>
/// <param name="binding">The binding.</param>
/// <param name="anchor">
/// An optional anchor from which to locate required context. When binding to objects that
/// are not in the logical tree, certain types of binding need an anchor into the tree in
/// order to locate named controls or resources. The <paramref name="anchor"/> parameter
/// can be used to provice this context.
/// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Bind(
this IPerspexObject o,
this IPerspexObject target,
PerspexProperty property,
IBinding binding)
IBinding binding,
object anchor = null)
{
Contract.Requires<ArgumentNullException>(o != null);
Contract.Requires<ArgumentNullException>(target != null);
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(binding != null);
var mode = binding.Mode;
var result = binding.Initiate(target, property, anchor);
if (mode == BindingMode.Default)
if (result != null)
{
mode = property.GetMetadata(o.GetType()).DefaultBindingMode;
return BindingOperations.Apply(target, property, result, anchor);
}
return o.Bind(
property,
binding.CreateSubject(o, property),
mode,
binding.Priority);
}
/// <summary>
/// Binds a property to a subject according to a <see cref="BindingMode"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="property">The property to bind.</param>
/// <param name="source">The binding source.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The binding priority.</param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Bind(
this IPerspexObject o,
PerspexProperty property,
ISubject<object> source,
BindingMode mode,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(o != null);
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(source != null);
switch (mode)
else
{
case BindingMode.Default:
case BindingMode.OneWay:
return o.Bind(property, source, priority);
case BindingMode.TwoWay:
return new CompositeDisposable(
o.Bind(property, source, priority),
o.GetObservable(property).Subscribe(source));
case BindingMode.OneTime:
return source.Take(1).Subscribe(x => o.SetValue(property, x, priority));
case BindingMode.OneWayToSource:
return o.GetObservable(property).Subscribe(source);
default:
throw new ArgumentException("Invalid binding mode.");
return Disposable.Empty;
}
}

95
src/Perspex.Controls/Control.cs

@ -14,6 +14,7 @@ using Perspex.Controls.Templates;
using Perspex.Data;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.LogicalTree;
using Perspex.Styling;
namespace Perspex.Controls
@ -30,7 +31,7 @@ namespace Perspex.Controls
/// - Implements <see cref="IStyleable"/> to allow styling to work on the control.
/// - Implements <see cref="ILogical"/> to form part of a logical tree.
/// </remarks>
public class Control : InputElement, IControl, ISetLogicalParent
public class Control : InputElement, IControl, INamed, ISetLogicalParent, ISupportInitialize
{
/// <summary>
/// Defines the <see cref="DataContext"/> property.
@ -47,6 +48,12 @@ namespace Perspex.Controls
public static readonly StyledProperty<ITemplate<IControl>> FocusAdornerProperty =
PerspexProperty.Register<Control, ITemplate<IControl>>(nameof(FocusAdorner));
/// <summary>
/// Defines the <see cref="Name"/> property.
/// </summary>
public static readonly DirectProperty<Control, string> NameProperty =
PerspexProperty.RegisterDirect<Control, string>(nameof(Name), o => o.Name, (o, v) => o.Name = v);
/// <summary>
/// Defines the <see cref="Parent"/> property.
/// </summary>
@ -77,6 +84,8 @@ namespace Perspex.Controls
public static readonly RoutedEvent<RequestBringIntoViewEventArgs> RequestBringIntoViewEvent =
RoutedEvent.Register<Control, RequestBringIntoViewEventArgs>("RequestBringIntoView", RoutingStrategies.Bubble);
private int _initCount;
private string _name;
private IControl _parent;
private readonly Classes _classes = new Classes();
private DataTemplates _dataTemplates;
@ -85,6 +94,7 @@ namespace Perspex.Controls
private IPerspexList<ILogical> _logicalChildren;
private INameScope _nameScope;
private Styles _styles;
private bool _styled;
private Subject<Unit> _styleDetach = new Subject<Unit>();
/// <summary>
@ -125,6 +135,36 @@ namespace Perspex.Controls
/// </remarks>
public event EventHandler DataContextChanged;
/// <summary>
/// Gets or sets the name of the control.
/// </summary>
/// <remarks>
/// An element's name is used to uniquely identify a control within the control's name
/// scope. Once the element is added to a logical tree, its name cannot be changed.
/// </remarks>
public string Name
{
get
{
return _name;
}
set
{
if (value.Trim() == string.Empty)
{
throw new InvalidOperationException("Cannot set Name to empty string.");
}
if (_styled)
{
throw new InvalidOperationException("Cannot set Name : control already styled.");
}
_name = value;
}
}
/// <summary>
/// Gets or sets the control's classes.
/// </summary>
@ -272,6 +312,28 @@ namespace Perspex.Controls
/// <inheritdoc/>
IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent;
/// <inheritdoc/>
void ISupportInitialize.BeginInit()
{
++_initCount;
}
/// <inheritdoc/>
void ISupportInitialize.EndInit()
{
if (_initCount == 0)
{
throw new InvalidOperationException("BeginInit was not called.");
}
if (--_initCount == 0 && _isAttachedToLogicalTree && !_styled)
{
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
}
/// <summary>
/// Gets a value which indicates whether a change to the <see cref="DataContext"/> is in
/// the process of being notified.
@ -419,18 +481,15 @@ namespace Perspex.Controls
// - That AttachedToLogicalTree signal travels down to the ListBoxItem
if (!_isAttachedToLogicalTree)
{
if (_nameScope == null)
{
_nameScope = NameScope.GetNameScope(this) ?? ((Control)Parent)?._nameScope;
}
_isAttachedToLogicalTree = true;
if (Name != null)
if (_initCount == 0)
{
_nameScope?.Register(Name, this);
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
_isAttachedToLogicalTree = true;
PerspexLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
AttachedToLogicalTree?.Invoke(this, e);
}
@ -567,6 +626,24 @@ namespace Perspex.Controls
return null;
}
private void ApplyStyling()
{
PerspexLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
}
private void RegisterWithNameScope()
{
if (_nameScope == null)
{
_nameScope = NameScope.GetNameScope(this) ?? ((Control)Parent)?._nameScope;
}
if (Name != null)
{
_nameScope?.Register(Name, this);
}
}
private static void ValidateLogicalChild(ILogical c)
{
if (c == null)

1
src/Perspex.Controls/DropDown.cs

@ -7,6 +7,7 @@ using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Input;
using Perspex.Layout;
using Perspex.LogicalTree;
using Perspex.Media;
using Perspex.VisualTree;

1
src/Perspex.Controls/Expander.cs

@ -1,5 +1,6 @@
using Perspex.Animation;
using Perspex.Controls.Primitives;
using Perspex.VisualTree;
namespace Perspex.Controls
{

2
src/Perspex.Controls/IControl.cs

@ -5,7 +5,9 @@ using System;
using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.Layout;
using Perspex.LogicalTree;
using Perspex.Styling;
using Perspex.VisualTree;
namespace Perspex.Controls
{

2
src/Perspex.Controls/ISetLogicalParent.cs

@ -1,6 +1,8 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.LogicalTree;
namespace Perspex.Controls
{
/// <summary>

1
src/Perspex.Controls/ItemsControl.cs

@ -12,6 +12,7 @@ using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Controls.Utils;
using Perspex.LogicalTree;
using Perspex.Metadata;
namespace Perspex.Controls

1
src/Perspex.Controls/Mixins/ContentControlMixin.cs

@ -8,6 +8,7 @@ using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Interactivity;
using Perspex.LogicalTree;
using Perspex.Styling;
namespace Perspex.Controls.Mixins

1
src/Perspex.Controls/Mixins/SelectableMixin.cs

@ -4,6 +4,7 @@
using System;
using Perspex.Interactivity;
using Perspex.Controls.Primitives;
using Perspex.VisualTree;
namespace Perspex.Controls.Mixins
{

1
src/Perspex.Controls/Panel.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Perspex.LogicalTree;
using Perspex.Media;
using Perspex.Metadata;

1
src/Perspex.Controls/Perspex.Controls.csproj

@ -165,7 +165,6 @@
<Compile Include="TopLevel.cs" />
<Compile Include="Primitives\PopupRoot.cs" />
<Compile Include="Utils\UndoRedoHelper.cs" />
<Compile Include="LogicalTreeAttachmentEventArgs.cs" />
<Compile Include="WindowState.cs" />
<Compile Include="Window.cs" />
<Compile Include="RowDefinition.cs" />

1
src/Perspex.Controls/Presenters/ContentPresenter.cs

@ -3,6 +3,7 @@
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.LogicalTree;
namespace Perspex.Controls.Presenters
{

11
src/Perspex.Controls/Presenters/TextPresenter.cs

@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Media;
using Perspex.Styling;
using Perspex.Threading;
using Perspex.VisualTree;
@ -22,10 +23,9 @@ namespace Perspex.Controls.Presenters
TextBox.SelectionEndProperty.AddOwner<TextPresenter>();
private readonly DispatcherTimer _caretTimer;
private bool _caretBlink;
private IObservable<bool> _canScrollHorizontally;
private Brush _highlightBrush;
static TextPresenter()
{
@ -85,11 +85,14 @@ namespace Perspex.Controls.Presenters
var length = Math.Max(selectionStart, selectionEnd) - start;
var rects = FormattedText.HitTestTextRange(start, length);
var brush = new SolidColorBrush(0xff086f9e);
if (_highlightBrush == null)
{
_highlightBrush = (Brush)this.FindStyleResource("HighlightBrush");
}
foreach (var rect in rects)
{
context.FillRectangle(brush, rect);
context.FillRectangle(_highlightBrush, rect);
}
}

42
src/Perspex.Controls/Primitives/Track.cs

@ -3,7 +3,6 @@
using System;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Metadata;
namespace Perspex.Controls.Primitives
@ -34,28 +33,10 @@ namespace Perspex.Controls.Primitives
static Track()
{
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged);
AffectsArrange(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
}
public Track()
{
this.GetObservableWithHistory(ThumbProperty).Subscribe(val =>
{
if (val.Item1 != null)
{
val.Item1.DragDelta -= ThumbDragged;
}
VisualChildren.Clear();
if (val.Item2 != null)
{
val.Item2.DragDelta += ThumbDragged;
VisualChildren.Add(val.Item2);
}
});
}
public double Minimum
{
get { return _minimum; }
@ -151,6 +132,27 @@ namespace Perspex.Controls.Primitives
return finalSize;
}
private void ThumbChanged(PerspexPropertyChangedEventArgs e)
{
var oldThumb = (Thumb)e.OldValue;
var newThumb = (Thumb)e.NewValue;
if (oldThumb != null)
{
oldThumb.DragDelta -= ThumbDragged;
}
LogicalChildren.Clear();
VisualChildren.Clear();
if (newThumb != null)
{
newThumb.DragDelta += ThumbDragged;
LogicalChildren.Add(newThumb);
VisualChildren.Add(newThumb);
}
}
private void ThumbDragged(object sender, VectorEventArgs e)
{
double range = Maximum - Minimum;

1
src/Perspex.Controls/RequestBringIntoViewEventArgs.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Interactivity;
using Perspex.VisualTree;
namespace Perspex.Controls
{

1
src/Perspex.Controls/TextBlock.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive;
using System.Reactive.Linq;
using Perspex.Data;
using Perspex.LogicalTree;
using Perspex.Media;
using Perspex.Metadata;

1
src/Perspex.Diagnostics/Debug.cs

@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using Perspex.Controls;
using Perspex.Data;
using Perspex.VisualTree;
namespace Perspex.Diagnostics
{

1
src/Perspex.Diagnostics/ViewModels/LogicalTreeNode.cs

@ -3,6 +3,7 @@
using System;
using Perspex.Controls;
using Perspex.LogicalTree;
using ReactiveUI;
namespace Perspex.Diagnostics.ViewModels

1
src/Perspex.Input/IInputElement.cs

@ -5,6 +5,7 @@ using System.Diagnostics.Contracts;
using System;
using System.Collections.Generic;
using Perspex.Interactivity;
using Perspex.VisualTree;
namespace Perspex.Input
{

2
src/Perspex.Input/IMainMenu.cs

@ -1,6 +1,8 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.VisualTree;
namespace Perspex.Input
{
/// <summary>

1
src/Perspex.Input/IPointerDevice.cs

@ -3,6 +3,7 @@
using System;
using Perspex.Interactivity;
using Perspex.VisualTree;
namespace Perspex.Input
{

1
src/Perspex.Input/KeyboardDevice.cs

@ -8,6 +8,7 @@ using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using Perspex.Input.Raw;
using Perspex.Interactivity;
using Perspex.VisualTree;
namespace Perspex.Input
{

1
src/Perspex.Input/PointerEventArgs.cs

@ -3,6 +3,7 @@
using System;
using Perspex.Interactivity;
using Perspex.VisualTree;
namespace Perspex.Input
{

1
src/Perspex.Interactivity/Interactive.cs

@ -7,6 +7,7 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Perspex.Layout;
using Perspex.VisualTree;
namespace Perspex.Interactivity
{

2
src/Perspex.Layout/ILayoutable.cs

@ -1,6 +1,8 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.VisualTree;
namespace Perspex.Layout
{
/// <summary>

1
src/Perspex.SceneGraph/Animation/CrossFade.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Reactive.Threading.Tasks;
using System.Threading.Tasks;
using Perspex.VisualTree;
namespace Perspex.Animation
{

1
src/Perspex.SceneGraph/Animation/IPageTransition.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Threading.Tasks;
using Perspex.VisualTree;
namespace Perspex.Animation
{

1
src/Perspex.SceneGraph/Animation/PageSlide.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Reactive.Threading.Tasks;
using System.Threading.Tasks;
using Perspex.Media;
using Perspex.VisualTree;
namespace Perspex.Animation
{

35
src/Perspex.SceneGraph/Media/Mutable/SolidColorBrush.cs

@ -0,0 +1,35 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Perspex.Media.Mutable
{
/// <summary>
/// Fills an area with a solid color.
/// </summary>
/// <remarks>
/// This is a mutable version of the normal immutable <see cref="Perspex.Media.SolidColorBrush"/>
/// for use in XAML. XAML really needs support for immutable data...
/// </remarks>
public class SolidColorBrush : Brush
{
public static readonly DirectProperty<SolidColorBrush, Color> ColorProperty =
PerspexProperty.RegisterDirect<SolidColorBrush, Color>(
"Color",
o => o.Color,
(o, v) => o.Color = v);
/// <summary>
/// Gets the color of the brush.
/// </summary>
public Color Color { get; set; }
/// <summary>
/// Returns a string representation of the brush.
/// </summary>
/// <returns>A string representation of the brush.</returns>
public override string ToString()
{
return Color.ToString();
}
}
}

2
src/Perspex.SceneGraph/Media/VisualBrush.cs

@ -1,6 +1,8 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.VisualTree;
namespace Perspex.Media
{
/// <summary>

3
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -73,6 +73,7 @@
<Compile Include="Media\GradientSpreadMethod.cs" />
<Compile Include="Media\GradientStop.cs" />
<Compile Include="Media\LineGeometry.cs" />
<Compile Include="Media\Mutable\SolidColorBrush.cs" />
<Compile Include="Media\PenLineJoin.cs" />
<Compile Include="Media\PolylineGeometry.cs" />
<Compile Include="Media\RadialGradientBrush.cs" />
@ -86,7 +87,7 @@
<Compile Include="Media\FormattedText.cs" />
<Compile Include="Media\Geometry.cs" />
<Compile Include="Media\IDrawingContext.cs" />
<Compile Include="IVisual.cs" />
<Compile Include="VisualTree\IVisual.cs" />
<Compile Include="Media\Imaging\Bitmap.cs" />
<Compile Include="Media\Imaging\IBitmap.cs" />
<Compile Include="Media\Imaging\RenderTargetBitmap.cs" />

3
src/Perspex.SceneGraph/Properties/AssemblyInfo.cs

@ -6,4 +6,5 @@ using Perspex.Metadata;
[assembly: AssemblyTitle("Perspex.SceneGraph")]
[assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Animation")]
[assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Media")]
[assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Media")]
[assembly: XmlnsDefinition("https://github.com/perspex/mutable", "Perspex.Media.Mutable")]

1
src/Perspex.SceneGraph/Rendering/IRenderQueueManager.cs

@ -3,6 +3,7 @@
using System;
using System.Reactive;
using Perspex.VisualTree;
namespace Perspex.Rendering
{

1
src/Perspex.SceneGraph/Rendering/RenderQueueManager.cs

@ -4,6 +4,7 @@
using System;
using System.Reactive;
using System.Reactive.Subjects;
using Perspex.VisualTree;
namespace Perspex.Rendering
{

1
src/Perspex.SceneGraph/Rendering/RendererMixin.cs

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Perspex.Media;
using Perspex.Platform;
using Perspex.VisualTree;
namespace Perspex.Rendering
{

43
src/Perspex.SceneGraph/Visual.cs

@ -27,7 +27,7 @@ namespace Perspex
/// To traverse the scene graph (aka Visual Tree), use the extension methods defined
/// in <see cref="VisualExtensions"/>.
/// </remarks>
public class Visual : Animatable, IVisual, INamed
public class Visual : Animatable, IVisual
{
/// <summary>
/// Defines the <see cref="Bounds"/> property.
@ -47,12 +47,6 @@ namespace Perspex
public static readonly StyledProperty<bool> IsVisibleProperty =
PerspexProperty.Register<Visual, bool>(nameof(IsVisible), true);
/// <summary>
/// Defines the <see cref="Name"/> property.
/// </summary>
public static readonly DirectProperty<Visual, string> NameProperty =
PerspexProperty.RegisterDirect<Visual, string>(nameof(Name), o => o.Name, (o, v) => o.Name = v);
/// <summary>
/// Defines the <see cref="Opacity"/> property.
/// </summary>
@ -83,11 +77,6 @@ namespace Perspex
public static readonly StyledProperty<int> ZIndexProperty =
PerspexProperty.Register<Visual, int>(nameof(ZIndex));
/// <summary>
/// The name of the visual, if any.
/// </summary>
private string _name;
/// <summary>
/// The visual's bounds relative to its parent.
/// </summary>
@ -176,36 +165,6 @@ namespace Perspex
set { SetValue(IsVisibleProperty, value); }
}
/// <summary>
/// Gets or sets the name of the visual.
/// </summary>
/// <remarks>
/// An element's name is used to uniquely identify a control within the control's name
/// scope. Once the element is added to a visual tree, its name cannot be changed.
/// </remarks>
public string Name
{
get
{
return _name;
}
set
{
if (value.Trim() == string.Empty)
{
throw new InvalidOperationException("Cannot set Name to empty string.");
}
if (VisualRoot != null)
{
throw new InvalidOperationException("Cannot set Name : control already added to tree.");
}
_name = value;
}
}
/// <summary>
/// Gets the opacity of the scene graph node.
/// </summary>

3
src/Perspex.SceneGraph/IVisual.cs → src/Perspex.SceneGraph/VisualTree/IVisual.cs

@ -4,9 +4,8 @@
using System;
using Perspex.Collections;
using Perspex.Media;
using Perspex.VisualTree;
namespace Perspex
namespace Perspex.VisualTree
{
/// <summary>
/// Represents a node in the visual scene graph.

13
src/Perspex.Styling/ILogical.cs → src/Perspex.Styling/LogicalTree/ILogical.cs

@ -1,15 +1,26 @@
// 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.Collections;
namespace Perspex
namespace Perspex.LogicalTree
{
/// <summary>
/// Represents a node in the logical tree.
/// </summary>
public interface ILogical
{
/// <summary>
/// Raised when the control is attached to a rooted logical tree.
/// </summary>
event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
/// <summary>
/// Raised when the control is detached from a rooted logical tree.
/// </summary>
event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
/// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree.
/// </summary>

6
src/Perspex.Controls/LogicalTreeAttachmentEventArgs.cs → src/Perspex.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs

@ -4,11 +4,11 @@
using System;
using Perspex.Styling;
namespace Perspex.Controls
namespace Perspex.LogicalTree
{
/// <summary>
/// Holds the event arguments for the <see cref="Control.AttachedToLogicalTree"/> and
/// <see cref="Control.DetachedFromLogicalTree"/> events.
/// Holds the event arguments for the <see cref="ILogical.AttachedToLogicalTree"/> and
/// <see cref="ILogical.DetachedFromLogicalTree"/> events.
/// </summary>
public class LogicalTreeAttachmentEventArgs : EventArgs
{

4
src/Perspex.Styling/Perspex.Styling.csproj

@ -43,8 +43,9 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="ILogical.cs" />
<Compile Include="LogicalTree\ILogical.cs" />
<Compile Include="LogicalTree\LogicalExtensions.cs" />
<Compile Include="LogicalTree\LogicalTreeAttachmentEventArgs.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Styling\ActivatedSubject.cs" />
<Compile Include="Styling\ActivatedValue.cs" />
@ -63,6 +64,7 @@
<Compile Include="Styling\Style.cs" />
<Compile Include="Styling\StyleActivator.cs" />
<Compile Include="Styling\ActivatedObservable.cs" />
<Compile Include="Styling\StyleExtensions.cs" />
<Compile Include="Styling\Styler.cs" />
<Compile Include="Styling\Styles.cs" />
</ItemGroup>

3
src/Perspex.Styling/Styling/ActivatedObservable.cs

@ -30,6 +30,9 @@ namespace Perspex.Styling
IObservable<object> source,
string description)
{
Contract.Requires<ArgumentNullException>(activator != null);
Contract.Requires<ArgumentNullException>(source != null);
Activator = activator;
Description = description;
Source = source;

2
src/Perspex.Styling/Styling/ISetter.cs

@ -11,7 +11,7 @@ namespace Perspex.Styling
public interface ISetter
{
/// <summary>
/// Applies the setter to the control.
/// Applies the setter to a control.
/// </summary>
/// <param name="style">The style that is being applied.</param>
/// <param name="control">The control.</param>

9
src/Perspex.Styling/Styling/IStyle.cs

@ -16,5 +16,14 @@ namespace Perspex.Styling
/// The control that contains this style. May be null.
/// </param>
void Attach(IStyleable control, IStyleHost container);
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="PerspexProperty.UnsetValue"/>.
/// </returns>
object FindResource(string name);
}
}

1
src/Perspex.Styling/Styling/Selectors.cs

@ -8,6 +8,7 @@ using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reflection;
using Perspex.LogicalTree;
namespace Perspex.Styling
{

76
src/Perspex.Styling/Styling/Setter.cs

@ -58,7 +58,7 @@ namespace Perspex.Styling
}
/// <summary>
/// Applies the setter to the control.
/// Applies the setter to a control.
/// </summary>
/// <param name="style">The style that is being applied.</param>
/// <param name="control">The control.</param>
@ -76,51 +76,75 @@ namespace Perspex.Styling
var binding = Value as IBinding;
if (binding != null)
if (binding == null)
{
if (activator == null)
{
control.Bind(Property, binding);
control.SetValue(Property, Value, BindingPriority.Style);
}
else
{
var subject = binding.CreateSubject(control, Property);
var activated = new ActivatedSubject(activator, subject, description);
Bind(control, Property, binding, activated);
var activated = new ActivatedValue(activator, Value, description);
var instanced = new InstancedBinding(
activated,
BindingMode.OneWay,
BindingPriority.StyleTrigger);
BindingOperations.Apply(control, Property, instanced, null);
}
}
else
{
if (activator == null)
{
control.SetValue(Property, Value, BindingPriority.Style);
}
else
var source = binding.Initiate(control, Property);
if (source != null)
{
var activated = new ActivatedValue(activator, Value, description);
control.Bind(Property, activated, BindingPriority.StyleTrigger);
var cloned = Clone(source, style, activator);
BindingOperations.Apply(control, Property, cloned, null);
}
}
}
private void Bind(
IStyleable control,
PerspexProperty property,
IBinding binding,
ISubject<object> subject)
private InstancedBinding Clone(InstancedBinding sourceInstance, IStyle style, IObservable<bool> activator)
{
var mode = binding.Mode;
InstancedBinding cloned;
if (mode == BindingMode.Default)
if (activator != null)
{
mode = property.GetMetadata(control.GetType()).DefaultBindingMode;
var description = style?.ToString();
if (sourceInstance.Subject != null)
{
var activated = new ActivatedSubject(activator, sourceInstance.Subject, description);
cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
}
else if (sourceInstance.Observable != null)
{
var activated = new ActivatedObservable(activator, sourceInstance.Observable, description);
cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
}
else
{
var activated = new ActivatedValue(activator, sourceInstance.Value, description);
cloned = new InstancedBinding(activated, BindingMode.OneWay, BindingPriority.StyleTrigger);
}
}
else
{
if (sourceInstance.Subject != null)
{
cloned = new InstancedBinding(sourceInstance.Subject, sourceInstance.Mode, BindingPriority.Style);
}
else if (sourceInstance.Observable != null)
{
cloned = new InstancedBinding(sourceInstance.Observable, sourceInstance.Mode, BindingPriority.Style);
}
else
{
cloned = new InstancedBinding(sourceInstance.Value, BindingPriority.Style);
}
}
control.Bind(
property,
subject,
mode,
binding.Priority);
return cloned;
}
}
}

49
src/Perspex.Styling/Styling/Style.cs

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Metadata;
@ -15,6 +14,7 @@ namespace Perspex.Styling
public class Style : IStyle
{
private static readonly IObservable<bool> True = Observable.Never<bool>().StartWith(true);
private Dictionary<string, object> _resources;
/// <summary>
/// Initializes a new instance of the <see cref="Style"/> class.
@ -32,6 +32,32 @@ namespace Perspex.Styling
Selector = selector(new Selector());
}
/// <summary>
/// Gets or sets a dictionary of style resources.
/// </summary>
public IDictionary<string, object> Resources
{
get
{
if (_resources == null)
{
_resources = new Dictionary<string, object>();
}
return _resources;
}
set
{
var resources = Resources;
foreach (var i in value)
{
resources.Add(i);
}
}
}
/// <summary>
/// Gets or sets style's selector.
/// </summary>
@ -77,6 +103,27 @@ namespace Perspex.Styling
}
}
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="PerspexProperty.UnsetValue"/>.
/// </returns>
public object FindResource(string name)
{
object result = null;
if (_resources?.TryGetValue(name, out result) == true)
{
return result;
}
else
{
return PerspexProperty.UnsetValue;
}
}
/// <summary>
/// Returns a string representation of the style.
/// </summary>

39
src/Perspex.Styling/Styling/StyleExtensions.cs

@ -0,0 +1,39 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Perspex.Styling
{
public static class StyleExtensions
{
/// <summary>
/// Tries to find a named style resource.
/// </summary>
/// <param name="control">The control from which to find the resource.</param>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="PerspexProperty.UnsetValue"/>.
/// </returns>
public static object FindStyleResource(this IStyleHost control, string name)
{
Contract.Requires<ArgumentNullException>(control != null);
Contract.Requires<ArgumentNullException>(name != null);
Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(name));
while (control != null)
{
var result = control.Styles.FindResource(name);
if (result != PerspexProperty.UnsetValue)
{
return result;
}
control = control.StylingParent;
}
return PerspexProperty.UnsetValue;
}
}
}

23
src/Perspex.Styling/Styling/Styles.cs

@ -1,6 +1,7 @@
// 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.Collections;
namespace Perspex.Styling
@ -24,5 +25,27 @@ namespace Perspex.Styling
style.Attach(control, container);
}
}
/// <summary>
/// Tries to find a named resource within the style.
/// </summary>
/// <param name="name">The resource name.</param>
/// <returns>
/// The resource if found, otherwise <see cref="PerspexProperty.UnsetValue"/>.
/// </returns>
public object FindResource(string name)
{
foreach (var style in this.Reverse())
{
var result = style.FindResource(name);
if (result != PerspexProperty.UnsetValue)
{
return result;
}
}
return PerspexProperty.UnsetValue;
}
}
}

29
src/Perspex.Themes.Default/Accents/BaseLight.paml

@ -0,0 +1,29 @@
<Style xmlns="https://github.com/perspex"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mut="https://github.com/perspex/mutable"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Style.Resources>
<SolidColorBrush x:Key="ThemeBackgroundBrush">#FFFFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderLightBrush">#FFAAAAAA</SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderMidBrush">#FF888888</SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderDarkBrush">#FF333333</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlLightBrush">#FFFFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlMidBrush">#FFAAAAAA</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlDarkBrush">#FF888888</SolidColorBrush>
<SolidColorBrush x:Key="ThemeForegroundBrush">#FF000000</SolidColorBrush>
<SolidColorBrush x:Key="ThemeForegroundLightBrush">#FF808080</SolidColorBrush>
<SolidColorBrush x:Key="HighlightBrush">#FF086F9E</SolidColorBrush>
<SolidColorBrush x:Key="ThemeAccentBrush">#CC119EDA</SolidColorBrush>
<SolidColorBrush x:Key="ThemeAccentBrush2">#99119EDA</SolidColorBrush>
<SolidColorBrush x:Key="ThemeAccentBrush3">#66119EDA</SolidColorBrush>
<SolidColorBrush x:Key="ThemeAccentBrush4">#33119EDA</SolidColorBrush>
<sys:Double x:Key="ThemeBorderThickness">2</sys:Double>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>
<sys:Double x:Key="FontSizeSmall">10</sys:Double>
<sys:Double x:Key="FontSizeNormal">12</sys:Double>
<sys:Double x:Key="FontSizeLarge">16</sys:Double>
</Style.Resources>
</Style>

16
src/Perspex.Themes.Default/Button.paml

@ -1,15 +1,15 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="Button">
<Setter Property="Background" Value="#ffaaaaaa"/>
<Setter Property="BorderBrush" Value="#ffaaaaaa"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="{StyleResource ThemeControlMidBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderLightBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundBrush}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Padding="3"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
@ -24,12 +24,12 @@
</Setter>
</Style>
<Style Selector="Button:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="#ff888888"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="Button:pressed /template/ Border#border">
<Setter Property="Background" Value="#ff888888"/>
<Setter Property="Background" Value="{StyleResource ThemeControlDarkBrush}"/>
</Style>
<Style Selector="Button:disabled">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Opacity" Value="{StyleResource ThemeDisabledOpacity}"/>
</Style>
</Styles>

11
src/Perspex.Themes.Default/CheckBox.paml

@ -1,7 +1,7 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="CheckBox">
<Setter Property="BorderBrush" Value="#ff333333"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="Auto,*">
@ -12,7 +12,7 @@
Height="18"
VerticalAlignment="Center">
<Path Name="checkMark"
Fill="#ff333333"
Fill="{StyleResource HighlightBrush}"
Width="11"
Height="10"
Stretch="Uniform"
@ -29,6 +29,9 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="CheckBox:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderDarkBrush}"/>
</Style>
<Style Selector="CheckBox /template/ Path#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
@ -36,6 +39,6 @@
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CheckBox:disabled /template/ Border#border">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Opacity" Value="{StyleResource ThemeDisabledOpacity}"/>
</Style>
</Styles>

18
src/Perspex.Themes.Default/DropDown.paml

@ -1,11 +1,12 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="DropDown">
<Setter Property="BorderBrush" Value="#ff707070"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
<Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid ColumnDefinitions="*,Auto">
@ -20,7 +21,7 @@
Focusable="False"
IsChecked="{TemplateBinding IsDropDownOpen, Mode=TwoWay}"
Grid.Column="1">
<Path Fill="Black"
<Path Fill="{StyleResource ThemeForegroundBrush}"
Width="8"
Height="4"
Stretch="Uniform"
@ -33,7 +34,7 @@
MinWidth="{TemplateBinding Path=Bounds.Width}"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">
<Border BorderBrush="Black"
<Border BorderBrush="{StyleResource ThemeBorderMidBrush}"
BorderThickness="1">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
@ -45,10 +46,7 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="CheckBox /template/ Path#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CheckBox:checked /template/ Path#checkMark">
<Setter Property="IsVisible" Value="True"/>
<Style Selector="DropDown:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderDarkBrush}"/>
</Style>
</Styles>

8
src/Perspex.Themes.Default/GridSplitter.paml

@ -1,7 +1,7 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="GridSplitter:vertical">
<Setter Property="Width" Value="6"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Background" Value="{StyleResource ThemeControlLightBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
@ -11,7 +11,7 @@
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Width" Value="4"/>
<Setter Property="Height" Value="4"/>
<Setter Property="Fill" Value="#404040"/>
<Setter Property="Fill" Value="{StyleResource ThemeControlMidBrush}"/>
<Setter Property="Margin" Value="1"/>
</Style>
</StackPanel.Styles>
@ -25,7 +25,7 @@
</Style>
<Style Selector="GridSplitter:horizontal">
<Setter Property="Height" Value="6"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Background" Value="{StyleResource ThemeControlLightBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
@ -35,7 +35,7 @@
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Width" Value="4"/>
<Setter Property="Height" Value="4"/>
<Setter Property="Fill" Value="#404040"/>
<Setter Property="Fill" Value="{StyleResource ThemeControlMidBrush}"/>
<Setter Property="Margin" Value="1"/>
</Style>
</StackPanel.Styles>

6
src/Perspex.Themes.Default/ListBox.paml

@ -1,6 +1,7 @@
<Style xmlns="https://github.com/perspex" Selector="ListBox">
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
@ -10,6 +11,7 @@
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{TemplateBinding Padding}"
MemberSelector="{TemplateBinding MemberSelector}"/>
</ScrollViewer>
</Border>

26
src/Perspex.Themes.Default/MenuItem.paml

@ -37,7 +37,7 @@
</ContentPresenter>
<Path Name="rightArrow"
Data="M0,0L4,3.5 0,7z"
Fill="#ff212121"
Fill="{StyleResource ThemeForegroundBrush}"
Margin="10,0,0,0"
VerticalAlignment="Center"
Grid.Column="3"/>
@ -46,17 +46,17 @@
StaysOpen="True"
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{TemplateBinding Background}"
BorderBrush="#ff999999"
BorderThickness="1"
Padding="2">
BorderBrush="{StyleResource ThemeBorderMidBrush}"
BorderThickness="1">
<ScrollViewer>
<Panel>
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="2"
MemberSelector="{TemplateBinding MemberSelector}"/>
<Rectangle Name="iconSeparator"
Fill="#ffd7d7d7"
Fill="{StyleResource ThemeControlMidBrush}"
HorizontalAlignment="Left"
IsHitTestVisible="False"
Margin="29,2,0,2"
@ -92,17 +92,17 @@
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}"
StaysOpen="True">
<Border Background="{TemplateBinding Background}"
BorderBrush="#ff999999"
BorderThickness="1"
Padding="2">
BorderBrush="{StyleResource ThemeBorderMidBrush}"
BorderThickness="1">
<ScrollViewer>
<Panel>
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="2"
MemberSelector="{TemplateBinding MemberSelector}"/>
<Rectangle Name="iconSeparator"
Fill="#ffd7d7d7"
Fill="{StyleResource ThemeControlMidBrush}"
HorizontalAlignment="Left"
IsHitTestVisible="False"
Margin="29,2,0,2"
@ -118,13 +118,13 @@
</Style>
<Style Selector="MenuItem:selected /template/ Border#root">
<Setter Property="Background" Value="#3d26a0da"/>
<Setter Property="BorderBrush" Value="#ff26a0da"/>
<Setter Property="Background" Value="{StyleResource ThemeAccentBrush4}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeAccentBrush}"/>
</Style>
<Style Selector="MenuItem:pointerover /template/ Border#root">
<Setter Property="Background" Value="#3d26a0da"/>
<Setter Property="BorderBrush" Value="#ff26a0da"/>
<Setter Property="Background" Value="{StyleResource ThemeAccentBrush4}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeAccentBrush}"/>
</Style>
<Style Selector="MenuItem:empty /template/ Path#rightArrow">

3
src/Perspex.Themes.Default/Perspex.Themes.Default.csproj

@ -100,6 +100,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Accents\BaseLight.paml">
<SubType>Designer</SubType>
</EmbeddedResource>
<None Include="app.config" />
<EmbeddedResource Include="Button.paml">
<SubType>Designer</SubType>

1
src/Perspex.Themes.Default/PopupRoot.paml

@ -1,4 +1,5 @@
<Style xmlns="https://github.com/perspex" Selector="PopupRoot">
<Setter Property="Background" Value="{StyleResource ThemeBackgroundBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">

4
src/Perspex.Themes.Default/ProgressBar.paml

@ -1,6 +1,6 @@
<Style xmlns="https://github.com/perspex" Selector="ProgressBar">
<Setter Property="Background" Value="#ffdddddd"/>
<Setter Property="Foreground" Value="#ffbee6fd"/>
<Setter Property="Background" Value="{StyleResource ThemeAccentBrush4}"/>
<Setter Property="Foreground" Value="{StyleResource ThemeAccentBrush}"/>
<Setter Property="MinHeight" Value="14"/>
<Setter Property="MinWidth" Value="200"/>
<Setter Property="Template">

12
src/Perspex.Themes.Default/RadioButton.paml

@ -1,17 +1,18 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="RadioButton">
<Setter Property="BorderBrush" Value="#ff333333"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="Auto,*">
<Ellipse Stroke="{TemplateBinding BorderBrush}"
<Ellipse Name="border"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{TemplateBinding BorderThickness}"
Width="18"
Height="18"
VerticalAlignment="Center"/>
<Ellipse Name="checkMark"
Fill="{TemplateBinding BorderBrush}"
Fill="{StyleResource HighlightBrush}"
Width="10"
Height="10"
Stretch="Uniform"
@ -26,6 +27,9 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="RadioButton:pointerover /template/ Ellipse#border">
<Setter Property="Stroke" Value="{StyleResource ThemeBorderDarkBrush}"/>
</Style>
<Style Selector="RadioButton /template/ Ellipse#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>

4
src/Perspex.Themes.Default/ScrollBar.paml

@ -2,7 +2,7 @@
<Style Selector="ScrollBar">
<Setter Property="Template">
<ControlTemplate>
<Border Background="Silver">
<Border Background="{StyleResource ThemeControlMidBrush}">
<Track Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Path=Value, Mode=TwoWay}"
@ -11,7 +11,7 @@
<Thumb Name="thumb">
<Thumb.Template>
<ControlTemplate>
<Border Background="Gray"/>
<Border Background="{StyleResource ThemeControlDarkBrush}"/>
</ControlTemplate>
</Thumb.Template>
</Thumb>

8
src/Perspex.Themes.Default/Separator.paml

@ -3,19 +3,21 @@
<Style Selector="Separator">
<Setter Property="Template">
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="MenuItem > Separator">
<Setter Property="Background" Value="#ffd7d7d7"/>
<Setter Property="Background" Value="{StyleResource ThemeControlMidBrush}"/>
<Setter Property="Margin" Value="29,1,0,1"/>
<Setter Property="Height" Value="1"/>
</Style>
<Style Selector="ContextMenu > Separator">
<Setter Property="Background" Value="#ffd7d7d7"/>
<Setter Property="Background" Value="{StyleResource ThemeControlMidBrush}"/>
<Setter Property="Margin" Value="29,1,0,1"/>
<Setter Property="Height" Value="1"/>
</Style>

6
src/Perspex.Themes.Default/TabStripItem.paml

@ -1,7 +1,7 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="TabStripItem">
<Setter Property="FontSize" Value="16"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="FontSize" Value="{StyleResource FontSizeLarge}"/>
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundLightBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
@ -15,6 +15,6 @@
</Setter>
</Style>
<Style Selector="TabStripItem:selected">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundBrush}"/>
</Style>
</Styles>

20
src/Perspex.Themes.Default/TextBox.paml

@ -1,23 +1,22 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#ff707070"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Background" Value="{StyleResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="2">
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"
HorizontalScrollBarVisibility="{TemplateBinding HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{TemplateBinding VerticalScrollBarVisibility}">
<StackPanel Margin="{TemplateBinding Padding}">
<TextBlock Name="floatingWatermark"
Foreground="#ff007ACC"
FontSize="10"
Foreground="{StyleResource ThemeAccentBrush}"
FontSize="{StyleResource FontSizeSmall}"
Text="{TemplateBinding Watermark}">
<TextBlock.IsVisible>
<MultiBinding Converter="{Static BoolConverters.And}">
@ -48,7 +47,10 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TextBox:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderDarkBrush}"/>
</Style>
<Style Selector="TextBox:focus /template/ Border#border">
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderDarkBrush}"/>
</Style>
</Styles>

18
src/Perspex.Themes.Default/ToggleButton.paml

@ -1,9 +1,9 @@
<Styles xmlns="https://github.com/perspex">
<Style Selector="ToggleButton">
<Setter Property="Background" Value="#ffaaaaaa"/>
<Setter Property="BorderBrush" Value="#ffaaaaaa"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="{StyleResource ThemeControlMidBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderLightBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundBrush}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
@ -24,16 +24,16 @@
</Setter>
</Style>
<Style Selector="ToggleButton:checked /template/ Border#border">
<Setter Property="Background" Value="#ff777777"/>
<Setter Property="BorderBrush" Value="#ff777777"/>
<Setter Property="Background" Value="{StyleResource ThemeControlDarkBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="#ff888888"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pressed /template/ Border#border">
<Setter Property="Background" Value="#ff888888"/>
<Setter Property="Background" Value="{StyleResource ThemeControlDarkBrush}"/>
</Style>
<Style Selector="ToggleButton:disabled">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Opacity" Value="{StyleResource ThemeDisabledOpacity}"/>
</Style>
</Styles>

5
src/Perspex.Themes.Default/ToolTip.paml

@ -1,6 +1,7 @@
<Style xmlns="https://github.com/perspex" Selector="ToolTip">
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="{StyleResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4,2"/>
<Setter Property="Template">
<ControlTemplate>

9
src/Perspex.Themes.Default/TreeView.paml

@ -1,16 +1,17 @@
<Style xmlns="https://github.com/perspex" Selector="TreeView">
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="4">
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanScrollHorizontally="True">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{TemplateBinding Padding}"
MemberSelector="{TemplateBinding MemberSelector}"/>
</ScrollViewer>
</Border>

2
src/Perspex.Themes.Default/TreeViewItem.paml

@ -36,7 +36,7 @@
Height="12"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Path Fill="Black"
<Path Fill="{StyleResource ThemeForegroundBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 2 L 4 6 L 0 10 Z"/>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save