Browse Source

Merge branch 'master' into RemoveLegacyDesigner

pull/1622/head
Jeremy Koritzinsky 9 years ago
committed by GitHub
parent
commit
cdd72a4c12
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Avalonia.sln
  2. 8
      build/Binding.props
  3. 5
      build/SourceLink.props
  4. 56
      packages.cake
  5. 4
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  6. 20
      samples/RenderTest/Pages/AnimationsPage.xaml
  7. 2
      samples/RenderTest/Pages/ClippingPage.xaml
  8. 7
      src/Android/Avalonia.Android/Resources/Resource.Designer.cs
  9. 33
      src/Avalonia.Animation/Animation.cs
  10. 17
      src/Avalonia.Animation/AnimationSetter.cs
  11. 18
      src/Avalonia.Animation/AnimatorAttribute.cs
  12. 21
      src/Avalonia.Animation/DoubleSetter.cs
  13. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  14. 2
      src/Avalonia.Base/AvaloniaObject.cs
  15. 5
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  16. 3
      src/Avalonia.Base/Data/BindingNotification.cs
  17. 2
      src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs
  18. 2
      src/Avalonia.Base/Data/Converters/BoolConverters.cs
  19. 2
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  20. 2
      src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs
  21. 2
      src/Avalonia.Base/Data/Converters/FuncValueConverter.cs
  22. 2
      src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs
  23. 2
      src/Avalonia.Base/Data/Converters/IValueConverter.cs
  24. 2
      src/Avalonia.Base/Data/Converters/StringConverters.cs
  25. 4
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  26. 2
      src/Avalonia.Base/Data/Core/CommonPropertyNames.cs
  27. 2
      src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs
  28. 2
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  29. 4
      src/Avalonia.Base/Data/Core/ExpressionNodeBuilder.cs
  30. 45
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  31. 4
      src/Avalonia.Base/Data/Core/ExpressionParseException.cs
  32. 2
      src/Avalonia.Base/Data/Core/ISettableNode.cs
  33. 2
      src/Avalonia.Base/Data/Core/ITransformNode.cs
  34. 2
      src/Avalonia.Base/Data/Core/IndexerNode.cs
  35. 2
      src/Avalonia.Base/Data/Core/LogicalNotNode.cs
  36. 3
      src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs
  37. 2
      src/Avalonia.Base/Data/Core/Parsers/ArgumentListParser.cs
  38. 2
      src/Avalonia.Base/Data/Core/Parsers/ExpressionParser.cs
  39. 2
      src/Avalonia.Base/Data/Core/Parsers/IdentifierParser.cs
  40. 2
      src/Avalonia.Base/Data/Core/Parsers/Reader.cs
  41. 2
      src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs
  42. 2
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  43. 2
      src/Avalonia.Base/Data/Core/Plugins/DataValidatiorBase.cs
  44. 2
      src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
  45. 2
      src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs
  46. 2
      src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs
  47. 2
      src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs
  48. 2
      src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs
  49. 2
      src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
  50. 2
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  51. 2
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  52. 2
      src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs
  53. 2
      src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs
  54. 2
      src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs
  55. 2
      src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs
  56. 4
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  57. 2
      src/Avalonia.Base/Data/Core/StreamNode.cs
  58. 3
      src/Avalonia.Base/Properties/AssemblyInfo.cs
  59. 764
      src/Avalonia.Controls/Control.cs
  60. 15
      src/Avalonia.Controls/ControlExtensions.cs
  61. 80
      src/Avalonia.Controls/DropDown.cs
  62. 35
      src/Avalonia.Controls/DropDownItem.cs
  63. 6
      src/Avalonia.Controls/HotkeyManager.cs
  64. 30
      src/Avalonia.Controls/IControl.cs
  65. 24
      src/Avalonia.Controls/Utils/AncestorFinder.cs
  66. 4
      src/Avalonia.Input/InputElement.cs
  67. 3
      src/Avalonia.Styling/Avalonia.Styling.csproj
  68. 16
      src/Avalonia.Styling/Controls/Classes.cs
  69. 0
      src/Avalonia.Styling/Controls/IPseudoClasses.cs
  70. 2
      src/Avalonia.Styling/Controls/ISetInheritanceParent.cs
  71. 2
      src/Avalonia.Styling/Controls/ISetLogicalParent.cs
  72. 36
      src/Avalonia.Styling/Controls/NameScope.cs
  73. 12
      src/Avalonia.Styling/Controls/NameScopeExtensions.cs
  74. 0
      src/Avalonia.Styling/INamed.cs
  75. 42
      src/Avalonia.Styling/IStyledElement.cs
  76. 97
      src/Avalonia.Styling/LogicalTree/ControlLocator.cs
  77. 783
      src/Avalonia.Styling/StyledElement.cs
  78. 14
      src/Avalonia.Styling/Styling/IRequiresTemplateInSetter.cs
  79. 1
      src/Avalonia.Styling/Styling/IStyleable.cs
  80. 9
      src/Avalonia.Styling/Styling/Setter.cs
  81. 15
      src/Avalonia.Styling/app.config
  82. 22
      src/Avalonia.Visuals/Animation/TransformSetter.cs
  83. 3
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  84. 5
      src/Avalonia.Visuals/Media/Transform.cs
  85. 2
      src/Avalonia.Visuals/Visual.cs
  86. 46
      src/Avalonia.Visuals/VisualTree/VisualLocator.cs
  87. 1
      src/Directory.Build.props
  88. 13
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  89. 2
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  90. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  91. 4
      src/Markup/Avalonia.Markup.Xaml/Converters/SelectorTypeConverter.cs
  92. 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  93. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs
  94. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
  95. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  96. 5
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs
  97. 4
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaRuntimeTypeProvider.cs
  98. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
  99. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
  100. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

2
Avalonia.sln

@ -131,6 +131,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props

8
build/Binding.props

@ -0,0 +1,8 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.3.0" />
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
<PackageReference Include="System.ComponentModel.Primitives" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
</ItemGroup>
</Project>

5
build/SourceLink.props

@ -0,0 +1,5 @@
<Project>
<ItemGroup>
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.0" PrivateAssets="All" />
</ItemGroup>
</Project>

56
packages.cake

@ -155,38 +155,28 @@ public class Packages
var coreLibraries = new string[][]
{
new [] { "./src/", "Avalonia.Animation", ".dll" },
new [] { "./src/", "Avalonia.Animation", ".xml" },
new [] { "./src/", "Avalonia.Base", ".dll" },
new [] { "./src/", "Avalonia.Base", ".xml" },
new [] { "./src/", "Avalonia.Controls", ".dll" },
new [] { "./src/", "Avalonia.Controls", ".xml" },
new [] { "./src/", "Avalonia.DesignerSupport", ".dll" },
new [] { "./src/", "Avalonia.DesignerSupport", ".xml" },
new [] { "./src/", "Avalonia.Diagnostics", ".dll" },
new [] { "./src/", "Avalonia.Diagnostics", ".xml" },
new [] { "./src/", "Avalonia.Input", ".dll" },
new [] { "./src/", "Avalonia.Input", ".xml" },
new [] { "./src/", "Avalonia.Interactivity", ".dll" },
new [] { "./src/", "Avalonia.Interactivity", ".xml" },
new [] { "./src/", "Avalonia.Layout", ".dll" },
new [] { "./src/", "Avalonia.Layout", ".xml" },
new [] { "./src/", "Avalonia.Logging.Serilog", ".dll" },
new [] { "./src/", "Avalonia.Logging.Serilog", ".xml" },
new [] { "./src/", "Avalonia.Visuals", ".dll" },
new [] { "./src/", "Avalonia.Visuals", ".xml" },
new [] { "./src/", "Avalonia.Styling", ".dll" },
new [] { "./src/", "Avalonia.Styling", ".xml" },
new [] { "./src/", "Avalonia.Themes.Default", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".xml" },
new [] { "./src/Markup/", "Avalonia.Markup", ".dll" },
new [] { "./src/Markup/", "Avalonia.Markup", ".xml" },
new [] { "./src/Markup/", "Avalonia.Markup.Xaml", ".dll" },
new [] { "./src/Markup/", "Avalonia.Markup.Xaml", ".xml" }
new [] { "./src/", "Avalonia.Animation"},
new [] { "./src/", "Avalonia.Base"},
new [] { "./src/", "Avalonia.Controls"},
new [] { "./src/", "Avalonia.DesignerSupport"},
new [] { "./src/", "Avalonia.Diagnostics"},
new [] { "./src/", "Avalonia.Input"},
new [] { "./src/", "Avalonia.Interactivity"},
new [] { "./src/", "Avalonia.Layout"},
new [] { "./src/", "Avalonia.Logging.Serilog"},
new [] { "./src/", "Avalonia.Visuals"},
new [] { "./src/", "Avalonia.Styling"},
new [] { "./src/", "Avalonia.Themes.Default"},
new [] { "./src/Markup/", "Avalonia.Markup"},
new [] { "./src/Markup/", "Avalonia.Markup.Xaml"},
};
var coreLibrariesFiles = coreLibraries.Select((lib) => {
return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard2.0/" + lib[1] + lib[2]);
var extensionsToPack = new [] {".dll", ".xml", ".pdb"};
var coreLibrariesFiles = coreLibraries
.Zip(extensionsToPack, (lib, ext) => new { lib, ext})
.Select((lib) => {
return (FilePath)context.File(lib.lib[0] + lib.lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard2.0/" + lib.lib[1] + lib.ext);
}).ToList();
var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
@ -207,16 +197,14 @@ public class Packages
};
});
var net45RuntimePlatformExtensions = new [] {".xml", ".dll"};
var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => {
var net45RuntimePlatform = extensionsToPack.Select(libSuffix => {
return new NuSpecContent {
Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/net461/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath,
Target = "lib/net45"
};
});
var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"};
var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => {
var netCoreRuntimePlatform = extensionsToPack.Select(libSuffix => {
return new NuSpecContent {
Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath,
Target = "lib/netcoreapp2.0"

4
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@ -2,12 +2,14 @@ using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Markup;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Data.Converters;
using Avalonia.Data;
namespace ControlCatalog.Pages
{

20
samples/RenderTest/Pages/AnimationsPage.xaml

@ -48,13 +48,13 @@
PlaybackDirection="AlternateReverse"
Easing="SineEaseInOut">
<KeyFrame Cue="20%">
<TransformSetter Property="RotateTransform.Angle" Value="45"/>
<Setter Property="RotateTransform.Angle" Value="45"/>
</KeyFrame>
<KeyFrame Cue="50%">
<TransformSetter Property="ScaleTransform.ScaleX" Value="1.5"/>
<Setter Property="ScaleTransform.ScaleX" Value="1.5"/>
</KeyFrame>
<KeyFrame Cue="80%">
<TransformSetter Property="RotateTransform.Angle" Value="120"/>
<Setter Property="RotateTransform.Angle" Value="120"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -63,8 +63,8 @@
<Style.Animations>
<Animation Duration="0:0:0.5" Easing="SineEaseInOut">
<KeyFrame Cue="50%">
<TransformSetter Property="ScaleTransform.ScaleX" Value="0.8"/>
<TransformSetter Property="ScaleTransform.ScaleY" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleX" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.8"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -76,8 +76,8 @@
Easing="QuadraticEaseInOut"
RepeatCount="Loop">
<KeyFrame Cue="50%">
<TransformSetter Property="ScaleTransform.ScaleX" Value="0.8"/>
<TransformSetter Property="ScaleTransform.ScaleY" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleX" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.8"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -86,7 +86,7 @@
<Style.Animations>
<Animation Duration="0:0:3" Easing="BounceEaseInOut">
<KeyFrame Cue="48%">
<TransformSetter Property="TranslateTransform.Y" Value="-100"/>
<Setter Property="TranslateTransform.Y" Value="-100"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -95,10 +95,10 @@
<Style.Animations>
<Animation Duration="0:0:3" Easing="CircularEaseInOut">
<KeyFrame Cue="25%">
<TransformSetter Property="SkewTransform.AngleX" Value="-20"/>
<Setter Property="SkewTransform.AngleX" Value="-20"/>
</KeyFrame>
<KeyFrame Cue="75%">
<TransformSetter Property="SkewTransform.AngleX" Value="20"/>
<Setter Property="SkewTransform.AngleX" Value="20"/>
</KeyFrame>
</Animation>
</Style.Animations>

2
samples/RenderTest/Pages/ClippingPage.xaml

@ -10,7 +10,7 @@
<Style.Animations>
<Animation Duration="0:0:2" RepeatCount="Loop">
<KeyFrame Cue="100%">
<TransformSetter Property="RotateTransform.Angle" Value="360"/>
<Setter Property="RotateTransform.Angle" Value="360"/>
</KeyFrame>
</Animation>
</Style.Animations>

7
src/Android/Avalonia.Android/Resources/Resource.Designer.cs

@ -40,11 +40,14 @@ namespace Avalonia.Android
public partial class String
{
// aapt resource value: 0x7f020002
public static int ApplicationName = 2130837506;
// aapt resource value: 0x7f020001
public static int ApplicationName = 2130837505;
public static int Hello = 2130837505;
// aapt resource value: 0x7f020000
public static int Hello = 2130837504;
public static int library_name = 2130837504;
static String()
{

33
src/Avalonia.Animation/Animation.cs

@ -18,6 +18,28 @@ namespace Avalonia.Animation
/// </summary>
public class Animation : AvaloniaList<KeyFrame>, IDisposable, IAnimation
{
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
{
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) )
};
public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
where TAnimator: IAnimator
{
Animators.Insert(0, (condition, typeof(TAnimator)));
}
private static Type GetAnimatorType(AvaloniaProperty property)
{
foreach (var (condition, type) in Animators)
{
if (condition(property))
{
return type;
}
}
return null;
}
private bool _isChildrenChanged = false;
private List<IDisposable> _subscription = new List<IDisposable>();
@ -67,14 +89,13 @@ namespace Avalonia.Animation
{
foreach (var setter in keyframe)
{
var custAttr = setter.GetType()
.GetCustomAttributes()
.Where(p => p.GetType() == typeof(AnimatorAttribute));
var handler = GetAnimatorType(setter.Property);
if (!custAttr.Any())
throw new InvalidProgramException($"Type {setter.GetType()} doesn't have Animator attribute.");
if (handler == null)
{
throw new InvalidOperationException($"No animator registered for the property {setter.Property}. Add an animator to the Animation.Animators collection that matches this property to animate it.");
}
var handler = ((AnimatorAttribute)custAttr.First()).HandlerType;
if (!handlerList.Contains((handler, setter.Property)))
handlerList.Add((handler, setter.Property));

17
src/Avalonia.Animation/AnimationSetter.cs

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
namespace Avalonia.Animation
{
public abstract class AnimationSetter : IAnimationSetter
{
public AvaloniaProperty Property { get; set; }
public object Value { get; set; }
}
}

18
src/Avalonia.Animation/AnimatorAttribute.cs

@ -1,18 +0,0 @@
using System;
namespace Avalonia.Animation
{
/// <summary>
/// Attribute for <see cref="IAnimationSetter"/> objects
/// that maps the setter to it's <see cref="Animator{T}"/>.
/// </summary>
public class AnimatorAttribute : Attribute
{
public Type HandlerType;
public AnimatorAttribute(Type handler)
{
this.HandlerType = handler;
}
}
}

21
src/Avalonia.Animation/DoubleSetter.cs

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
namespace Avalonia.Animation
{
/// <summary>
/// Setter that handles <see cref="double"/> properties
/// in the target.
/// </summary>
[Animator(typeof(DoubleAnimator))]
public class DoubleSetter : AnimationSetter
{
}
}

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -5,6 +5,7 @@
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>
<Import Project="..\..\build\Base.props" />
<Import Project="..\..\build\Binding.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" />
</Project>

2
src/Avalonia.Base/AvaloniaObject.cs

@ -703,7 +703,7 @@ namespace Avalonia
/// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property)
{
if (property.Inherits && _inheritanceParent is AvaloniaObject aobj)
if (property.Inherits && InheritanceParent is AvaloniaObject aobj)
return aobj.GetValueInternal(property);
return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
}

5
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Avalonia.Data.Core;
namespace Avalonia.Collections
{
@ -116,8 +117,8 @@ namespace Avalonia.Collections
_inner = new Dictionary<TKey, TValue>();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(CommonPropertyNames.IndexerName));
if (CollectionChanged != null)

3
src/Avalonia.Base/Data/BindingNotification.cs

@ -171,8 +171,7 @@ namespace Avalonia.Data
/// </remarks>
public static object ExtractError(object o)
{
var notification = o as BindingNotification;
return notification != null ? notification.Error : o;
return o is BindingNotification notification ? notification.Error : o;
}
/// <summary>

2
src/Markup/Avalonia.Markup/AlwaysEnabledDelegateCommand.cs → src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs

@ -6,7 +6,7 @@ using System.Reflection;
using System.Text;
using System.Windows.Input;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
class AlwaysEnabledDelegateCommand : ICommand
{

2
src/Markup/Avalonia.Markup/BoolConverters.cs → src/Avalonia.Base/Data/Converters/BoolConverters.cs

@ -3,7 +3,7 @@
using System.Linq;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// Provides a set of useful <see cref="IValueConverter"/>s for working with string values.

2
src/Markup/Avalonia.Markup/DefaultValueConverter.cs → src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@ -7,7 +7,7 @@ using Avalonia.Data;
using Avalonia.Utilities;
using System.Windows.Input;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// Provides a default set of value conversions for bindings that do not specify a value

2
src/Markup/Avalonia.Markup/FuncMultiValueConverter.cs → src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{T1, TResult}"/>

2
src/Markup/Avalonia.Markup/FuncValueConverter.cs → src/Avalonia.Base/Data/Converters/FuncValueConverter.cs

@ -5,7 +5,7 @@ using System;
using System.Globalization;
using Avalonia.Utilities;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{T1, TResult}"/>

2
src/Markup/Avalonia.Markup/IMultiValueConverter.cs → src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// Converts multi-binding inputs to a final value.

2
src/Markup/Avalonia.Markup/IValueConverter.cs → src/Avalonia.Base/Data/Converters/IValueConverter.cs

@ -5,7 +5,7 @@ using System;
using System.Globalization;
using Avalonia.Data;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// Converts a binding value.

2
src/Markup/Avalonia.Markup/StringConverters.cs → src/Avalonia.Base/Data/Converters/StringConverters.cs

@ -5,7 +5,7 @@ using System;
using System.Globalization;
using Avalonia.Utilities;
namespace Avalonia.Markup
namespace Avalonia.Data.Converters
{
/// <summary>
/// Provides a set of useful <see cref="IValueConverter"/>s for working with string values.

4
src/Markup/Avalonia.Markup/Data/BindingExpression.cs → src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -5,11 +5,11 @@ using System;
using System.Globalization;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Logging;
using Avalonia.Utilities;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
/// <summary>
/// Binds to an expression on an object using a type value converter to convert the values

2
src/Markup/Avalonia.Markup/Data/CommonPropertyNames.cs → src/Avalonia.Base/Data/Core/CommonPropertyNames.cs

@ -1,7 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
public static class CommonPropertyNames
{

2
src/Markup/Avalonia.Markup/Data/EmptyExpressionNode.cs → src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs

@ -4,7 +4,7 @@
using System;
using System.Reactive.Linq;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal class EmptyExpressionNode : ExpressionNode
{

2
src/Markup/Avalonia.Markup/Data/ExpressionNode.cs → src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -7,7 +7,7 @@ using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Data;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal abstract class ExpressionNode : ISubject<object>
{

4
src/Markup/Avalonia.Markup/Data/ExpressionNodeBuilder.cs → src/Avalonia.Base/Data/Core/ExpressionNodeBuilder.cs

@ -2,9 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Markup.Data.Parsers;
using Avalonia.Data.Core.Parsers;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal static class ExpressionNodeBuilder
{

45
src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs → src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -8,9 +8,9 @@ using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Data;
using Avalonia.Markup.Data.Plugins;
using Avalonia.Data.Core.Plugins;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
/// <summary>
/// Observes and sets the value of an expression on an object.
@ -245,40 +245,35 @@ namespace Avalonia.Markup.Data
private object Translate(object o)
{
var weak = o as WeakReference;
if (weak != null)
if (o is WeakReference weak)
{
return weak.Target;
}
else
else if (BindingNotification.ExtractError(o) is MarkupBindingChainException broken)
{
var broken = BindingNotification.ExtractError(o) as MarkupBindingChainException;
if (broken != null)
{
broken.Commit(Description);
}
return o;
broken.Commit(Description);
}
return o;
}
private IDisposable StartRoot()
{
var observable = _root as IObservable<object>;
if (observable != null)
switch (_root)
{
return observable.Subscribe(
x => _node.Target = new WeakReference(x != AvaloniaProperty.UnsetValue ? x : null),
_ => _finished.OnNext(Unit.Default),
() => _finished.OnNext(Unit.Default));
}
else
{
_node.Target = (WeakReference)_root;
return Disposable.Empty;
case IObservable<object> observable:
return observable.Subscribe(
x => _node.Target = new WeakReference(x != AvaloniaProperty.UnsetValue ? x : null),
_ => _finished.OnNext(Unit.Default),
() => _finished.OnNext(Unit.Default));
case WeakReference weak:
_node.Target = weak;
break;
default:
throw new AvaloniaInternalException("The ExpressionObserver._root member should only be either an observable or WeakReference.");
}
return Disposable.Empty;
}
}
}

4
src/Markup/Avalonia.Markup/Data/ExpressionParseException.cs → src/Avalonia.Base/Data/Core/ExpressionParseException.cs

@ -2,9 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Markup.Data.Parsers;
using Avalonia.Data.Core.Parsers;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
/// <summary>
/// Exception thrown when <see cref="ExpressionObserver"/> could not parse the provided

2
src/Markup/Avalonia.Markup/Data/ISettableNode.cs → src/Avalonia.Base/Data/Core/ISettableNode.cs

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
interface ISettableNode
{

2
src/Markup/Avalonia.Markup/Data/ITransformNode.cs → src/Avalonia.Base/Data/Core/ITransformNode.cs

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
interface ITransformNode
{

2
src/Markup/Avalonia.Markup/Data/IndexerNode.cs → src/Avalonia.Base/Data/Core/IndexerNode.cs

@ -13,7 +13,7 @@ using System.Reflection;
using System.Reactive.Linq;
using Avalonia.Data;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal class IndexerNode : ExpressionNode, ISettableNode
{

2
src/Markup/Avalonia.Markup/Data/LogicalNotNode.cs → src/Avalonia.Base/Data/Core/LogicalNotNode.cs

@ -5,7 +5,7 @@ using System;
using System.Globalization;
using Avalonia.Data;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal class LogicalNotNode : ExpressionNode, ITransformNode
{

3
src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs → src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs

@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Data;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal class MarkupBindingChainException : BindingChainException
{

2
src/Markup/Avalonia.Markup/Data/Parsers/ArgumentListParser.cs → src/Avalonia.Base/Data/Core/Parsers/ArgumentListParser.cs

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Markup.Data.Parsers
namespace Avalonia.Data.Core.Parsers
{
internal static class ArgumentListParser
{

2
src/Markup/Avalonia.Markup/Data/Parsers/ExpressionParser.cs → src/Avalonia.Base/Data/Core/Parsers/ExpressionParser.cs

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Markup.Data.Parsers
namespace Avalonia.Data.Core.Parsers
{
internal class ExpressionParser
{

2
src/Markup/Avalonia.Markup/Data/Parsers/IdentifierParser.cs → src/Avalonia.Base/Data/Core/Parsers/IdentifierParser.cs

@ -4,7 +4,7 @@
using System.Globalization;
using System.Text;
namespace Avalonia.Markup.Data.Parsers
namespace Avalonia.Data.Core.Parsers
{
internal static class IdentifierParser
{

2
src/Markup/Avalonia.Markup/Data/Parsers/Reader.cs → src/Avalonia.Base/Data/Core/Parsers/Reader.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Markup.Data.Parsers
namespace Avalonia.Data.Core.Parsers
{
internal class Reader
{

2
src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -6,7 +6,7 @@ using System.Linq;
using System.Reactive.Linq;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Reads a property from a <see cref="AvaloniaObject"/>.

2
src/Markup/Avalonia.Markup/Data/Plugins/DataAnnotationsValidationPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@ -8,7 +8,7 @@ using System.Linq;
using System.Reflection;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Validates properties on that have <see cref="ValidationAttribute"/>s.

2
src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs → src/Avalonia.Base/Data/Core/Plugins/DataValidatiorBase.cs

@ -4,7 +4,7 @@
using System;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Base class for data validators.

2
src/Markup/Avalonia.Markup/Data/Plugins/ExceptionValidationPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs

@ -5,7 +5,7 @@ using Avalonia.Data;
using System;
using System.Reflection;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Validates properties that report errors by throwing exceptions.

2
src/Markup/Avalonia.Markup/Data/Plugins/IDataValidationPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs

@ -4,7 +4,7 @@
using System;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Defines how data validation is observed by an <see cref="ExpressionObserver"/>.

2
src/Markup/Avalonia.Markup/Data/Plugins/IPropertyAccessor.cs → src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs

@ -4,7 +4,7 @@
using System;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Defines an accessor to a property on an object returned by a

2
src/Markup/Avalonia.Markup/Data/Plugins/IPropertyAccessorPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Defines how a member is read, written and observed by an

2
src/Markup/Avalonia.Markup/Data/Plugins/IStreamPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Defines a plugin that handles the '^' stream binding operator.

2
src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs

@ -8,7 +8,7 @@ using System.Linq;
using Avalonia.Data;
using Avalonia.Utilities;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Validates properties on objects that implement <see cref="INotifyDataErrorInfo"/>.

2
src/Markup/Avalonia.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -10,7 +10,7 @@ using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Utilities;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Reads a property from a standard C# object that optionally supports the

2
src/Markup/Avalonia.Markup/Data/Plugins/MethodAccessorPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -5,7 +5,7 @@ using Avalonia.Data;
using System.Reflection;
using System.Linq;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
class MethodAccessorPlugin : IPropertyAccessorPlugin
{

2
src/Markup/Avalonia.Markup/Data/Plugins/ObservableStreamPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Handles binding to <see cref="IObservable{T}"/>s for the '^' stream binding operator.

2
src/Markup/Avalonia.Markup/Data/Plugins/PropertyAccessorBase.cs → src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs

@ -4,7 +4,7 @@
using System;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Defines a default base implementation for a <see cref="IPropertyAccessor"/>.

2
src/Markup/Avalonia.Markup/Data/Plugins/PropertyError.cs → src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs

@ -2,7 +2,7 @@ using System;
using System.Reactive.Disposables;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// An <see cref="IPropertyAccessor"/> that represents an error.

2
src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs → src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs

@ -9,7 +9,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Data;
namespace Avalonia.Markup.Data.Plugins
namespace Avalonia.Data.Core.Plugins
{
/// <summary>
/// Handles binding to <see cref="Task"/>s for the '^' stream binding operator.

4
src/Markup/Avalonia.Markup/Data/PropertyAccessorNode.cs → src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -6,9 +6,9 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Data;
using Avalonia.Markup.Data.Plugins;
using Avalonia.Data.Core.Plugins;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal class PropertyAccessorNode : ExpressionNode, ISettableNode
{

2
src/Markup/Avalonia.Markup/Data/StreamNode.cs → src/Avalonia.Base/Data/Core/StreamNode.cs

@ -6,7 +6,7 @@ using System.Globalization;
using Avalonia.Data;
using System.Reactive.Linq;
namespace Avalonia.Markup.Data
namespace Avalonia.Data.Core
{
internal class StreamNode : ExpressionNode
{

3
src/Avalonia.Base/Properties/AssemblyInfo.cs

@ -1,9 +1,10 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data.Converters")]
[assembly: InternalsVisibleTo("Avalonia.Base.UnitTests")]
[assembly: InternalsVisibleTo("Avalonia.UnitTests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

764
src/Avalonia.Controls/Control.cs

@ -29,53 +29,22 @@ namespace Avalonia.Controls
/// <remarks>
/// The control class extends <see cref="InputElement"/> and adds the following features:
///
/// - An inherited <see cref="DataContext"/>.
/// - A <see cref="Tag"/> property to allow user-defined data to be attached to the control.
/// - A collection of class strings for custom styling.
/// - 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, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize, IVisualBrushInitialize
public class Control : InputElement, IControl, INamed, ISupportInitialize, IVisualBrushInitialize, IRequiresTemplateInSetter
{
/// <summary>
/// Defines the <see cref="DataContext"/> property.
/// </summary>
public static readonly StyledProperty<object> DataContextProperty =
AvaloniaProperty.Register<Control, object>(
nameof(DataContext),
inherits: true,
notifying: DataContextNotifying);
/// <summary>
/// Defines the <see cref="FocusAdorner"/> property.
/// </summary>
public static readonly StyledProperty<ITemplate<IControl>> FocusAdornerProperty =
AvaloniaProperty.Register<Control, ITemplate<IControl>>(nameof(FocusAdorner));
/// <summary>
/// Defines the <see cref="Name"/> property.
/// </summary>
public static readonly DirectProperty<Control, string> NameProperty =
AvaloniaProperty.RegisterDirect<Control, string>(nameof(Name), o => o.Name, (o, v) => o.Name = v);
/// <summary>
/// Defines the <see cref="Parent"/> property.
/// </summary>
public static readonly DirectProperty<Control, IControl> ParentProperty =
AvaloniaProperty.RegisterDirect<Control, IControl>(nameof(Parent), o => o.Parent);
/// <summary>
/// Defines the <see cref="Tag"/> property.
/// </summary>
public static readonly StyledProperty<object> TagProperty =
AvaloniaProperty.Register<Control, object>(nameof(Tag));
/// <summary>
/// Defines the <see cref="TemplatedParent"/> property.
/// </summary>
public static readonly StyledProperty<ITemplatedControl> TemplatedParentProperty =
AvaloniaProperty.Register<Control, ITemplatedControl>(nameof(TemplatedParent), inherits: true);
/// <summary>
/// Defines the <see cref="ContextMenu"/> property.
/// </summary>
@ -88,152 +57,8 @@ namespace Avalonia.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;
private IControl _focusAdorner;
private bool _isAttachedToLogicalTree;
private IAvaloniaList<ILogical> _logicalChildren;
private INameScope _nameScope;
private IResourceDictionary _resources;
private Styles _styles;
private bool _styled;
private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
private bool _dataContextUpdating;
/// <summary>
/// Initializes static members of the <see cref="Control"/> class.
/// </summary>
static Control()
{
AffectsMeasure(IsVisibleProperty);
PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled");
PseudoClass(IsFocusedProperty, ":focus");
PseudoClass(IsPointerOverProperty, ":pointerover");
DataContextProperty.Changed.AddClassHandler<Control>(x => x.OnDataContextChangedCore);
}
/// <summary>
/// Initializes a new instance of the <see cref="Control"/> class.
/// </summary>
public Control()
{
_nameScope = this as INameScope;
_isAttachedToLogicalTree = this is IStyleRoot;
}
/// <summary>
/// Raised when the control is attached to a rooted logical tree.
/// </summary>
public event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
/// <summary>
/// Raised when the control is detached from a rooted logical tree.
/// </summary>
public event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
/// <summary>
/// Occurs when the <see cref="DataContext"/> property changes.
/// </summary>
/// <remarks>
/// This event will be raised when the <see cref="DataContext"/> property has changed and
/// all subscribers to that change have been notified.
/// </remarks>
public event EventHandler DataContextChanged;
/// <summary>
/// Occurs when the control has finished initialization.
/// </summary>
/// <remarks>
/// The Initialized event indicates that all property values on the control have been set.
/// When loading the control from markup, it occurs when
/// <see cref="ISupportInitialize.EndInit"/> is called *and* the control
/// is attached to a rooted logical tree. When the control is created by code and
/// <see cref="ISupportInitialize"/> is not used, it is called when the control is attached
/// to the visual tree.
/// </remarks>
public event EventHandler Initialized;
/// <summary>
/// Occurs when a resource in this control or a parent control has changed.
/// </summary>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <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 (String.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException("Cannot set Name to null or empty string.");
}
if (_styled)
{
throw new InvalidOperationException("Cannot set Name : control already styled.");
}
_name = value;
}
}
/// <summary>
/// Gets or sets the control's classes.
/// </summary>
/// <remarks>
/// <para>
/// Classes can be used to apply user-defined styling to controls, or to allow controls
/// that share a common purpose to be easily selected.
/// </para>
/// <para>
/// Even though this property can be set, the setter is only intended for use in object
/// initializers. Assigning to this property does not change the underlying collection,
/// it simply clears the existing collection and addds the contents of the assigned
/// collection.
/// </para>
/// </remarks>
public Classes Classes
{
get
{
return _classes;
}
set
{
if (_classes != value)
{
_classes.Replace(value);
}
}
}
/// <summary>
/// Gets or sets the control's data context.
/// </summary>
/// <remarks>
/// The data context is an inherited property that specifies the default object that will
/// be used for data binding.
/// </remarks>
public object DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
/// <summary>
/// Gets or sets the control's focus adorner.
@ -253,55 +78,6 @@ namespace Avalonia.Controls
/// </remarks>
public DataTemplates DataTemplates => _dataTemplates ?? (_dataTemplates = new DataTemplates());
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>
/// <remarks>
/// For more information about when IsInitialized is set, see the <see cref="Initialized"/>
/// event.
/// </remarks>
public bool IsInitialized { get; private set; }
/// <summary>
/// Gets the styles for the control.
/// </summary>
/// <remarks>
/// Styles for the entire application are added to the Application.Styles collection, but
/// each control may in addition define its own styles which are applied to the control
/// itself and its children.
/// </remarks>
public Styles Styles
{
get { return _styles ?? (Styles = new Styles()); }
set
{
Contract.Requires<ArgumentNullException>(value != null);
if (_styles != value)
{
if (_styles != null)
{
(_styles as ISetStyleParent)?.SetParent(null);
_styles.ResourcesChanged -= ThisResourcesChanged;
}
_styles = value;
if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
{
setParent.SetParent(this);
}
_styles.ResourcesChanged += ThisResourcesChanged;
}
}
}
/// <summary>
/// Gets the control's logical parent.
/// </summary>
public IControl Parent => _parent;
/// <summary>
/// Gets or sets a context menu to the control.
/// </summary>
@ -311,34 +87,6 @@ namespace Avalonia.Controls
set { SetValue(ContextMenuProperty, value); }
}
/// <summary>
/// Gets or sets the control's resource dictionary.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
Contract.Requires<ArgumentNullException>(value != null);
var hadResources = false;
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ThisResourcesChanged;
}
_resources = value;
_resources.ResourcesChanged += ThisResourcesChanged;
if (hadResources || _resources.Count > 0)
{
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
/// <summary>
/// Gets or sets a user-defined object attached to the control.
/// </summary>
@ -348,226 +96,11 @@ namespace Avalonia.Controls
set { SetValue(TagProperty, value); }
}
/// <summary>
/// Gets the control whose lookless template this control is part of.
/// </summary>
public ITemplatedControl TemplatedParent
{
get { return GetValue(TemplatedParentProperty); }
internal set { SetValue(TemplatedParentProperty, value); }
}
/// <summary>
/// Gets the control's logical children.
/// </summary>
protected IAvaloniaList<ILogical> LogicalChildren
{
get
{
if (_logicalChildren == null)
{
var list = new AvaloniaList<ILogical>();
list.ResetBehavior = ResetBehavior.Remove;
list.Validate = ValidateLogicalChild;
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;
}
return _logicalChildren;
}
}
public new IControl Parent => (IControl)base.Parent;
/// <inheritdoc/>
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
/// <summary>
/// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
/// pseudoclasses.
/// </summary>
protected IPseudoClasses PseudoClasses => Classes;
/// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree.
/// </summary>
bool ILogical.IsAttachedToLogicalTree => _isAttachedToLogicalTree;
/// <summary>
/// Gets the control's logical parent.
/// </summary>
ILogical ILogical.LogicalParent => Parent;
/// <summary>
/// Gets the control's logical children.
/// </summary>
IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
/// <inheritdoc/>
IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
/// <summary>
/// Gets the type by which the control is styled.
/// </summary>
/// <remarks>
/// Usually controls are styled by their own type, but there are instances where you want
/// a control to be styled by its base type, e.g. creating SpecialButton that
/// derives from Button and adds extra functionality but is still styled as a regular
/// Button.
/// </remarks>
Type IStyleable.StyleKey => GetType();
/// <inheritdoc/>
IObservable<IStyleable> IStyleable.StyleDetach => _styleDetach;
/// <inheritdoc/>
bool IStyleHost.IsStylesInitialized => _styles != null;
/// <inheritdoc/>
IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent;
/// <inheritdoc/>
public virtual void BeginInit()
{
++_initCount;
}
/// <inheritdoc/>
public virtual void EndInit()
{
if (_initCount == 0)
{
throw new InvalidOperationException("BeginInit was not called.");
}
if (--_initCount == 0 && _isAttachedToLogicalTree)
{
InitializeStylesIfNeeded();
InitializeIfNeeded();
}
}
private void InitializeStylesIfNeeded(bool force = false)
{
if (_initCount == 0 && (!_styled || force))
{
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
}
private void InitializeIfNeeded()
{
if (_initCount == 0 && !IsInitialized)
{
IsInitialized = true;
Initialized?.Invoke(this, EventArgs.Empty);
}
}
/// <inheritdoc/>
void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
this.OnAttachedToLogicalTreeCore(e);
}
/// <inheritdoc/>
void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
this.OnDetachedFromLogicalTreeCore(e);
}
/// <inheritdoc/>
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
/// <inheritdoc/>
bool IResourceProvider.TryGetResource(string key, out object value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||
(_styles?.TryGetResource(key, out value) ?? false);
}
/// <summary>
/// Sets the control's logical parent.
/// </summary>
/// <param name="parent">The parent.</param>
void ISetLogicalParent.SetParent(ILogical parent)
{
var old = Parent;
if (parent != old)
{
if (old != null && parent != null)
{
throw new InvalidOperationException("The Control already has a parent.");
}
if (_isAttachedToLogicalTree)
{
var oldRoot = FindStyleRoot(old) ?? this as IStyleRoot;
if (oldRoot == null)
{
throw new AvaloniaInternalException("Was attached to logical tree but cannot find root.");
}
var e = new LogicalTreeAttachmentEventArgs(oldRoot);
OnDetachedFromLogicalTreeCore(e);
}
if (InheritanceParent == null || parent == null)
{
InheritanceParent = parent as AvaloniaObject;
}
_parent = (IControl)parent;
if (old != null)
{
old.ResourcesChanged -= ThisResourcesChanged;
}
if (_parent != null)
{
_parent.ResourcesChanged += ThisResourcesChanged;
}
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
{
var newRoot = FindStyleRoot(this);
if (newRoot == null)
{
throw new AvaloniaInternalException("Parent is atttached to logical tree but cannot find root.");
}
var e = new LogicalTreeAttachmentEventArgs(newRoot);
OnAttachedToLogicalTreeCore(e);
}
RaisePropertyChanged(ParentProperty, old, _parent, BindingPriority.LocalValue);
}
}
/// <summary>
/// Sets the control's inheritance parent.
/// </summary>
/// <param name="parent">The parent.</param>
void ISetInheritanceParent.SetParent(IAvaloniaObject parent)
{
InheritanceParent = parent;
}
/// <inheritdoc/>
void IVisualBrushInitialize.EnsureInitialized()
{
@ -600,52 +133,6 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Adds a pseudo-class to be set when a property is true.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass(AvaloniaProperty<bool> property, string className)
{
PseudoClass(property, x => x, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property equals a certain value.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="selector">Returns a boolean value based on the property value.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass<T>(
AvaloniaProperty<T> property,
Func<T, bool> selector,
string className)
{
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(selector != null);
Contract.Requires<ArgumentNullException>(className != null);
if (string.IsNullOrWhiteSpace(className))
{
throw new ArgumentException("Cannot supply an empty className.");
}
property.Changed.Merge(property.Initialized)
.Where(e => e.Sender is Control)
.Subscribe(e =>
{
if (selector((T)e.NewValue))
{
((Control)e.Sender).PseudoClasses.Add(className);
}
else
{
((Control)e.Sender).PseudoClasses.Remove(className);
}
});
}
/// <summary>
/// Gets the element that recieves the focus adorner.
/// </summary>
@ -655,22 +142,6 @@ namespace Avalonia.Controls
return this;
}
/// <summary>
/// Called when the control is added to a rooted logical tree.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
}
/// <summary>
/// Called when the control is removed from a rooted logical tree.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
}
/// <inheritdoc/>
protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
@ -685,29 +156,6 @@ namespace Avalonia.Controls
base.OnDetachedFromVisualTreeCore(e);
}
/// <summary>
/// Called when the <see cref="DataContext"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnDataContextChanged(EventArgs e)
{
DataContextChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Called when the <see cref="DataContext"/> begins updating.
/// </summary>
protected virtual void OnDataContextBeginUpdate()
{
}
/// <summary>
/// Called when the <see cref="DataContext"/> finishes updating.
/// </summary>
protected virtual void OnDataContextEndUpdate()
{
}
/// <inheritdoc/>
protected override void OnGotFocus(GotFocusEventArgs e)
{
@ -757,211 +205,5 @@ namespace Avalonia.Controls
_focusAdorner = null;
}
}
private static void DataContextNotifying(IAvaloniaObject o, bool notifying)
{
if (o is Control control)
{
DataContextNotifying(control, notifying);
}
}
private static void DataContextNotifying(Control control, bool notifying)
{
if (notifying)
{
if (!control._dataContextUpdating)
{
control._dataContextUpdating = true;
control.OnDataContextBeginUpdate();
foreach (var child in control.LogicalChildren)
{
if (child is Control c &&
c.InheritanceParent == control &&
!c.IsSet(DataContextProperty))
{
DataContextNotifying(c, notifying);
}
}
}
}
else
{
if (control._dataContextUpdating)
{
control.OnDataContextEndUpdate();
control._dataContextUpdating = false;
}
}
}
private static IStyleRoot FindStyleRoot(IStyleHost e)
{
while (e != null)
{
if (e is IRenderRoot root)
{
return root as IStyleRoot;
}
e = e.StylingParent;
}
return null;
}
private void ApplyStyling()
{
AvaloniaLocator.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);
var visualParent = Parent as Visual;
if (this is INameScope && visualParent != null)
{
// If we have e.g. a named UserControl in a window then we want that control
// to be findable by name from the Window, so register with both name scopes.
// This differs from WPF's behavior in that XAML manually registers controls
// with name scopes based on the XAML file in which the name attribute appears,
// but we're trying to avoid XAML magic in Avalonia in order to made code-
// created UIs easy. This will cause problems if a UserControl declares a name
// in its XAML and that control is included multiple times in a parent control
// (as the name will be duplicated), however at the moment I'm fine with saying
// "don't do that".
var parentNameScope = NameScope.FindNameScope(visualParent);
parentNameScope?.Register(Name, this);
}
}
}
private static void ValidateLogicalChild(ILogical c)
{
if (c == null)
{
throw new ArgumentException("Cannot add null to LogicalChildren.");
}
}
private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
{
// This method can be called when a control is already attached to the logical tree
// in the following scenario:
// - ListBox gets assigned Items containing ListBoxItem
// - ListBox makes ListBoxItem a logical child
// - ListBox template gets applied; making its Panel get attached to logical tree
// - That AttachedToLogicalTree signal travels down to the ListBoxItem
if (!_isAttachedToLogicalTree)
{
_isAttachedToLogicalTree = true;
InitializeStylesIfNeeded(true);
OnAttachedToLogicalTree(e);
AttachedToLogicalTree?.Invoke(this, e);
}
foreach (var child in LogicalChildren.OfType<Control>())
{
child.OnAttachedToLogicalTreeCore(e);
}
}
private void OnDetachedFromLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
{
if (_isAttachedToLogicalTree)
{
if (Name != null)
{
_nameScope?.Unregister(Name);
}
_isAttachedToLogicalTree = false;
_styleDetach.OnNext(this);
OnDetachedFromLogicalTree(e);
DetachedFromLogicalTree?.Invoke(this, e);
foreach (var child in LogicalChildren.OfType<Control>())
{
child.OnDetachedFromLogicalTreeCore(e);
}
#if DEBUG
if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0)
{
Logger.Warning(
LogArea.Control,
this,
"{Type} detached from logical tree but still has class listeners",
this.GetType());
}
#endif
}
}
private void OnDataContextChangedCore(AvaloniaPropertyChangedEventArgs e)
{
OnDataContextChanged(EventArgs.Empty);
}
private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Remove:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Replace:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Reset:
throw new NotSupportedException("Reset should not be signalled on LogicalChildren collection");
}
}
private void SetLogicalParent(IEnumerable<ILogical> children)
{
foreach (var i in children)
{
if (i.LogicalParent == null)
{
((ISetLogicalParent)i).SetParent(this);
}
}
}
private void ClearLogicalParent(IEnumerable<ILogical> children)
{
foreach (var i in children)
{
if (i.LogicalParent == this)
{
((ISetLogicalParent)i).SetParent(null);
}
}
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
((ILogical)this).NotifyResourcesChanged(e);
}
}
}

15
src/Avalonia.Controls/ControlExtensions.cs

@ -66,21 +66,6 @@ namespace Avalonia.Controls
return nameScope.Find<T>(name);
}
/// <summary>
/// Finds the name scope for a control by searching up the logical tree.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The control's name scope, or null if not found.</returns>
public static INameScope FindNameScope(this IControl control)
{
Contract.Requires<ArgumentNullException>(control != null);
return control.GetSelfAndLogicalAncestors()
.OfType<Control>()
.Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
.FirstOrDefault(x => x != null);
}
/// <summary>
/// Adds or removes a pseudoclass depending on a boolean value.
/// </summary>

80
src/Avalonia.Controls/DropDown.cs

@ -6,7 +6,6 @@ using Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.VisualTree;
@ -51,6 +50,7 @@ namespace Avalonia.Controls
{
FocusableProperty.OverrideDefaultValue<DropDown>(true);
SelectedItemProperty.Changed.AddClassHandler<DropDown>(x => x.SelectedItemChanged);
KeyDownEvent.AddClassHandler<DropDown>(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel);
}
/// <summary>
@ -96,54 +96,46 @@ namespace Avalonia.Controls
this.UpdateSelectionBoxItem(this.SelectedItem);
}
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
if (!e.Handled && e.NavigationMethod == NavigationMethod.Directional)
{
e.Handled = UpdateSelectionFromEventSource(e.Source);
}
}
/// <inheritdoc/>
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled)
if (e.Handled)
return;
if (e.Key == Key.F4 ||
((e.Key == Key.Down || e.Key == Key.Up) && ((e.Modifiers & InputModifiers.Alt) != 0)))
{
if (e.Key == Key.F4 ||
((e.Key == Key.Down || e.Key == Key.Up) && ((e.Modifiers & InputModifiers.Alt) != 0)))
IsDropDownOpen = !IsDropDownOpen;
e.Handled = true;
}
else if (IsDropDownOpen && e.Key == Key.Escape)
{
IsDropDownOpen = false;
e.Handled = true;
}
else if (IsDropDownOpen && e.Key == Key.Enter)
{
SelectFocusedItem();
IsDropDownOpen = false;
e.Handled = true;
}
else if (!IsDropDownOpen)
{
if (e.Key == Key.Down)
{
IsDropDownOpen = !IsDropDownOpen;
if (++SelectedIndex >= ItemCount)
SelectedIndex = 0;
e.Handled = true;
}
else if (IsDropDownOpen && (e.Key == Key.Escape || e.Key == Key.Enter))
else if (e.Key == Key.Up)
{
IsDropDownOpen = false;
e.Handled = true;
}
if (--SelectedIndex < 0)
SelectedIndex = ItemCount - 1;
if (!IsDropDownOpen)
{
if (e.Key == Key.Down)
{
if (SelectedIndex == -1)
SelectedIndex = 0;
if (++SelectedIndex >= ItemCount)
SelectedIndex = 0;
e.Handled = true;
}
else if (e.Key == Key.Up)
{
if (--SelectedIndex < 0)
SelectedIndex = ItemCount - 1;
e.Handled = true;
}
e.Handled = true;
}
}
}
@ -230,5 +222,17 @@ namespace Avalonia.Controls
SelectionBoxItem = item;
}
}
private void SelectFocusedItem()
{
foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator.Containers)
{
if (dropdownItem.ContainerControl.IsFocused)
{
SelectedIndex = dropdownItem.Index;
break;
}
}
}
}
}

35
src/Avalonia.Controls/DropDownItem.cs

@ -1,12 +1,45 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Controls
{
/// <summary>
/// A selectable item in a <see cref="DropDown"/>.
/// </summary>
public class DropDownItem : ListBoxItem
public class DropDownItem : ContentControl, ISelectable
{
/// <summary>
/// Defines the <see cref="IsSelected"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsSelectedProperty =
AvaloniaProperty.Register<DropDownItem, bool>(nameof(IsSelected));
/// <summary>
/// Initializes static members of the <see cref="DropDownItem"/> class.
/// </summary>
static DropDownItem()
{
FocusableProperty.OverrideDefaultValue<DropDownItem>(true);
IsFocusedProperty.Changed.Subscribe(x =>
{
var sender = x.Sender as IControl;
if (sender != null)
{
((IPseudoClasses)sender.Classes).Set(":selected", (bool)x.NewValue);
}
});
}
/// <summary>
/// Gets or sets the selection state of the item.
/// </summary>
public bool IsSelected
{
get { return GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
}
}

6
src/Avalonia.Controls/HotkeyManager.cs

@ -55,13 +55,13 @@ namespace Avalonia.Controls
public void Init()
{
_hotkeySub = _control.GetObservable(HotKeyProperty).Subscribe(OnHotkeyChanged);
_parentSub = AncestorFinder.Create(_control, typeof (TopLevel)).Subscribe(OnParentChanged);
_parentSub = AncestorFinder.Create<TopLevel>(_control).Subscribe(OnParentChanged);
}
private void OnParentChanged(IControl control)
private void OnParentChanged(TopLevel control)
{
Unregister();
_root = (TopLevel) control;
_root = control;
Register();
}

30
src/Avalonia.Controls/IControl.cs

@ -16,37 +16,11 @@ namespace Avalonia.Controls
/// </summary>
public interface IControl : IVisual,
IDataTemplateHost,
ILogical,
ILayoutable,
IInputElement,
INamed,
IResourceNode,
IStyleable,
IStyleHost
IStyledElement
{
/// <summary>
/// Occurs when the control has finished initialization.
/// </summary>
event EventHandler Initialized;
/// <summary>
/// Gets or sets the control's styling classes.
/// </summary>
new Classes Classes { get; set; }
/// <summary>
/// Gets or sets the control's data context.
/// </summary>
object DataContext { get; set; }
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Gets the control's logical parent.
/// </summary>
IControl Parent { get; }
new IControl Parent { get; }
}
}

24
src/Avalonia.Controls/Utils/AncestorFinder.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reflection;
using System.Text;
@ -14,15 +15,15 @@ namespace Avalonia.Controls.Utils
{
class FinderNode : IDisposable
{
private readonly IControl _control;
private readonly IStyledElement _control;
private readonly TypeInfo _ancestorType;
public IObservable<IControl> Observable => _subject;
private readonly Subject<IControl> _subject = new Subject<IControl>();
public IObservable<IStyledElement> Observable => _subject;
private readonly Subject<IStyledElement> _subject = new Subject<IStyledElement>();
private FinderNode _child;
private IDisposable _disposable;
public FinderNode(IControl control, TypeInfo ancestorType)
public FinderNode(IStyledElement control, TypeInfo ancestorType)
{
_control = control;
_ancestorType = ancestorType;
@ -33,7 +34,7 @@ namespace Avalonia.Controls.Utils
_disposable = _control.GetObservable(Control.ParentProperty).Subscribe(OnValueChanged);
}
private void OnValueChanged(IControl next)
private void OnValueChanged(IStyledElement next)
{
if (next == null || _ancestorType.IsAssignableFrom(next.GetType().GetTypeInfo()))
_subject.OnNext(next);
@ -46,7 +47,7 @@ namespace Avalonia.Controls.Utils
}
}
private void OnChildValueChanged(IControl control) => _subject.OnNext(control);
private void OnChildValueChanged(IStyledElement control) => _subject.OnNext(control);
public void Dispose()
@ -55,10 +56,15 @@ namespace Avalonia.Controls.Utils
}
}
public static IObservable<T> Create<T>(IStyledElement control)
where T : IStyledElement
{
return Create(control, typeof(T)).Cast<T>();
}
public static IObservable<IControl> Create(IControl control, Type ancestorType)
public static IObservable<IStyledElement> Create(IStyledElement control, Type ancestorType)
{
return new AnonymousObservable<IControl>(observer =>
return new AnonymousObservable<IStyledElement>(observer =>
{
var finder = new FinderNode(control, ancestorType.GetTypeInfo());
var subscription = finder.Observable.Subscribe(observer);
@ -70,8 +76,6 @@ namespace Avalonia.Controls.Utils
finder.Dispose();
});
});
}
}
}

4
src/Avalonia.Input/InputElement.cs

@ -168,6 +168,10 @@ namespace Avalonia.Input
PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed);
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased);
PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged);
PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled");
PseudoClass(IsFocusedProperty, ":focus");
PseudoClass(IsPointerOverProperty, ":pointerover");
}
/// <summary>

3
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -5,9 +5,8 @@
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
</Project>

16
src/Avalonia.Controls/Classes.cs → src/Avalonia.Styling/Controls/Classes.cs

@ -9,7 +9,7 @@ using Avalonia.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// Holds a collection of style classes for an <see cref="IControl"/>.
/// Holds a collection of style classes for an <see cref="IStyledElement"/>.
/// </summary>
/// <remarks>
/// Similar to CSS, each control may have any number of styling classes applied.
@ -54,7 +54,7 @@ namespace Avalonia.Controls
/// <param name="name">The class name.</param>
/// <remarks>
/// Only standard classes may be added via this method. To add pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override void Add(string name)
@ -73,7 +73,7 @@ namespace Avalonia.Controls
/// <param name="names">The class names.</param>
/// <remarks>
/// Only standard classes may be added via this method. To add pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override void AddRange(IEnumerable<string> names)
@ -114,7 +114,7 @@ namespace Avalonia.Controls
/// <param name="name">The class name.</param>
/// <remarks>
/// Only standard classes may be added via this method. To add pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override void Insert(int index, string name)
@ -134,7 +134,7 @@ namespace Avalonia.Controls
/// <param name="names">The class names.</param>
/// <remarks>
/// Only standard classes may be added via this method. To add pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override void InsertRange(int index, IEnumerable<string> names)
@ -160,7 +160,7 @@ namespace Avalonia.Controls
/// <param name="name">The class name.</param>
/// <remarks>
/// Only standard classes may be removed via this method. To remove pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override bool Remove(string name)
@ -175,7 +175,7 @@ namespace Avalonia.Controls
/// <param name="names">The class name.</param>
/// <remarks>
/// Only standard classes may be removed via this method. To remove pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override void RemoveAll(IEnumerable<string> names)
@ -201,7 +201,7 @@ namespace Avalonia.Controls
/// <param name="index">The index of the class in the collection.</param>
/// <remarks>
/// Only standard classes may be removed via this method. To remove pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public override void RemoveAt(int index)

0
src/Avalonia.Controls/IPseudoClasses.cs → src/Avalonia.Styling/Controls/IPseudoClasses.cs

2
src/Avalonia.Controls/ISetInheritanceParent.cs → src/Avalonia.Styling/Controls/ISetInheritanceParent.cs

@ -4,7 +4,7 @@
namespace Avalonia.Controls
{
/// <summary>
/// Defines an interface through which a <see cref="Control"/>'s inheritance parent can be set.
/// Defines an interface through which a <see cref="StyledElement"/>'s inheritance parent can be set.
/// </summary>
/// <remarks>
/// You should not usually need to use this interface - it is for advanced scenarios only.

2
src/Avalonia.Controls/ISetLogicalParent.cs → src/Avalonia.Styling/Controls/ISetLogicalParent.cs

@ -6,7 +6,7 @@ using Avalonia.LogicalTree;
namespace Avalonia.Controls
{
/// <summary>
/// Defines an interface through which a <see cref="Control"/>'s logical parent can be set.
/// Defines an interface through which a <see cref="StyledElement"/>'s logical parent can be set.
/// </summary>
/// <remarks>
/// You should not usually need to use this interface - it is for advanced scenarios only.

36
src/Avalonia.Styling/Controls/NameScope.cs

@ -16,7 +16,7 @@ namespace Avalonia.Controls
/// Defines the NameScope attached property.
/// </summary>
public static readonly AttachedProperty<INameScope> NameScopeProperty =
AvaloniaProperty.RegisterAttached<NameScope, Visual, INameScope>("NameScope");
AvaloniaProperty.RegisterAttached<NameScope, StyledElement, INameScope>("NameScope");
private readonly Dictionary<string, object> _inner = new Dictionary<string, object>();
@ -31,53 +31,53 @@ namespace Avalonia.Controls
public event EventHandler<NameScopeEventArgs> Unregistered;
/// <summary>
/// Finds the containing name scope for a visual.
/// Finds the containing name scope for a styled element.
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="styled">The styled element.</param>
/// <returns>The containing name scope.</returns>
public static INameScope FindNameScope(Visual visual)
public static INameScope FindNameScope(StyledElement styled)
{
Contract.Requires<ArgumentNullException>(visual != null);
Contract.Requires<ArgumentNullException>(styled != null);
INameScope result;
while (visual != null)
while (styled != null)
{
result = visual as INameScope ?? GetNameScope(visual);
result = styled as INameScope ?? GetNameScope(styled);
if (result != null)
{
return result;
}
visual = (visual as ILogical)?.LogicalParent as Visual;
styled = (styled as ILogical)?.LogicalParent as StyledElement;
}
return null;
}
/// <summary>
/// Gets the value of the attached <see cref="NameScopeProperty"/> on a visual.
/// Gets the value of the attached <see cref="NameScopeProperty"/> on a styled element.
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="styled">The styled element.</param>
/// <returns>The value of the NameScope attached property.</returns>
public static INameScope GetNameScope(Visual visual)
public static INameScope GetNameScope(StyledElement styled)
{
Contract.Requires<ArgumentNullException>(visual != null);
Contract.Requires<ArgumentNullException>(styled != null);
return visual.GetValue(NameScopeProperty);
return styled.GetValue(NameScopeProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="NameScopeProperty"/> on a visual.
/// Sets the value of the attached <see cref="NameScopeProperty"/> on a styled element.
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="styled">The styled element.</param>
/// <param name="value">The value to set.</param>
public static void SetNameScope(Visual visual, INameScope value)
public static void SetNameScope(StyledElement styled, INameScope value)
{
Contract.Requires<ArgumentNullException>(visual != null);
Contract.Requires<ArgumentNullException>(styled != null);
visual.SetValue(NameScopeProperty, value);
styled.SetValue(NameScopeProperty, value);
}
/// <summary>

12
src/Avalonia.Styling/Controls/NameScopeExtensions.cs

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.LogicalTree;
namespace Avalonia.Controls
{
@ -64,5 +66,15 @@ namespace Avalonia.Controls
return (T)result;
}
public static INameScope FindNameScope(this ILogical control)
{
Contract.Requires<ArgumentNullException>(control != null);
return control.GetSelfAndLogicalAncestors()
.OfType<StyledElement>()
.Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
.FirstOrDefault(x => x != null);
}
}
}

0
src/Avalonia.Visuals/INamed.cs → src/Avalonia.Styling/INamed.cs

42
src/Avalonia.Styling/IStyledElement.cs

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Styling;
namespace Avalonia
{
public interface IStyledElement :
IStyleable,
IStyleHost,
ILogical,
IResourceProvider,
IResourceNode
{
/// <summary>
/// Occurs when the control has finished initialization.
/// </summary>
event EventHandler Initialized;
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Gets or sets the control's styling classes.
/// </summary>
new Classes Classes { get; set; }
/// <summary>
/// Gets or sets the control's data context.
/// </summary>
object DataContext { get; set; }
/// <summary>
/// Gets the control's logical parent.
/// </summary>
IStyledElement Parent { get; }
}
}

97
src/Avalonia.Styling/LogicalTree/ControlLocator.cs

@ -0,0 +1,97 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using Avalonia.Controls;
namespace Avalonia.LogicalTree
{
/// <summary>
/// Locates controls relative to other controls.
/// </summary>
public static class ControlLocator
{
/// <summary>
/// Tracks a named control relative to another control.
/// </summary>
/// <param name="relativeTo">
/// The control relative from which the other control should be found.
/// </param>
/// <param name="name">The name of the control to find.</param>
public static IObservable<ILogical> Track(ILogical relativeTo, string name)
{
var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
x => relativeTo.AttachedToLogicalTree += x,
x => relativeTo.AttachedToLogicalTree -= x)
.Select(x => ((ILogical)x.Sender).FindNameScope())
.StartWith(relativeTo.FindNameScope());
var detached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
x => relativeTo.DetachedFromLogicalTree += x,
x => relativeTo.DetachedFromLogicalTree -= x)
.Select(x => (INameScope)null);
return attached.Merge(detached).Select(nameScope =>
{
if (nameScope != null)
{
var registered = Observable.FromEventPattern<NameScopeEventArgs>(
x => nameScope.Registered += x,
x => nameScope.Registered -= x)
.Where(x => x.EventArgs.Name == name)
.Select(x => x.EventArgs.Element)
.OfType<ILogical>();
var unregistered = Observable.FromEventPattern<NameScopeEventArgs>(
x => nameScope.Unregistered += x,
x => nameScope.Unregistered -= x)
.Where(x => x.EventArgs.Name == name)
.Select(_ => (ILogical)null);
return registered
.StartWith(nameScope.Find<ILogical>(name))
.Merge(unregistered);
}
else
{
return Observable.Return<ILogical>(null);
}
}).Switch();
}
public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null)
{
return TrackAttachmentToTree(relativeTo).Select(isAttachedToTree =>
{
if (isAttachedToTree)
{
return relativeTo.GetLogicalAncestors()
.Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
.ElementAtOrDefault(ancestorLevel);
}
else
{
return null;
}
});
}
private static IObservable<bool> TrackAttachmentToTree(ILogical relativeTo)
{
var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
x => relativeTo.AttachedToLogicalTree += x,
x => relativeTo.AttachedToLogicalTree -= x)
.Select(x => true)
.StartWith(relativeTo.IsAttachedToLogicalTree);
var detached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
x => relativeTo.DetachedFromLogicalTree += x,
x => relativeTo.DetachedFromLogicalTree -= x)
.Select(x => false);
var attachmentStatus = attached.Merge(detached);
return attachmentStatus;
}
}
}

783
src/Avalonia.Styling/StyledElement.cs

@ -0,0 +1,783 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using Avalonia.Animation;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Logging;
using Avalonia.LogicalTree;
using Avalonia.Styling;
namespace Avalonia
{
/// <summary>
/// Extends an <see cref="Animatable"/> with the following features:
///
/// - An inherited <see cref="DataContext"/>.
/// - Implements <see cref="IStyleable"/> to allow styling to work on the styled element.
/// - Implements <see cref="ILogical"/> to form part of a logical tree.
/// - A collection of class strings for custom styling.
/// </summary>
public class StyledElement : Animatable, IStyledElement, ISetLogicalParent, ISetInheritanceParent
{
/// <summary>
/// Defines the <see cref="DataContext"/> property.
/// </summary>
public static readonly StyledProperty<object> DataContextProperty =
AvaloniaProperty.Register<StyledElement, object>(
nameof(DataContext),
inherits: true,
notifying: DataContextNotifying);
/// <summary>
/// Defines the <see cref="Name"/> property.
/// </summary>
public static readonly DirectProperty<StyledElement, string> NameProperty =
AvaloniaProperty.RegisterDirect<StyledElement, string>(nameof(Name), o => o.Name, (o, v) => o.Name = v);
/// <summary>
/// Defines the <see cref="Parent"/> property.
/// </summary>
public static readonly DirectProperty<StyledElement, IStyledElement> ParentProperty =
AvaloniaProperty.RegisterDirect<StyledElement, IStyledElement>(nameof(Parent), o => o.Parent);
/// <summary>
/// Defines the <see cref="TemplatedParent"/> property.
/// </summary>
public static readonly StyledProperty<ITemplatedControl> TemplatedParentProperty =
AvaloniaProperty.Register<StyledElement, ITemplatedControl>(nameof(TemplatedParent), inherits: true);
private int _initCount;
private string _name;
private readonly Classes _classes = new Classes();
private bool _isAttachedToLogicalTree;
private IAvaloniaList<ILogical> _logicalChildren;
private INameScope _nameScope;
private IResourceDictionary _resources;
private Styles _styles;
private bool _styled;
private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
private bool _dataContextUpdating;
/// <summary>
/// Initializes static members of the <see cref="StyledElement"/> class.
/// </summary>
static StyledElement()
{
DataContextProperty.Changed.AddClassHandler<StyledElement>(x => x.OnDataContextChangedCore);
}
/// <summary>
/// Initializes a new instance of the <see cref="StyledElement"/> class.
/// </summary>
public StyledElement()
{
_nameScope = this as INameScope;
_isAttachedToLogicalTree = this is IStyleRoot;
}
/// <summary>
/// Raised when the styled element is attached to a rooted logical tree.
/// </summary>
public event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
/// <summary>
/// Raised when the styled element is detached from a rooted logical tree.
/// </summary>
public event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
/// <summary>
/// Occurs when the <see cref="DataContext"/> property changes.
/// </summary>
/// <remarks>
/// This event will be raised when the <see cref="DataContext"/> property has changed and
/// all subscribers to that change have been notified.
/// </remarks>
public event EventHandler DataContextChanged;
/// <summary>
/// Occurs when the styled element has finished initialization.
/// </summary>
/// <remarks>
/// The Initialized event indicates that all property values on the styled element have been set.
/// When loading the styled element from markup, it occurs when
/// <see cref="ISupportInitialize.EndInit"/> is called *and* the styled element
/// is attached to a rooted logical tree. When the styled element is created by code and
/// <see cref="ISupportInitialize"/> is not used, it is called when the styled element is attached
/// to the visual tree.
/// </remarks>
public event EventHandler Initialized;
/// <summary>
/// Occurs when a resource in this styled element or a parent styled element has changed.
/// </summary>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets or sets the name of the styled element.
/// </summary>
/// <remarks>
/// An element's name is used to uniquely identify an element within the element'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 (String.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException("Cannot set Name to null or empty string.");
}
if (_styled)
{
throw new InvalidOperationException("Cannot set Name : styled element already styled.");
}
_name = value;
}
}
/// <summary>
/// Gets or sets the styled element's classes.
/// </summary>
/// <remarks>
/// <para>
/// Classes can be used to apply user-defined styling to styled elements, or to allow styled elements
/// that share a common purpose to be easily selected.
/// </para>
/// <para>
/// Even though this property can be set, the setter is only intended for use in object
/// initializers. Assigning to this property does not change the underlying collection,
/// it simply clears the existing collection and addds the contents of the assigned
/// collection.
/// </para>
/// </remarks>
public Classes Classes
{
get
{
return _classes;
}
set
{
if (_classes != value)
{
_classes.Replace(value);
}
}
}
/// <summary>
/// Gets or sets the control's data context.
/// </summary>
/// <remarks>
/// The data context is an inherited property that specifies the default object that will
/// be used for data binding.
/// </remarks>
public object DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>
/// <remarks>
/// For more information about when IsInitialized is set, see the <see cref="Initialized"/>
/// event.
/// </remarks>
public bool IsInitialized { get; private set; }
/// <summary>
/// Gets the styles for the styled element.
/// </summary>
/// <remarks>
/// Styles for the entire application are added to the Application.Styles collection, but
/// each styled element may in addition define its own styles which are applied to the styled element
/// itself and its children.
/// </remarks>
public Styles Styles
{
get { return _styles ?? (Styles = new Styles()); }
set
{
Contract.Requires<ArgumentNullException>(value != null);
if (_styles != value)
{
if (_styles != null)
{
(_styles as ISetStyleParent)?.SetParent(null);
_styles.ResourcesChanged -= ThisResourcesChanged;
}
_styles = value;
if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
{
setParent.SetParent(this);
}
_styles.ResourcesChanged += ThisResourcesChanged;
}
}
}
/// <summary>
/// Gets or sets the styled element's resource dictionary.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
Contract.Requires<ArgumentNullException>(value != null);
var hadResources = false;
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ThisResourcesChanged;
}
_resources = value;
_resources.ResourcesChanged += ThisResourcesChanged;
if (hadResources || _resources.Count > 0)
{
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
/// <summary>
/// Gets the styled element whose lookless template this styled element is part of.
/// </summary>
public ITemplatedControl TemplatedParent
{
get { return GetValue(TemplatedParentProperty); }
internal set { SetValue(TemplatedParentProperty, value); }
}
/// <summary>
/// Gets the styled element's logical children.
/// </summary>
protected IAvaloniaList<ILogical> LogicalChildren
{
get
{
if (_logicalChildren == null)
{
var list = new AvaloniaList<ILogical>
{
ResetBehavior = ResetBehavior.Remove,
Validate = ValidateLogicalChild
};
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;
}
return _logicalChildren;
}
}
/// <summary>
/// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
/// pseudoclasses.
/// </summary>
protected IPseudoClasses PseudoClasses => Classes;
/// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree.
/// </summary>
bool ILogical.IsAttachedToLogicalTree => _isAttachedToLogicalTree;
/// <summary>
/// Gets the styled element's logical parent.
/// </summary>
public IStyledElement Parent { get; private set; }
/// <summary>
/// Gets the styled element's logical parent.
/// </summary>
ILogical ILogical.LogicalParent => Parent;
/// <summary>
/// Gets the styled element's logical children.
/// </summary>
IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
/// <inheritdoc/>
IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
/// <summary>
/// Gets the type by which the styled element is styled.
/// </summary>
/// <remarks>
/// Usually controls are styled by their own type, but there are instances where you want
/// a styled element to be styled by its base type, e.g. creating SpecialButton that
/// derives from Button and adds extra functionality but is still styled as a regular
/// Button.
/// </remarks>
Type IStyleable.StyleKey => GetType();
/// <inheritdoc/>
IObservable<IStyleable> IStyleable.StyleDetach => _styleDetach;
/// <inheritdoc/>
bool IStyleHost.IsStylesInitialized => _styles != null;
/// <inheritdoc/>
IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent;
/// <inheritdoc/>
public virtual void BeginInit()
{
++_initCount;
}
/// <inheritdoc/>
public virtual void EndInit()
{
if (_initCount == 0)
{
throw new InvalidOperationException("BeginInit was not called.");
}
if (--_initCount == 0 && _isAttachedToLogicalTree)
{
InitializeStylesIfNeeded();
InitializeIfNeeded();
}
}
private void InitializeStylesIfNeeded(bool force = false)
{
if (_initCount == 0 && (!_styled || force))
{
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
}
protected void InitializeIfNeeded()
{
if (_initCount == 0 && !IsInitialized)
{
IsInitialized = true;
Initialized?.Invoke(this, EventArgs.Empty);
}
}
/// <inheritdoc/>
void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
this.OnAttachedToLogicalTreeCore(e);
}
/// <inheritdoc/>
void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
this.OnDetachedFromLogicalTreeCore(e);
}
/// <inheritdoc/>
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
/// <inheritdoc/>
bool IResourceProvider.TryGetResource(string key, out object value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||
(_styles?.TryGetResource(key, out value) ?? false);
}
/// <summary>
/// Sets the styled element's logical parent.
/// </summary>
/// <param name="parent">The parent.</param>
void ISetLogicalParent.SetParent(ILogical parent)
{
var old = Parent;
if (parent != old)
{
if (old != null && parent != null)
{
throw new InvalidOperationException("The Control already has a parent.");
}
if (_isAttachedToLogicalTree)
{
var oldRoot = FindStyleRoot(old) ?? this as IStyleRoot;
if (oldRoot == null)
{
throw new AvaloniaInternalException("Was attached to logical tree but cannot find root.");
}
var e = new LogicalTreeAttachmentEventArgs(oldRoot);
OnDetachedFromLogicalTreeCore(e);
}
if (InheritanceParent == null || parent == null)
{
InheritanceParent = parent as AvaloniaObject;
}
Parent = (IStyledElement)parent;
if (old != null)
{
old.ResourcesChanged -= ThisResourcesChanged;
}
if (Parent != null)
{
Parent.ResourcesChanged += ThisResourcesChanged;
}
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (Parent is IStyleRoot || Parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
{
var newRoot = FindStyleRoot(this);
if (newRoot == null)
{
throw new AvaloniaInternalException("Parent is atttached to logical tree but cannot find root.");
}
var e = new LogicalTreeAttachmentEventArgs(newRoot);
OnAttachedToLogicalTreeCore(e);
}
RaisePropertyChanged(ParentProperty, old, Parent, BindingPriority.LocalValue);
}
}
/// <summary>
/// Sets the styled element's inheritance parent.
/// </summary>
/// <param name="parent">The parent.</param>
void ISetInheritanceParent.SetParent(IAvaloniaObject parent)
{
InheritanceParent = parent;
}
/// <summary>
/// Adds a pseudo-class to be set when a property is true.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass(AvaloniaProperty<bool> property, string className)
{
PseudoClass(property, x => x, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property equals a certain value.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="selector">Returns a boolean value based on the property value.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass<T>(
AvaloniaProperty<T> property,
Func<T, bool> selector,
string className)
{
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(selector != null);
Contract.Requires<ArgumentNullException>(className != null);
if (string.IsNullOrWhiteSpace(className))
{
throw new ArgumentException("Cannot supply an empty className.");
}
property.Changed.Merge(property.Initialized)
.Where(e => e.Sender is StyledElement)
.Subscribe(e =>
{
if (selector((T)e.NewValue))
{
((StyledElement)e.Sender).PseudoClasses.Add(className);
}
else
{
((StyledElement)e.Sender).PseudoClasses.Remove(className);
}
});
}
/// <summary>
/// Called when the styled element is added to a rooted logical tree.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
}
/// <summary>
/// Called when the styled element is removed from a rooted logical tree.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
}
/// <summary>
/// Called when the <see cref="DataContext"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnDataContextChanged(EventArgs e)
{
DataContextChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Called when the <see cref="DataContext"/> begins updating.
/// </summary>
protected virtual void OnDataContextBeginUpdate()
{
}
/// <summary>
/// Called when the <see cref="DataContext"/> finishes updating.
/// </summary>
protected virtual void OnDataContextEndUpdate()
{
}
private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted)
{
if (o is StyledElement element)
{
DataContextNotifying(element, updateStarted);
}
}
private static void DataContextNotifying(StyledElement element, bool updateStarted)
{
if (updateStarted)
{
if (!element._dataContextUpdating)
{
element._dataContextUpdating = true;
element.OnDataContextBeginUpdate();
foreach (var child in element.LogicalChildren)
{
if (child is StyledElement s &&
s.InheritanceParent == element &&
!s.IsSet(DataContextProperty))
{
DataContextNotifying(s, updateStarted);
}
}
}
}
else
{
if (element._dataContextUpdating)
{
element.OnDataContextEndUpdate();
element._dataContextUpdating = false;
}
}
}
private static IStyleRoot FindStyleRoot(IStyleHost e)
{
while (e != null)
{
if (e is IStyleRoot root)
{
return root;
}
e = e.StylingParent;
}
return null;
}
private void ApplyStyling()
{
AvaloniaLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
}
private void RegisterWithNameScope()
{
if (_nameScope == null)
{
_nameScope = NameScope.GetNameScope(this) ?? ((StyledElement)Parent)?._nameScope;
}
if (Name != null)
{
_nameScope?.Register(Name, this);
var visualParent = Parent as StyledElement;
if (this is INameScope && visualParent != null)
{
// If we have e.g. a named UserControl in a window then we want that control
// to be findable by name from the Window, so register with both name scopes.
// This differs from WPF's behavior in that XAML manually registers controls
// with name scopes based on the XAML file in which the name attribute appears,
// but we're trying to avoid XAML magic in Avalonia in order to made code-
// created UIs easy. This will cause problems if a UserControl declares a name
// in its XAML and that control is included multiple times in a parent control
// (as the name will be duplicated), however at the moment I'm fine with saying
// "don't do that".
var parentNameScope = NameScope.FindNameScope(visualParent);
parentNameScope?.Register(Name, this);
}
}
}
private static void ValidateLogicalChild(ILogical c)
{
if (c == null)
{
throw new ArgumentException("Cannot add null to LogicalChildren.");
}
}
private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
{
// This method can be called when a control is already attached to the logical tree
// in the following scenario:
// - ListBox gets assigned Items containing ListBoxItem
// - ListBox makes ListBoxItem a logical child
// - ListBox template gets applied; making its Panel get attached to logical tree
// - That AttachedToLogicalTree signal travels down to the ListBoxItem
if (!_isAttachedToLogicalTree)
{
_isAttachedToLogicalTree = true;
InitializeStylesIfNeeded(true);
OnAttachedToLogicalTree(e);
AttachedToLogicalTree?.Invoke(this, e);
}
foreach (var child in LogicalChildren.OfType<StyledElement>())
{
child.OnAttachedToLogicalTreeCore(e);
}
}
private void OnDetachedFromLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
{
if (_isAttachedToLogicalTree)
{
if (Name != null)
{
_nameScope?.Unregister(Name);
}
_isAttachedToLogicalTree = false;
_styleDetach.OnNext(this);
OnDetachedFromLogicalTree(e);
DetachedFromLogicalTree?.Invoke(this, e);
foreach (var child in LogicalChildren.OfType<StyledElement>())
{
child.OnDetachedFromLogicalTreeCore(e);
}
#if DEBUG
if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0)
{
Logger.Warning(
LogArea.Control,
this,
"{Type} detached from logical tree but still has class listeners",
this.GetType());
}
#endif
}
}
private void OnDataContextChangedCore(AvaloniaPropertyChangedEventArgs e)
{
OnDataContextChanged(EventArgs.Empty);
}
private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Remove:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Replace:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Reset:
throw new NotSupportedException("Reset should not be signalled on LogicalChildren collection");
}
}
private void SetLogicalParent(IEnumerable<ILogical> children)
{
foreach (var i in children)
{
if (i.LogicalParent == null)
{
((ISetLogicalParent)i).SetParent(this);
}
}
}
private void ClearLogicalParent(IEnumerable<ILogical> children)
{
foreach (var i in children)
{
if (i.LogicalParent == this)
{
((ISetLogicalParent)i).SetParent(null);
}
}
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
((ILogical)this).NotifyResourcesChanged(e);
}
}
}

14
src/Avalonia.Styling/Styling/IRequiresTemplateInSetter.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Styling
{
/// <summary>
/// This is an interface for advanced scenarios to assist users in correct style development.
/// You as a user will not need to use this interface directly.
/// </summary>
public interface IRequiresTemplateInSetter
{
}
}

1
src/Avalonia.Styling/Styling/IStyleable.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Collections;
using Avalonia.LogicalTree;
namespace Avalonia.Styling
{

9
src/Avalonia.Styling/Styling/Setter.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using System.Reflection;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Metadata;
@ -19,7 +20,7 @@ namespace Avalonia.Styling
/// A <see cref="Setter"/> is used to set a <see cref="AvaloniaProperty"/> value on a
/// <see cref="AvaloniaObject"/> depending on a condition.
/// </remarks>
public class Setter : ISetter
public class Setter : ISetter, IAnimationSetter
{
private object _value;
@ -65,10 +66,10 @@ namespace Avalonia.Styling
set
{
if (value is IStyleable)
if (value is IRequiresTemplateInSetter)
{
throw new ArgumentException(
"Cannot assign a control to Style.Value. Wrap the control in a <Template>.",
"Cannot assign a control to Setter.Value. Wrap the control in a <Template>.",
"value");
}
@ -105,7 +106,7 @@ namespace Avalonia.Styling
if (template != null && !isPropertyOfTypeITemplate)
{
var materialized = template.Build();
NameScope.SetNameScope((Visual)materialized, new NameScope());
NameScope.SetNameScope((StyledElement)materialized, new NameScope());
value = materialized;
}

15
src/Avalonia.Styling/app.config

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reactive.Interfaces" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.2.5.0" newVersion="2.2.5.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reactive.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.2.5.0" newVersion="2.2.5.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

22
src/Avalonia.Visuals/Animation/TransformSetter.cs

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
using Avalonia.Media;
namespace Avalonia.Animation
{
/// <summary>
/// Setter that handles <see cref="Transform"/> objects
/// in the target.
/// </summary>
[Animator(typeof(TransformAnimator))]
public class TransformSetter : AnimationSetter
{
}
}

3
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -5,6 +5,7 @@
<ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</ItemGroup>
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
</Project>

5
src/Avalonia.Visuals/Media/Transform.cs

@ -12,6 +12,11 @@ namespace Avalonia.Media
/// </summary>
public abstract class Transform : Animatable
{
static Transform()
{
Animation.Animation.RegisterAnimator<TransformAnimator>(prop => typeof(Transform).IsAssignableFrom(prop.OwnerType));
}
/// <summary>
/// Raised when the transform changes.
/// </summary>

2
src/Avalonia.Visuals/Visual.cs

@ -24,7 +24,7 @@ namespace Avalonia
/// <see cref="IRenderer"/> to render the control. To traverse the visual tree, use the
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
public class Visual : Animatable, IVisual
public class Visual : StyledElement, IVisual
{
/// <summary>
/// Defines the <see cref="Bounds"/> property.

46
src/Avalonia.Visuals/VisualTree/VisualLocator.cs

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using System.Text;
namespace Avalonia.VisualTree
{
public class VisualLocator
{
public static IObservable<IVisual> Track(IVisual relativeTo, int ancestorLevel, Type ancestorType = null)
{
return TrackAttachmentToTree(relativeTo).Select(isAttachedToTree =>
{
if (isAttachedToTree)
{
return relativeTo.GetVisualAncestors()
.Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
.ElementAtOrDefault(ancestorLevel);
}
else
{
return null;
}
});
}
private static IObservable<bool> TrackAttachmentToTree(IVisual relativeTo)
{
var attached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => relativeTo.AttachedToVisualTree += x,
x => relativeTo.AttachedToVisualTree -= x)
.Select(x => true)
.StartWith(relativeTo.IsAttachedToVisualTree);
var detached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => relativeTo.DetachedFromVisualTree += x,
x => relativeTo.DetachedFromVisualTree -= x)
.Select(x => false);
var attachmentStatus = attached.Merge(detached);
return attachmentStatus;
}
}
}

1
src/Directory.Build.props

@ -1,3 +1,4 @@
<Project>
<Import Project="..\build\SharedVersion.props" />
<Import Project="..\build\SourceLink.props" Condition="'$(DisableSourceLink)' == ''" />
</Project>

13
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DefineConstants>PCL;NETSTANDARD;NETSTANDARD2_0;HAS_TYPE_CONVERTER;HAS_CUSTOM_ATTRIBUTE_PROVIDER</DefineConstants>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
@ -9,7 +10,6 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;NETSTANDARD1_3;PCL;NETSTANDARD</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoWarn>CS1591</NoWarn>
@ -18,7 +18,6 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>NETSTANDARD1_3;PCL;NETSTANDARD</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Avalonia.Markup.Xaml.xml</DocumentationFile>
@ -30,8 +29,8 @@
<Compile Include="Converters\ParseTypeConverter.cs" />
<Compile Include="Converters\SetterValueTypeConverter.cs" />
<Compile Include="Converters\TimeSpanTypeConverter.cs" />
<Compile Include="Data\ResourceInclude.cs" />
<Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
<Compile Include="MarkupExtensions\ResourceInclude.cs" />
<Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
<Compile Include="MarkupExtensions\StyleIncludeExtension.cs" />
<Compile Include="PortableXaml\AvaloniaXamlContext.cs" />
@ -47,15 +46,9 @@
<Compile Include="Converters\AvaloniaPropertyTypeConverter.cs" />
<Compile Include="Converters\PointsListTypeConverter.cs" />
<Compile Include="Converters\SelectorTypeConverter.cs" />
<Compile Include="Data\Binding.cs" />
<Compile Include="Data\DelayedBinding.cs" />
<Compile Include="Data\MultiBinding.cs" />
<Compile Include="Data\RelativeSource.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />
<Compile Include="Parsers\SelectorGrammar.cs" />
<Compile Include="Parsers\SelectorParser.cs" />
<Compile Include="PortableXaml\AvaloniaTypeAttributeProvider.cs" />
<Compile Include="PortableXaml\AvaloniaXamlType.cs" />
<Compile Include="PortableXaml\TypeDescriptorExtensions.cs" />
@ -72,7 +65,6 @@
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlLoadException.cs" />
<Compile Include="PortableXaml\portable.xaml.github\src\Portable.Xaml\**\*.cs" Exclude="PortableXaml\portable.xaml.github\src\Portable.Xaml\Assembly\**\*.cs" />
<Compile Remove="**\UriTypeConverter.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
@ -86,6 +78,5 @@
<ProjectReference Include="..\Avalonia.Markup\Avalonia.Markup.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Markup.props" />
<Import Project="..\..\..\build\Sprache.props" />
<Import Project="..\..\..\build\Rx.props" />
</Project>

2
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -2,7 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;

2
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Converters
using Avalonia.Styling;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;
using System.ComponentModel;
using System.ComponentModel;
using Portable.Xaml.Markup;
public class AvaloniaPropertyTypeConverter : TypeConverter

4
src/Markup/Avalonia.Markup.Xaml/Converters/SelectorTypeConverter.cs

@ -3,12 +3,12 @@
using System;
using System.Globalization;
using Avalonia.Markup.Xaml.Parsers;
using Avalonia.Markup.Parsers;
namespace Avalonia.Markup.Xaml.Converters
{
using Portable.Xaml.ComponentModel;
using System.ComponentModel;
using System.ComponentModel;
public class SelectorTypeConverter : TypeConverter
{

3
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -2,12 +2,13 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Data;
using Avalonia.Markup.Xaml.Data;
using System;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
using Avalonia.Controls;
using Avalonia.Data.Converters;
using Avalonia.Markup.Data;
using Avalonia.Styling;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs

@ -1,7 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Markup.Xaml.Data;
using Avalonia.Data;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{

2
src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs → src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs

@ -4,7 +4,7 @@ using Avalonia.Controls;
using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml.Data
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
/// <summary>
/// Loads a resource dictionary from a specified URL.

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Data;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
@ -69,7 +69,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
throw new KeyNotFoundException($"Static resource '{ResourceKey}' not found.");
}
private object GetValue(IControl control)
private object GetValue(IStyledElement control)
{
return control.FindResource(ResourceKey);
}

5
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs

@ -2,14 +2,15 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Data;
using Avalonia.Markup.Xaml.Data;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
using System;
using Avalonia.Data.Converters;
using Avalonia.Markup.Data;
using Portable.Xaml.Markup;
[MarkupExtensionReturnType(typeof(Binding))]
[MarkupExtensionReturnType(typeof(IBinding))]
public class TemplateBindingExtension : MarkupExtension
{
public TemplateBindingExtension()

4
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaRuntimeTypeProvider.cs

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
using Avalonia.Metadata;
@ -35,7 +37,7 @@ namespace Avalonia.Markup.Xaml.Context
typeof(Style).GetTypeInfo().Assembly,
typeof(DataTemplate).GetTypeInfo().Assembly,
typeof(SolidColorBrush).GetTypeInfo().Assembly,
typeof(IValueConverter).GetTypeInfo().Assembly,
typeof(Binding).GetTypeInfo().Assembly,
};
private Dictionary<string, HashSet<ClrNamespaceInfo>> _namespaces = new Dictionary<string, HashSet<ClrNamespaceInfo>>();

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs

@ -1,6 +1,6 @@
using Avalonia.Data;
using Avalonia.Markup.Xaml.Context;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.Styling;
using Portable.Xaml;

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs

@ -1,6 +1,6 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Data;
using Avalonia.Metadata;
using Avalonia.Styling;
using Portable.Xaml;

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@ -1 +1 @@
Subproject commit cdf46d7892def8a6ba29f12a9339147377f7cf5c
Subproject commit 8abbe09592668efb573ac4d5548ba2d7e464ba78

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

Loading…
Cancel
Save