Browse Source

Merge branch 'master' into datagrid-indexer-sorting

pull/5131/head
Max Katz 6 years ago
committed by GitHub
parent
commit
9317d6d115
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/Avalonia.Base/Logging/LogArea.cs
  2. 4
      src/Avalonia.Controls/Button.cs
  3. 26
      src/Avalonia.Controls/HotkeyManager.cs
  4. 4
      src/Avalonia.Controls/MenuItem.cs
  5. 2
      src/Avalonia.Controls/NativeMenuItem.cs
  6. 3
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
  7. 36
      src/Avalonia.Input/ICommandSource.cs
  8. 23
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  9. 100
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
  10. 8
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
  11. 10
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  12. 4
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  13. 27
      src/Windows/Avalonia.Win32/Win32Platform.cs
  14. 141
      tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
  15. 121
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

5
src/Avalonia.Base/Logging/LogArea.cs

@ -34,5 +34,10 @@ namespace Avalonia.Logging
/// The log event comes from the control system.
/// </summary>
public const string Control = "Control";
/// <summary>
/// The log event comes from Win32Platform.
/// </summary>
public const string Win32Platform = nameof(Win32Platform);
}
}

4
src/Avalonia.Controls/Button.cs

@ -30,7 +30,7 @@ namespace Avalonia.Controls
/// A button control.
/// </summary>
[PseudoClasses(":pressed")]
public class Button : ContentControl
public class Button : ContentControl, ICommandSource
{
/// <summary>
/// Defines the <see cref="ClickMode"/> property.
@ -492,5 +492,7 @@ namespace Avalonia.Controls
{
PseudoClasses.Set(":pressed", isPressed);
}
void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e);
}
}

26
src/Avalonia.Controls/HotkeyManager.cs

@ -12,18 +12,21 @@ namespace Avalonia.Controls
class HotkeyCommandWrapper : ICommand
{
public HotkeyCommandWrapper(IControl control)
public HotkeyCommandWrapper(ICommandSource control)
{
Control = control;
CommandSource = control;
}
public readonly IControl Control;
public readonly ICommandSource CommandSource;
private ICommand GetCommand() => Control.GetValue(Button.CommandProperty);
private ICommand GetCommand() => CommandSource.Command;
public bool CanExecute(object parameter) => GetCommand()?.CanExecute(parameter) ?? false;
public bool CanExecute(object parameter) =>
CommandSource.Command?.CanExecute(CommandSource.CommandParameter) == true
&& CommandSource.IsEffectivelyEnabled;
public void Execute(object parameter) => GetCommand()?.Execute(parameter);
public void Execute(object parameter) =>
GetCommand()?.Execute(CommandSource.CommandParameter);
#pragma warning disable 67 // Event not used
public event EventHandler CanExecuteChanged;
@ -44,7 +47,7 @@ namespace Avalonia.Controls
public Manager(IControl control)
{
_control = control;
_wrapper = new HotkeyCommandWrapper(_control);
_wrapper = new HotkeyCommandWrapper(_control as ICommandSource);
}
public void Init()
@ -84,7 +87,7 @@ namespace Avalonia.Controls
{
if (_root != null && _hotkey != null)
{
_binding = new KeyBinding() {Gesture = _hotkey, Command = _wrapper};
_binding = new KeyBinding() { Gesture = _hotkey, Command = _wrapper };
_root.KeyBindings.Add(_binding);
}
}
@ -102,8 +105,13 @@ namespace Avalonia.Controls
HotKeyProperty.Changed.Subscribe(args =>
{
var control = args.Sender as IControl;
if (args.OldValue != null|| control == null)
if (args.OldValue != null || control == null || !(control is ICommandSource))
{
Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?.
Log(control, $"The element {args.Sender.GetType().Name} does not support binding a HotKey ({args.NewValue}).");
return;
}
new Manager(control).Init();
});
}

4
src/Avalonia.Controls/MenuItem.cs

@ -22,7 +22,7 @@ namespace Avalonia.Controls
/// A menu item control.
/// </summary>
[PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")]
public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable
public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource
{
/// <summary>
/// Defines the <see cref="Command"/> property.
@ -609,6 +609,8 @@ namespace Avalonia.Controls
SelectedItem = null;
}
void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e);
/// <summary>
/// A dependency resolver which returns a <see cref="MenuItemAccessKeyHandler"/>.
/// </summary>

2
src/Avalonia.Controls/NativeMenuItem.cs

@ -150,7 +150,7 @@ namespace Avalonia.Controls
void CanExecuteChanged()
{
IsEnabled = _command?.CanExecute(null) ?? true;
IsEnabled = _command?.CanExecute(CommandParameter) ?? true;
}
public bool HasClickHandlers => Click != null;

3
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml

@ -12,6 +12,9 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Controls.DataGrid.Themes.Default.xaml?assembly=Avalonia.Controls.DataGrid"/>
<Style Selector="DataGrid ContextMenu">
<Setter Property="Foreground" Value="Black"/>
</Style>
</Window.Styles>
<views:MainView/>

36
src/Avalonia.Input/ICommandSource.cs

@ -0,0 +1,36 @@
using System.Windows.Input;
namespace Avalonia.Input
{
///<summary>
/// An interface for classes that know how to invoke a Command.
///</summary>
public interface ICommandSource
{
/// <summary>
/// The command that will be executed when the class is "invoked."
/// Classes that implement this interface should enable or disable based on the command's CanExecute return value.
/// The property may be implemented as read-write if desired.
/// </summary>
ICommand Command { get; }
/// <summary>
/// The parameter that will be passed to the command when executing the command.
/// The property may be implemented as read-write if desired.
/// </summary>
object CommandParameter { get; }
/// <summary>
/// Bor the bheavior CanExecuteChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void CanExecuteChanged(object sender, System.EventArgs e);
/// <summary>
/// Gets a value indicating whether this control and all its parents are enabled.
/// </summary>
bool IsEffectivelyEnabled { get; }
}
}

23
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs

@ -72,16 +72,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.ElementName);
var sourceProperty = syntheticCompiledBindingProperties
.FirstOrDefault(v =>
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.Source);
var relativeSourceProperty = syntheticCompiledBindingProperties
.FirstOrDefault(v =>
v.Property is AvaloniaSyntheticCompiledBindingProperty prop
&& prop.Name == SyntheticCompiledBindingPropertyName.RelativeSource);
var sourceProperty = binding.Children.OfType<XamlAstXamlPropertyValueNode>()
.FirstOrDefault(v =>
v.Property is XamlAstClrProperty prop
&& prop.Name == "Source");
if (elementNameProperty?.Values[0] is XamlAstTextNode elementName)
{
convertedNode = new BindingExpressionGrammar.NameNode { Name = elementName.Text };
@ -91,14 +91,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
throw new XamlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]);
}
if (sourceProperty?.Values[0] != null)
if (sourceProperty != null && convertedNode != null)
{
if (convertedNode != null)
{
throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
convertedNode = new RawSourceBindingExpressionNode(sourceProperty?.Values[0]);
throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
if (GetRelativeSourceObjectFromAssignment(
@ -223,10 +218,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
binding.Children.Remove(elementNameProperty);
}
if (sourceProperty != null)
{
binding.Children.Remove(sourceProperty);
}
if (relativeSourceProperty != null)
{
binding.Children.Remove(relativeSourceProperty);

100
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XamlX;
using XamlX.Ast;
using XamlX.Transform;
using XamlX.TypeSystem;
@ -15,14 +13,102 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (node is XamlAstConstructableObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
{
IXamlType startType;
var parentDataContextNode = context.ParentNodes().OfType<AvaloniaXamlIlDataContextTypeMetadataNode>().FirstOrDefault();
if (parentDataContextNode is null)
IXamlType startType = null;
var sourceProperty = binding.Children.OfType<XamlPropertyAssignmentNode>().FirstOrDefault(c => c.Property.Name == "Source");
if ((sourceProperty?.Values.Count ?? 0) == 1)
{
throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding);
var sourceValue = sourceProperty.Values[0];
switch (sourceValue)
{
case XamlAstTextNode textNode:
startType = textNode.Type?.GetClrType();
break;
case XamlMarkupExtensionNode extension:
startType = extension.Type?.GetClrType();
//let's try to infer StaticResource type from parent resources in xaml
if (extension.Value.Type.GetClrType().FullName == "Avalonia.Markup.Xaml.MarkupExtensions.StaticResourceExtension" &&
extension.Value is XamlAstConstructableObjectNode cn &&
cn.Arguments.Count == 1 && cn.Arguments[0] is XamlAstTextNode keyNode)
{
bool matchProperty(IXamlAstNode node, IXamlType styledElementType, string propertyName)
{
return (node is XamlPropertyAssignmentNode p &&
p.Property.DeclaringType == styledElementType && p.Property.Name == propertyName)
||
(node is XamlManipulationGroupNode m && m.Children.Count > 0 &&
m.Children[0] is XamlPropertyAssignmentNode pm &&
pm.Property.DeclaringType == styledElementType && pm.Property.Name == propertyName);
}
string getResourceValue_xKey(XamlPropertyAssignmentNode node)
=> node.Values.Count == 2 && node.Values[0] is XamlAstTextNode t ? t.Text : "";
IXamlType getResourceValue_Type(XamlPropertyAssignmentNode node, IXamlType xamlType)
=> node.Values.Count == 2 ? node.Values[1].Type.GetClrType() : xamlType;
IEnumerable<XamlPropertyAssignmentNode> getResourceValues(IXamlAstNode node)
{
if (node is XamlPropertyAssignmentNode propertyNode)
{
if (propertyNode.Values.Count == 1 &&
propertyNode.Values[0] is XamlAstConstructableObjectNode obj &&
obj.Type.GetClrType().FullName == "Avalonia.Controls.ResourceDictionary")
{
foreach (var r in obj.Children.SelectMany(c => getResourceValues(c)))
{
yield return r;
}
}
else
{
yield return propertyNode;
}
}
else if (node is XamlManipulationGroupNode m)
{
foreach (var r in m.Children.OfType<XamlPropertyAssignmentNode>())
{
yield return r;
}
}
}
string key = keyNode.Text;
var styledElement = context.GetAvaloniaTypes().StyledElement;
var resource = context.ParentNodes()
.OfType<XamlAstConstructableObjectNode>()
.Where(o => styledElement.IsAssignableFrom(o.Type.GetClrType()))
.Select(o => o.Children.FirstOrDefault(p => matchProperty(p, styledElement, "Resources")))
.Where(r => r != null)
.SelectMany(r => getResourceValues(r))
.FirstOrDefault(r => getResourceValue_xKey(r) == key);
if (resource != null)
{
startType = getResourceValue_Type(resource, startType);
}
}
break;
case XamlStaticExtensionNode staticExtension:
startType = staticExtension.Type?.GetClrType();
break;
}
}
startType = parentDataContextNode.DataContextType;
if (startType == null)
{
var parentDataContextNode = context.ParentNodes().OfType<AvaloniaXamlIlDataContextTypeMetadataNode>().FirstOrDefault();
if (parentDataContextNode is null)
{
throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding);
}
startType = parentDataContextNode.DataContextType;
}
XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, binding, startType);
}

8
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs

@ -25,11 +25,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return new AvaloniaSyntheticCompiledBindingProperty(node,
SyntheticCompiledBindingPropertyName.RelativeSource);
}
else if (prop.Name == "Source")
{
return new AvaloniaSyntheticCompiledBindingProperty(node,
SyntheticCompiledBindingPropertyName.Source);
}
}
return node;
@ -39,8 +34,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
enum SyntheticCompiledBindingPropertyName
{
ElementName,
RelativeSource,
Source
RelativeSource
}
class AvaloniaSyntheticCompiledBindingProperty : XamlAstNode, IXamlAstPropertyReference

10
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

@ -32,6 +32,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
Mode = Mode,
Priority = Priority,
StringFormat = StringFormat,
Source = Source,
DefaultAnchor = new WeakReference(GetDefaultAnchor(provider))
};
}
@ -52,6 +53,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation)
{
if (Source != null)
{
return CreateSourceObserver(
Source,
Path.BuildExpression(enableDataValidation));
}
if (Path.RawSource != null)
{
return CreateSourceObserver(
@ -77,5 +85,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
[ConstructorArgument("path")]
public CompiledBindingPath Path { get; set; }
public object Source { get; set; }
}
}

4
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -931,8 +931,8 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll", EntryPoint = "MapVirtualKeyW")]
public static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll", EntryPoint = "GetMessageW")]
public static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
[DllImport("user32.dll", EntryPoint = "GetMessageW", SetLastError = true)]
public static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
[DllImport("user32.dll")]
public static extern int GetMessageTime();

27
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -130,21 +130,34 @@ namespace Avalonia.Win32
public void ProcessMessage()
{
UnmanagedMethods.MSG msg;
UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0);
UnmanagedMethods.TranslateMessage(ref msg);
UnmanagedMethods.DispatchMessage(ref msg);
if (UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1)
{
UnmanagedMethods.TranslateMessage(ref msg);
UnmanagedMethods.DispatchMessage(ref msg);
}
else
{
Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform)
?.Log(this, "Unmanaged error in {0}. Error Code: {1}", nameof(ProcessMessage), Marshal.GetLastWin32Error());
}
}
public void RunLoop(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
var result = 0;
while (!cancellationToken.IsCancellationRequested
&& (result = UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0)) > 0)
{
UnmanagedMethods.MSG msg;
UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0);
UnmanagedMethods.TranslateMessage(ref msg);
UnmanagedMethods.DispatchMessage(ref msg);
}
if (result < 0)
{
Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform)
?.Log(this, "Unmanaged error in {0}. Error Code: {1}", nameof(RunLoop), Marshal.GetLastWin32Error());
}
}
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action callback)

141
tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs

@ -5,6 +5,9 @@ using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Styling;
using Xunit;
using System;
using Avalonia.Input.Raw;
using Factory = System.Func<int, System.Action<object>, Avalonia.Controls.Window, Avalonia.AvaloniaObject>;
namespace Avalonia.Controls.UnitTests.Utils
{
@ -54,6 +57,127 @@ namespace Avalonia.Controls.UnitTests.Utils
}
}
[Theory]
[MemberData(nameof(ElementsFactory))]
public void HotKeyManager_Should_Use_CommandParameter(string factoryName, Factory factory)
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var commandResult = 0;
var expectedParameter = 1;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
var action = new Action<object>(parameter =>
{
if (parameter is int value)
{
commandResult = value;
}
});
var root = new Window();
var element = factory(expectedParameter, action, root);
root.Template = CreateWindowTemplate();
root.ApplyTemplate();
root.Presenter.ApplyTemplate();
HotKeyManager.SetHotKey(element, gesture);
target.ProcessRawEvent(new RawKeyEventArgs(target,
0,
root,
RawKeyEventType.KeyDown,
Key.A,
RawInputModifiers.Control));
Assert.True(expectedParameter == commandResult, $"{factoryName} HotKey did not carry the CommandParameter.");
}
}
[Theory]
[MemberData(nameof(ElementsFactory))]
public void HotKeyManager_Should_Do_Not_Executed_When_IsEnabled_False(string factoryName, Factory factory)
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var isExecuted = false;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
var action = new Action<object>(parameter =>
{
isExecuted = true;
});
var root = new Window();
var element = factory(0, action, root) as InputElement;
element.IsEnabled = false;
root.Template = CreateWindowTemplate();
root.ApplyTemplate();
root.Presenter.ApplyTemplate();
HotKeyManager.SetHotKey(element, gesture);
target.ProcessRawEvent(new RawKeyEventArgs(target,
0,
root,
RawKeyEventType.KeyDown,
Key.A,
RawInputModifiers.Control));
Assert.True(isExecuted == false, $"{factoryName} Execution raised when IsEnabled is false.");
}
}
public static TheoryData<string, Factory> ElementsFactory =>
new TheoryData<string, Factory>()
{
{nameof(Button), MakeButton},
{nameof(MenuItem),MakeMenu},
};
private static AvaloniaObject MakeMenu(int expectedParameter, Action<object> action, Window root)
{
var menuitem = new MenuItem()
{
Command = new Command(action),
CommandParameter = expectedParameter,
};
var rootMenu = new Menu();
rootMenu.Items = new[] { menuitem };
root.Content = rootMenu;
return menuitem;
}
private static AvaloniaObject MakeButton(int expectedParameter, Action<object> action, Window root)
{
var button = new Button()
{
Command = new Command(action),
CommandParameter = expectedParameter,
};
root.Content = button;
return button;
}
private FuncControlTemplate CreateWindowTemplate()
{
return new FuncControlTemplate<Window>((parent, scope) =>
@ -65,5 +189,22 @@ namespace Avalonia.Controls.UnitTests.Utils
}.RegisterInNameScope(scope);
});
}
class Command : System.Windows.Input.ICommand
{
private readonly Action<object> _execeute;
#pragma warning disable 67 // Event not used
public event EventHandler CanExecuteChanged;
#pragma warning restore 67 // Event not used
public Command(Action<object> execeute)
{
_execeute = execeute;
}
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _execeute?.Invoke(parameter);
}
}
}

121
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -10,6 +10,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Avalonia.Markup.Data;
using Avalonia.Media;
using Avalonia.UnitTests;
using XamlX;
using Xunit;
@ -536,7 +537,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
[Fact]
public void ResolvesSourceBindingLongForm()
public void Binds_To_Source()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@ -559,6 +560,124 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
[Fact]
public void Binds_To_Source_StaticResource()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='using:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'
x:CompileBindings='True'>
<Window.Resources>
<local:TestDataContext x:Key='dataKey' StringProperty='foobar'/>
</Window.Resources>
<TextBlock Name='textBlock' Text='{Binding StringProperty, Source={StaticResource dataKey}}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text);
}
}
[Fact]
public void Binds_To_Source_StaticResource1()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='using:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'
x:CompileBindings='True'>
<Window.Resources>
<local:TestDataContext x:Key='dataKey' StringProperty='foobar'/>
<x:String x:Key='otherObjectKey'>test</x:String>
</Window.Resources>
<TextBlock Name='textBlock' Text='{Binding StringProperty, Source={StaticResource dataKey}}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text);
}
}
[Fact]
public void Binds_To_Source_StaticResource_In_ResourceDictionary()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='using:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'
x:DataType='local:TestDataContext' x:CompileBindings='True'>
<Window.Resources>
<ResourceDictionary>
<local:TestDataContext x:Key='dataKey' StringProperty='foobar'/>
</ResourceDictionary>
</Window.Resources>
<TextBlock Name='textBlock' Text='{Binding StringProperty, Source={StaticResource dataKey}}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text);
}
}
[Fact]
public void Binds_To_Source_StaticResource_In_ResourceDictionary1()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='using:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'
x:DataType='local:TestDataContext' x:CompileBindings='True'>
<Window.Resources>
<ResourceDictionary>
<local:TestDataContext x:Key='dataKey' StringProperty='foobar'/>
<x:String x:Key='otherObjectKey'>test</x:String>
</ResourceDictionary>
</Window.Resources>
<TextBlock Name='textBlock' Text='{Binding StringProperty, Source={StaticResource dataKey}}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text);
}
}
[Fact]
public void Binds_To_Source_xStatic()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='using:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'
x:CompileBindings='True'>
<ContentControl Name='contentControl' Content='{Binding Color, Source={x:Static Brushes.Red}}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl");
Assert.Equal(Brushes.Red.Color, contentControl.Content);
}
}
[Fact]
public void CompilesBindingWhenRequested()
{

Loading…
Cancel
Save