Browse Source

Merge branch 'master' into features/Issue_6984

pull/6992/head
Giuseppe Lippolis 5 years ago
parent
commit
83b1ff6705
  1. 7
      native/Avalonia.Native/src/OSX/window.mm
  2. 2
      packages/Avalonia/AvaloniaBuildTasks.targets
  3. 2
      src/Avalonia.Base/Data/BindingValue.cs
  4. 2
      src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs
  5. 7
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  6. 52
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  7. 6
      src/Avalonia.Controls/Application.cs
  8. 14
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  9. 6
      src/Avalonia.Controls/Flyouts/FlyoutBase.cs
  10. 8
      src/Avalonia.Controls/Primitives/Popup.cs
  11. 10
      src/Avalonia.Controls/SplitView.cs
  12. 2
      src/Avalonia.Controls/TrayIcon.cs
  13. 6
      src/Avalonia.Controls/WindowBase.cs
  14. 3
      src/Avalonia.Input/ApiCompatBaseline.txt
  15. 16
      src/Avalonia.Input/FocusManager.cs
  16. 6
      src/Avalonia.Input/ICommandSource.cs
  17. 8
      src/Avalonia.Input/IFocusManager.cs
  18. 6
      src/Avalonia.Styling/Styling/PropertySetterInstance.cs
  19. 4
      src/Avalonia.Styling/Styling/Setter.cs
  20. 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  21. 1
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  22. 2
      src/tools/MicroComGenerator/CSharpGen.Utils.cs
  23. 27
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

7
native/Avalonia.Native/src/OSX/window.mm

@ -206,7 +206,11 @@ public:
auto window = Window;
Window = nullptr;
[window close];
try{
// Seems to throw sometimes on application exit.
[window close];
}
catch(NSException*){}
}
return S_OK;
@ -724,6 +728,7 @@ private:
if (cparent->WindowState() == Minimized)
cparent->SetWindowState(Normal);
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
UpdateStyle();

2
packages/Avalonia/AvaloniaBuildTasks.targets

@ -88,6 +88,7 @@
<AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath>
<AvaloniaXamlOriginalCopyFilePath Condition="'$(AvaloniaXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/original.dll</AvaloniaXamlOriginalCopyFilePath>
<AvaloniaXamlIlVerifyIl Condition="'$(AvaloniaXamlIlVerifyIl)' == ''">false</AvaloniaXamlIlVerifyIl>
<AvaloniaXamlIlDebuggerLaunch Condition="'$(AvaloniaXamlIlDebuggerLaunch)' == ''">false</AvaloniaXamlIlDebuggerLaunch>
</PropertyGroup>
<WriteLinesToFile
Condition="'$(_AvaloniaForceInternalMSBuild)' != 'true'"
@ -107,6 +108,7 @@
DelaySign="$(DelaySign)"
EnableComInteropPatching="$(_AvaloniaPatchComInterop)"
SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"

2
src/Avalonia.Base/Data/BindingValue.cs

@ -247,7 +247,7 @@ namespace Avalonia.Data
UnsetValueType _ => Unset,
DoNothingType _ => DoNothing,
BindingNotification n => n.ToBindingValue().Cast<T>(),
_ => new BindingValue<T>((T)value)
_ => new BindingValue<T>((T?)value)
};
}

2
src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs

@ -161,7 +161,7 @@ namespace Avalonia.Data.Converters
.Compile();
}
private static Expression? ConvertTarget(object? target, MethodInfo method) =>
private static Expression ConvertTarget(object target, MethodInfo method) =>
target is null ? null : Expression.Convert(Expression.Constant(target), method.DeclaringType);
internal class WeakPropertyChangedProxy

7
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@ -1,9 +1,6 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.Build.Framework;
namespace Avalonia.Build.Tasks
@ -41,7 +38,7 @@ namespace Avalonia.Build.Tasks
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath, VerifyIl, outputImportance,
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
EnableComInteropPatching, SkipXamlCompilation);
EnableComInteropPatching, SkipXamlCompilation, DebuggerLaunch);
if (!res.Success)
return false;
if (!res.WrittenFile)
@ -87,5 +84,7 @@ namespace Avalonia.Build.Tasks
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
public bool DebuggerLaunch { get; set; }
}
}

52
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
using Microsoft.Build.Framework;
using Mono.Cecil;
using Avalonia.Utilities;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using XamlX;
@ -44,16 +41,23 @@ namespace Avalonia.Build.Tasks
string projectDirectory,
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom,
bool skipXamlCompilation)
{
return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false);
}
internal static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch)
{
var typeSystem = new CecilTypeSystem(references
.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll"))
.Concat(new[] { input }), input);
var asm = typeSystem.TargetAssemblyDefinition;
if (!skipXamlCompilation)
{
var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance);
var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch);
if (compileRes == null && !patchCom)
return new CompileResult(true);
if (compileRes == false)
@ -62,7 +66,7 @@ namespace Avalonia.Build.Tasks
if (patchCom)
ComInteropHelper.PatchAssembly(asm, typeSystem);
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
@ -70,13 +74,43 @@ namespace Avalonia.Build.Tasks
asm.Write(output, writerParameters);
return new CompileResult(true, true);
}
static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem,
string projectDirectory, bool verifyIl,
MessageImportance logImportance)
MessageImportance logImportance
, bool debuggerLaunch = false)
{
if (debuggerLaunch)
{
// According this https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debugger.launch?view=net-6.0#remarks
// documentation, on not windows platform Debugger.Launch() always return true without running a debugger.
if (System.Diagnostics.Debugger.Launch())
{
// Set timeout at 1 minut.
var time = new System.Diagnostics.Stopwatch();
var timeout = TimeSpan.FromMinutes(1);
time.Start();
// wait for the debugger to be attacked or timeout.
while (!System.Diagnostics.Debugger.IsAttached && time.Elapsed < timeout)
{
engine.LogMessage($"[PID:{System.Diagnostics.Process.GetCurrentProcess().Id}] Wating attach debugger. Elapsed {time.Elapsed}...", MessageImportance.High);
System.Threading.Thread.Sleep(100);
}
time.Stop();
if (time.Elapsed >= timeout)
{
engine.LogMessage("Wating attach debugger timeout.", MessageImportance.Normal);
}
}
else
{
engine.LogMessage("Debugging cancelled.", MessageImportance.Normal);
}
}
var asm = typeSystem.TargetAssemblyDefinition;
var emres = new EmbeddedResources(asm);
var avares = new AvaloniaResources(asm, projectDirectory);

6
src/Avalonia.Controls/Application.cs

@ -104,7 +104,7 @@ namespace Avalonia
/// <value>
/// The application's focus manager.
/// </value>
public IFocusManager FocusManager
public IFocusManager? FocusManager
{
get;
private set;
@ -116,7 +116,7 @@ namespace Avalonia
/// <value>
/// The application's input manager.
/// </value>
public InputManager InputManager
public InputManager? InputManager
{
get;
private set;
@ -175,7 +175,7 @@ namespace Avalonia
/// - <see cref="ISingleViewApplicationLifetime"/>
/// - <see cref="IControlledApplicationLifetime"/>
/// </summary>
public IApplicationLifetime ApplicationLifetime { get; set; }
public IApplicationLifetime? ApplicationLifetime { get; set; }
event Action<IReadOnlyList<IStyle>> IGlobalStyles.GlobalStylesAdded
{

14
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
@ -122,12 +123,23 @@ namespace Avalonia.Controls.ApplicationLifetimes
lifetimeEvents.ShutdownRequested += OnShutdownRequested;
_cts = new CancellationTokenSource();
MainWindow?.Show();
// Note due to a bug in the JIT we wrap this in a method, otherwise MainWindow
// gets stuffed into a local var and can not be GCed until after the program stops.
// this method never exits until program end.
ShowMainWindow();
Dispatcher.UIThread.MainLoop(_cts.Token);
Environment.ExitCode = _exitCode;
return _exitCode;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void ShowMainWindow()
{
MainWindow?.Show();
}
public void Dispose()
{
if (_activeLifetime == this)

6
src/Avalonia.Controls/Flyouts/FlyoutBase.cs

@ -562,8 +562,12 @@ namespace Avalonia.Controls.Primitives
return eventArgs.Cancel;
}
internal static void SetPresenterClasses(IControl presenter, Classes classes)
internal static void SetPresenterClasses(IControl? presenter, Classes classes)
{
if(presenter is null)
{
return;
}
//Remove any classes no longer in use, ignoring pseudo classes
for (int i = presenter.Classes.Count - 1; i >= 0; i--)
{

8
src/Avalonia.Controls/Primitives/Popup.cs

@ -93,8 +93,8 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<bool> OverlayDismissEventPassThroughProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(OverlayDismissEventPassThrough));
public static readonly DirectProperty<Popup, IInputElement> OverlayInputPassThroughElementProperty =
AvaloniaProperty.RegisterDirect<Popup, IInputElement>(
public static readonly DirectProperty<Popup, IInputElement?> OverlayInputPassThroughElementProperty =
AvaloniaProperty.RegisterDirect<Popup, IInputElement?>(
nameof(OverlayInputPassThroughElement),
o => o.OverlayInputPassThroughElement,
(o, v) => o.OverlayInputPassThroughElement = v);
@ -138,7 +138,7 @@ namespace Avalonia.Controls.Primitives
private bool _isOpen;
private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState;
private IInputElement _overlayInputPassThroughElement;
private IInputElement? _overlayInputPassThroughElement;
private Action<IPopupHost?>? _popupHostChangedHandler;
/// <summary>
@ -310,7 +310,7 @@ namespace Avalonia.Controls.Primitives
/// Gets or sets an element that should receive pointer input events even when underneath
/// the popup's overlay.
/// </summary>
public IInputElement OverlayInputPassThroughElement
public IInputElement? OverlayInputPassThroughElement
{
get => _overlayInputPassThroughElement;
set => SetAndRaise(OverlayInputPassThroughElementProperty, ref _overlayInputPassThroughElement, value);

10
src/Avalonia.Controls/SplitView.cs

@ -129,14 +129,14 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Pane"/> property
/// </summary>
public static readonly StyledProperty<object?> PaneProperty =
AvaloniaProperty.Register<SplitView, object?>(nameof(Pane));
public static readonly StyledProperty<object> PaneProperty =
AvaloniaProperty.Register<SplitView, object>(nameof(Pane));
/// <summary>
/// Defines the <see cref="PaneTemplate"/> property.
/// </summary>
public static readonly StyledProperty<IDataTemplate?> PaneTemplateProperty =
AvaloniaProperty.Register<HeaderedContentControl, IDataTemplate?>(nameof(PaneTemplate));
public static readonly StyledProperty<IDataTemplate> PaneTemplateProperty =
AvaloniaProperty.Register<HeaderedContentControl, IDataTemplate>(nameof(PaneTemplate));
/// <summary>
/// Defines the <see cref="UseLightDismissOverlayMode"/> property
@ -267,7 +267,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the data template used to display the header content of the control.
/// </summary>
public IDataTemplate? PaneTemplate
public IDataTemplate PaneTemplate
{
get => GetValue(PaneTemplateProperty);
set => SetValue(PaneTemplateProperty, value);

2
src/Avalonia.Controls/TrayIcon.cs

@ -140,7 +140,7 @@ namespace Avalonia.Controls
/// Gets or sets the parameter to pass to the <see cref="Command"/> property of a
/// <see cref="TrayIcon"/>.
/// </summary>
public object CommandParameter
public object? CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }

6
src/Avalonia.Controls/WindowBase.cs

@ -193,6 +193,12 @@ namespace Avalonia.Controls
try
{
IsVisible = false;
if (this is IFocusScope scope)
{
FocusManager.Instance?.RemoveFocusScope(scope);
}
base.HandleClosed();
}
finally

3
src/Avalonia.Input/ApiCompatBaseline.txt

@ -3,6 +3,7 @@ MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Inp
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.Gestures.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.IFocusManager.RemoveFocusScope(Avalonia.Input.IFocusScope)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Interactivity.RoutedEventArgs> Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
@ -10,4 +11,4 @@ MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(Sy
MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler<Avalonia.Interactivity.RoutedEventArgs>)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract.
Total Issues: 11
Total Issues: 12

16
src/Avalonia.Input/FocusManager.cs

@ -162,6 +162,22 @@ namespace Avalonia.Input
Focus(e);
}
public void RemoveFocusScope(IFocusScope scope)
{
scope = scope ?? throw new ArgumentNullException(nameof(scope));
if (_focusScopes.TryGetValue(scope, out _))
{
SetFocusedElement(scope, null);
_focusScopes.Remove(scope);
}
if (Scope == scope)
{
Scope = null;
}
}
public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope;
/// <summary>

6
src/Avalonia.Input/ICommandSource.cs

@ -1,5 +1,5 @@
using System.Windows.Input;
#nullable enable
namespace Avalonia.Input
{
///<summary>
@ -12,13 +12,13 @@ namespace Avalonia.Input
/// 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; }
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; }
object? CommandParameter { get; }
/// <summary>

8
src/Avalonia.Input/IFocusManager.cs

@ -35,5 +35,13 @@ namespace Avalonia.Input
/// when it activates, e.g. when a Window is activated.
/// </remarks>
void SetFocusScope(IFocusScope scope);
/// <summary>
/// Notifies the focus manager that a focus scope has been removed.
/// </summary>
/// <param name="scope">The focus scope to be removed.</param>
/// This should not be called by client code. It is called by an <see cref="IFocusScope"/>
/// when it deactivates or closes, e.g. when a Window is closed.
void RemoveFocusScope(IFocusScope scope);
}
}

6
src/Avalonia.Styling/Styling/PropertySetterInstance.cs

@ -16,14 +16,14 @@ namespace Avalonia.Styling
private readonly IStyleable _target;
private readonly StyledPropertyBase<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty;
private readonly T _value;
private readonly T? _value;
private IDisposable? _subscription;
private bool _isActive;
public PropertySetterInstance(
IStyleable target,
StyledPropertyBase<T> property,
T value)
T? value)
{
_target = target;
_styledProperty = property;
@ -57,7 +57,7 @@ namespace Avalonia.Styling
{
if (_styledProperty is object)
{
_subscription = _target.SetValue(_styledProperty, _value, BindingPriority.Style);
_subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style);
}
else
{

4
src/Avalonia.Styling/Styling/Setter.cs

@ -101,7 +101,7 @@ namespace Avalonia.Styling
data.result = new PropertySetterInstance<T>(
data.target,
property,
(T)data.value);
(T?)data.value);
}
}
@ -128,7 +128,7 @@ namespace Avalonia.Styling
data.result = new PropertySetterInstance<T>(
data.target,
property,
(T)data.value);
(T)data.value!);
}
}

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

@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.Converters;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
@ -33,6 +34,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
_ => null,
};
if (provideTarget.TargetObject is Setter setter)
{
targetType = setter.Property.PropertyType;
}
// Look upwards though the ambient context for IResourceHosts and IResourceProviders
// which might be able to give us the resource.
foreach (var e in stack.Parents)

1
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -193,7 +193,6 @@ namespace Avalonia.Win32
case WindowsMessage.WM_MBUTTONUP:
case WindowsMessage.WM_XBUTTONUP:
{
shouldTakeFocus = ShouldTakeFocusOnClick;
if (ShouldIgnoreTouchEmulatedMessage())
{
break;

2
src/tools/MicroComGenerator/CSharpGen.Utils.cs

@ -40,7 +40,7 @@ namespace MicroComGenerator
SyntaxToken Semicolon() => Token(SyntaxKind.SemicolonToken);
static VariableDeclarationSyntax DeclareVar(string type, string name,
ExpressionSyntax? initializer = null)
ExpressionSyntax initializer = null)
=> VariableDeclaration(ParseTypeName(type),
SingletonSeparatedList(VariableDeclarator(name)
.WithInitializer(initializer == null ? null : EqualsValueClause(initializer))));

27
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

@ -512,6 +512,33 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
var brush = (ISolidColorBrush)border.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
[Fact]
public void Automatically_Converts_Color_To_SolidColorBrush_From_Setter()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<Color x:Key='color'>#ff506070</Color>
</Window.Resources>
<Window.Styles>
<Style Selector='Button'>
<Setter Property='Background' Value='{StaticResource color}'/>
</Style>
</Window.Styles>
<Button Name='button'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button");
var brush = (ISolidColorBrush)button.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
}
private IDisposable StyledWindow(params (string, string)[] assets)
{

Loading…
Cancel
Save