Browse Source

Merge remote-tracking branch 'origin/master' into Stylus

# Conflicts:
#	src/Avalonia.Base/Input/IPointer.cs
#	src/Windows/Avalonia.Win32/WindowImpl.cs
Stylus
Max Katz 4 years ago
parent
commit
6f78ca5abf
  1. 1519
      Avalonia.sln
  2. 1
      Directory.Build.props
  3. 2
      build/ApiCompatAttributeExcludeList.txt
  4. 5
      build/AvaloniaPublicKey.props
  5. 3
      build/CoreLibraries.props
  6. 6
      build/HarfBuzzSharp.props
  7. 2
      build/JetBrains.dotMemoryUnit.props
  8. 1
      build/NetFX.props
  9. 6
      build/SkiaSharp.props
  10. 2
      build/SourceLink.props
  11. 2
      native/Avalonia.Native/src/OSX/AutoFitContentView.mm
  12. 2
      native/Avalonia.Native/src/OSX/AvnPanelWindow.mm
  13. 4
      native/Avalonia.Native/src/OSX/AvnView.mm
  14. 119
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  15. 2
      native/Avalonia.Native/src/OSX/INSWindowHolder.h
  16. 23
      native/Avalonia.Native/src/OSX/PopupImpl.mm
  17. 47
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  18. 125
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  19. 12
      native/Avalonia.Native/src/OSX/WindowImpl.h
  20. 145
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  21. 5
      native/Avalonia.Native/src/OSX/WindowProtocol.h
  22. 11
      native/Avalonia.Native/src/OSX/app.mm
  23. 1
      nukebuild/Build.cs
  24. 3
      nukebuild/_build.csproj
  25. 15
      readme.md
  26. 1
      samples/BindingDemo/BindingDemo.csproj
  27. 7
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  28. 35
      samples/ControlCatalog.Android/EmbedSample.Android.cs
  29. 6
      samples/ControlCatalog.Android/MainActivity.cs
  30. 14
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  31. 35
      samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs
  32. 58
      samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs
  33. 0
      samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md
  34. 0
      samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4
  35. 29
      samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs
  36. 38
      samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs
  37. 45
      samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs
  38. 73
      samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs
  39. 8
      samples/ControlCatalog.NetCore/Program.cs
  40. 28
      samples/ControlCatalog.NetCore/app.manifest
  41. 4
      samples/ControlCatalog.Web/App.razor.cs
  42. 34
      samples/ControlCatalog.Web/EmbedSample.Browser.cs
  43. 70
      samples/ControlCatalog.Web/Shared/MainLayout.razor.css
  44. 44
      samples/ControlCatalog.Web/wwwroot/css/app.css
  45. 11
      samples/ControlCatalog.Web/wwwroot/js/app.js
  46. 9
      samples/ControlCatalog.iOS/AppDelegate.cs
  47. 38
      samples/ControlCatalog.iOS/EmbedSample.iOS.cs
  48. 20
      samples/ControlCatalog/ControlCatalog.csproj
  49. 10
      samples/ControlCatalog/Converter/MathSubtractConverter.cs
  50. 10
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  51. 7
      samples/ControlCatalog/MainView.xaml
  52. 16
      samples/ControlCatalog/MainView.xaml.cs
  53. 8
      samples/ControlCatalog/MainWindow.xaml.cs
  54. 4
      samples/ControlCatalog/Models/Countries.cs
  55. 4
      samples/ControlCatalog/Models/GDPValueConverter.cs
  56. 16
      samples/ControlCatalog/Models/Person.cs
  57. 33
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  58. 2
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  59. 4
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  60. 3
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
  61. 12
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
  62. 4
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  63. 1
      samples/ControlCatalog/Pages/CarouselPage.xaml
  64. 14
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  65. 22
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  66. 6
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  67. 8
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  68. 4
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
  69. 2
      samples/ControlCatalog/Pages/DialogsPage.xaml
  70. 65
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  71. 6
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  72. 8
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  73. 13
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  74. 26
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  75. 2
      samples/ControlCatalog/Pages/LabelsPage.axaml.cs
  76. 2
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  77. 68
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml
  78. 86
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
  79. 2
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  80. 10
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  81. 2
      samples/ControlCatalog/Pages/ScreenPage.cs
  82. 12
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  83. 2
      samples/ControlCatalog/Pages/TabStripPage.xaml.cs
  84. 6
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  85. 2
      samples/ControlCatalog/ViewModels/ApplicationViewModel.cs
  86. 2
      samples/ControlCatalog/ViewModels/ContextPageViewModel.cs
  87. 2
      samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
  88. 19
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  89. 13
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  90. 8
      samples/ControlCatalog/ViewModels/MenuItemViewModel.cs
  91. 2
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  92. 4
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  93. 16
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  94. 4
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  95. 3
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  96. 1
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  97. 3
      samples/Previewer/Previewer.csproj
  98. 3
      samples/RenderDemo/MainWindow.xaml
  99. 12
      samples/RenderDemo/Pages/GlyphRunPage.xaml
  100. 113
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

1519
Avalonia.sln

File diff suppressed because it is too large

1
Directory.Build.props

@ -1,4 +1,5 @@
<Project>
<Import Project="$(MSBuildThisFileDirectory)/build/AvaloniaPublicKey.props"/>
<PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>

2
build/ApiCompatAttributeExcludeList.txt

@ -0,0 +1,2 @@
T:Avalonia.Metadata.NotClientImplementableAttribute
T:Avalonia.Metadata.UnstableAttribute

5
build/AvaloniaPublicKey.props

@ -0,0 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AvaloniaPublicKey>0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87</AvaloniaPublicKey>
</PropertyGroup>
</Project>

3
build/CoreLibraries.props

@ -3,13 +3,10 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Base/Avalonia.Base.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Controls/Avalonia.Controls.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.OpenGL/Avalonia.OpenGL.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Dialogs/Avalonia.Dialogs.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.MicroCom/Avalonia.MicroCom.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj" />
</ItemGroup>
</Project>

6
build/HarfBuzzSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.254" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.254" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.254"/>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2" />
</ItemGroup>
</Project>

2
build/JetBrains.dotMemoryUnit.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="JetBrains.DotMemoryUnit" Version="3.1.20200127.214830" />
<PackageReference Include="JetBrains.DotMemoryUnit" Version="3.2.20220510" />
</ItemGroup>
</Project>

1
build/NetFX.props

@ -1,7 +1,6 @@
<Project>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
</Project>

6
build/SkiaSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.254" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.254" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.254"/>
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.1" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.1" />
</ItemGroup>
</Project>

2
build/SourceLink.props

@ -19,7 +19,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
</ItemGroup>
<!-- Workaround for https://github.com/dotnet/sdk/issues/11105 -->

2
native/Avalonia.Native/src/OSX/AutoFitContentView.mm

@ -85,7 +85,7 @@
_settingSize = true;
[super setFrameSize:newSize];
auto window = static_cast<id <AvnWindowProtocol>>([self window]);
auto window = (id <AvnWindowProtocol>) [self window];
// TODO get actual titlebar size

2
native/Avalonia.Native/src/OSX/AvnPanelWindow.mm

@ -3,8 +3,6 @@
// Copyright (c) 2022 Avalonia. All rights reserved.
//
#pragma once
#define IS_NSPANEL
#include "AvnWindow.mm"

4
native/Avalonia.Native/src/OSX/AvnView.mm

@ -222,7 +222,7 @@
- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
{
bool triggerInputWhenDisabled = type != Move;
bool triggerInputWhenDisabled = type != Move && type != LeaveWindow;
if([self ignoreUserInput: triggerInputWhenDisabled])
{
@ -709,4 +709,4 @@
return [[self accessibilityChild] accessibilityFocusedUIElement];
}
@end
@end

119
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -31,7 +31,9 @@
ComPtr<WindowBaseImpl> _parent;
bool _closed;
bool _isEnabled;
bool _canBecomeKeyWindow;
bool _isExtended;
bool _isTransitioningToFullScreen;
AvnMenu* _menu;
}
@ -67,7 +69,7 @@
}
}
- (void)performClose:(id)sender
- (void)performClose:(id _Nullable )sender
{
if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
{
@ -146,7 +148,7 @@
}
}
-(void) applyMenu:(AvnMenu *)menu
-(void) applyMenu:(AvnMenu *_Nullable)menu
{
if(menu == nullptr)
{
@ -156,7 +158,7 @@
_menu = menu;
}
-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
-(CLASS_NAME*_Nonnull) initWithParent: (WindowBaseImpl*_Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
{
// https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/
// create nswindow with specific contentRect, otherwise we wont be able to resize the window
@ -174,10 +176,17 @@
[self setBackgroundColor: [NSColor clearColor]];
_isExtended = false;
_isTransitioningToFullScreen = false;
if(self.isDialog)
{
[self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary];
}
return self;
}
- (BOOL)windowShouldClose:(NSWindow *)sender
- (BOOL)windowShouldClose:(NSWindow *_Nonnull)sender
{
auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
@ -189,19 +198,28 @@
return true;
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification
- (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification
{
[self backingScaleFactor];
}
- (void)windowWillClose:(NSNotification *)notification
- (void)windowWillClose:(NSNotification *_Nonnull)notification
{
_closed = true;
if(_parent)
{
ComPtr<WindowBaseImpl> parent = _parent;
_parent = NULL;
[self restoreParentWindow];
auto window = dynamic_cast<WindowImpl*>(parent.getRaw());
if(window != nullptr)
{
window->SetParent(nullptr);
}
parent->BaseEvents->Closed();
[parent->View onClosed];
}
@ -209,26 +227,32 @@
-(BOOL)canBecomeKeyWindow
{
// If the window has a child window being shown as a dialog then don't allow it to become the key window.
for(NSWindow* uch in [self childWindows])
if(_canBecomeKeyWindow)
{
auto ch = static_cast<id <AvnWindowProtocol>>(uch);
if(ch == nil)
continue;
if (ch.isDialog)
return false;
}
// If the window has a child window being shown as a dialog then don't allow it to become the key window.
auto parent = dynamic_cast<WindowImpl*>(_parent.getRaw());
if(parent != nullptr)
{
return parent->CanBecomeKeyWindow();
}
return true;
return true;
}
return false;
}
#ifndef IS_NSPANEL
-(BOOL)canBecomeMainWindow
{
#ifdef IS_NSPANEL
return false;
#else
return true;
}
#endif
-(void)setCanBecomeKeyWindow:(bool)value
{
_canBecomeKeyWindow = value;
}
-(bool)shouldTryToHandleEvents
@ -239,6 +263,10 @@
-(void) setEnabled:(bool)enable
{
_isEnabled = enable;
[[self standardWindowButton:NSWindowCloseButton] setEnabled:enable];
[[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:enable];
[[self standardWindowButton:NSWindowZoomButton] setEnabled:enable];
}
-(void)becomeKeyWindow
@ -253,17 +281,20 @@
[super becomeKeyWindow];
}
-(void) restoreParentWindow;
- (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification
{
auto parent = [self parentWindow];
if(parent != nil)
{
[parent removeChildWindow:self];
}
_parent->BringToFront();
dispatch_async(dispatch_get_main_queue(), ^{
@try {
[self invalidateShadow];
}
@finally{
}
});
}
- (void)windowDidMiniaturize:(NSNotification *)notification
- (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -273,7 +304,7 @@
}
}
- (void)windowDidDeminiaturize:(NSNotification *)notification
- (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -283,7 +314,7 @@
}
}
- (void)windowDidResize:(NSNotification *)notification
- (void)windowDidResize:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -293,7 +324,7 @@
}
}
- (void)windowWillExitFullScreen:(NSNotification *)notification
- (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -303,7 +334,7 @@
}
}
- (void)windowDidExitFullScreen:(NSNotification *)notification
- (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -326,8 +357,9 @@
}
}
- (void)windowWillEnterFullScreen:(NSNotification *)notification
- (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification
{
_isTransitioningToFullScreen = true;
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
@ -336,8 +368,9 @@
}
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification
- (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification
{
_isTransitioningToFullScreen = false;
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
@ -347,7 +380,7 @@
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame
{
return true;
}
@ -358,11 +391,13 @@
_parent->BaseEvents->Deactivated();
[self showAppMenuOnly];
[self invalidateShadow];
[super resignKeyWindow];
}
- (void)windowDidMove:(NSNotification *)notification
- (void)windowDidMove:(NSNotification *_Nonnull)notification
{
AvnPoint position;
@ -372,6 +407,11 @@
if(cparent != nullptr)
{
if(!cparent->IsShown())
{
return;
}
if(cparent->WindowState() == Maximized)
{
cparent->SetWindowState(Normal);
@ -389,7 +429,7 @@
return pt;
}
- (void)sendEvent:(NSEvent *)event
- (void)sendEvent:(NSEvent *_Nonnull)event
{
[super sendEvent:event];
@ -412,8 +452,13 @@
_parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
}
if(!_isTransitioningToFullScreen)
{
_parent->BringToFront();
}
}
break;
break;
case NSEventTypeMouseEntered:
{

2
native/Avalonia.Native/src/OSX/INSWindowHolder.h

@ -11,7 +11,7 @@
struct INSWindowHolder
{
virtual NSWindow* _Nonnull GetNSWindow () = 0;
virtual NSView* _Nonnull GetNSView () = 0;
virtual AvnView* _Nonnull GetNSView () = 0;
};
#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H

23
native/Avalonia.Native/src/OSX/PopupImpl.mm

@ -34,27 +34,16 @@ protected:
return NSWindowStyleMaskBorderless;
}
virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
{
START_COM_CALL;
@autoreleasepool
{
if (Window != nullptr)
{
[Window setContentSize:NSSize{x, y}];
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
}
return S_OK;
}
}
public:
virtual bool ShouldTakeFocusOnShow() override
{
return false;
}
virtual HRESULT Show(bool activate, bool isDialog) override
{
return WindowBaseImpl::Show(activate, true);
}
};
@ -65,4 +54,4 @@ extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events, gl));
return ptr;
}
}
}

47
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@ -16,8 +16,6 @@
class WindowBaseImpl : public virtual ComObject,
public virtual IAvnWindowBase,
public INSWindowHolder {
private:
NSCursor *cursor;
public:
FORWARD_IUNKNOWN()
@ -28,23 +26,7 @@ BEGIN_INTERFACE_MAP()
virtual ~WindowBaseImpl();
AutoFitContentView *StandardContainer;
AvnView *View;
NSWindow * Window;
ComPtr<IAvnWindowBaseEvents> BaseEvents;
ComPtr<IAvnGlContext> _glContext;
NSObject <IRenderTarget> *renderTarget;
AvnPoint lastPositionSet;
NSSize lastSize;
NSSize lastMinSize;
NSSize lastMaxSize;
AvnMenu* lastMenu;
NSString *_lastTitle;
bool _shown;
bool _inResize;
WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl);
WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel = false);
virtual HRESULT ObtainNSWindowHandle(void **ret) override;
@ -56,10 +38,12 @@ BEGIN_INTERFACE_MAP()
virtual NSWindow *GetNSWindow() override;
virtual NSView *GetNSView() override;
virtual AvnView *GetNSView() override;
virtual HRESULT Show(bool activate, bool isDialog) override;
virtual bool IsShown ();
virtual bool ShouldTakeFocusOnShow();
virtual HRESULT Hide() override;
@ -115,6 +99,8 @@ BEGIN_INTERFACE_MAP()
virtual bool IsDialog();
id<AvnWindowProtocol> GetWindowProtocol ();
virtual void BringToFront ();
protected:
virtual NSWindowStyleMask GetStyle();
@ -124,7 +110,26 @@ protected:
private:
void CreateNSWindow (bool isDialog);
void CleanNSWindow ();
void InitialiseNSWindow ();
NSCursor *cursor;
ComPtr<IAvnGlContext> _glContext;
bool hasPosition;
NSSize lastSize;
NSSize lastMinSize;
NSSize lastMaxSize;
AvnMenu* lastMenu;
bool _inResize;
protected:
AvnPoint lastPositionSet;
AutoFitContentView *StandardContainer;
bool _shown;
public:
NSObject <IRenderTarget> *renderTarget;
NSWindow * Window;
ComPtr<IAvnWindowBaseEvents> BaseEvents;
AvnView *View;
};
#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H

125
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -21,7 +21,7 @@ WindowBaseImpl::~WindowBaseImpl() {
Window = nullptr;
}
WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) {
WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel) {
_shown = false;
_inResize = false;
BaseEvents = events;
@ -30,15 +30,25 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl)
View = [[AvnView alloc] initWithParent:this];
StandardContainer = [[AutoFitContentView new] initWithContent:View];
lastPositionSet.X = 100;
lastPositionSet.Y = 100;
lastPositionSet = { 0, 0 };
hasPosition = false;
lastSize = NSSize { 100, 100 };
lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
lastMinSize = NSSize { 0, 0 };
_lastTitle = @"";
Window = nullptr;
lastMenu = nullptr;
CreateNSWindow(usePanel);
[Window setContentView:StandardContainer];
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered];
[Window setContentMinSize:lastMinSize];
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
[Window setHasShadow:true];
}
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
@ -69,7 +79,7 @@ NSWindow *WindowBaseImpl::GetNSWindow() {
return Window;
}
NSView *WindowBaseImpl::GetNSView() {
AvnView *WindowBaseImpl::GetNSView() {
return View;
}
@ -89,13 +99,19 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
START_COM_CALL;
@autoreleasepool {
CreateNSWindow(isDialog);
InitialiseNSWindow();
[Window setContentSize:lastSize];
if(hasPosition)
{
SetPosition(lastPositionSet);
} else
{
[Window center];
}
SetPosition(lastPositionSet);
UpdateStyle();
[Window setTitle:_lastTitle];
[Window invalidateShadow];
if (ShouldTakeFocusOnShow() && activate) {
[Window orderFront:Window];
@ -112,6 +128,11 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
}
}
bool WindowBaseImpl::IsShown ()
{
return _shown;
}
bool WindowBaseImpl::ShouldTakeFocusOnShow() {
return true;
}
@ -134,8 +155,6 @@ HRESULT WindowBaseImpl::Hide() {
@autoreleasepool {
if (Window != nullptr) {
[Window orderOut:Window];
[GetWindowProtocol() restoreParentWindow];
}
return S_OK;
@ -191,9 +210,8 @@ HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) {
if (ret == nullptr)
return E_POINTER;
auto frame = [View frame];
ret->Width = frame.size.width;
ret->Height = frame.size.height;
ret->Width = lastSize.width;
ret->Height = lastSize.height;
return S_OK;
}
@ -206,9 +224,11 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
if (ret == nullptr)
return E_POINTER;
auto frame = [Window frame];
ret->Width = frame.size.width;
ret->Height = frame.size.height;
if(Window != nullptr){
auto frame = [Window frame];
ret->Width = frame.size.width;
ret->Height = frame.size.height;
}
return S_OK;
}
@ -278,14 +298,15 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
}
@try {
if (!_shown) {
BaseEvents->Resized(AvnSize{x, y}, reason);
}
lastSize = NSSize {x, y};
if(Window != nullptr) {
[Window setContentSize:lastSize];
if(x != lastSize.width || y != lastSize.height) {
lastSize = NSSize{x, y};
if (!_shown) {
BaseEvents->Resized(AvnSize{x, y}, reason);
} else if (Window != nullptr) {
[Window setContentSize:lastSize];
[Window invalidateShadow];
}
}
}
@finally {
@ -354,12 +375,17 @@ HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) {
return E_POINTER;
}
auto frame = [Window frame];
if(Window != nullptr) {
auto frame = [Window frame];
ret->X = frame.origin.x;
ret->Y = frame.origin.y + frame.size.height;
ret->X = frame.origin.x;
ret->Y = frame.origin.y + frame.size.height;
*ret = ConvertPointY(*ret);
*ret = ConvertPointY(*ret);
} else
{
*ret = lastPositionSet;
}
return S_OK;
}
@ -370,7 +396,11 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) {
@autoreleasepool {
lastPositionSet = point;
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
hasPosition = true;
if(Window != nullptr) {
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
}
return S_OK;
}
@ -538,6 +568,8 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) {
CleanNSWindow();
Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
[Window setHidesOnDeactivate:false];
}
} else {
if (![Window isKindOfClass:[AvnWindow class]]) {
@ -548,35 +580,18 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) {
}
}
void WindowBaseImpl::InitialiseNSWindow() {
if(Window != nullptr) {
[Window setContentView:StandardContainer];
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered];
[Window setContentSize:lastSize];
[Window setContentMinSize:lastMinSize];
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
if (lastMenu != nullptr) {
[GetWindowProtocol() applyMenu:lastMenu];
if ([Window isKeyWindow]) {
[GetWindowProtocol() showWindowMenuWithAppMenu];
}
}
}
}
id <AvnWindowProtocol> WindowBaseImpl::GetWindowProtocol() {
if(Window == nullptr)
{
return nullptr;
}
return static_cast<id <AvnWindowProtocol>>(Window);
return (id <AvnWindowProtocol>) Window;
}
void WindowBaseImpl::BringToFront()
{
// do nothing.
}
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)

12
native/Avalonia.Native/src/OSX/WindowImpl.h

@ -8,10 +8,12 @@
#import "WindowBaseImpl.h"
#include "IWindowStateChanged.h"
#include <list>
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
{
private:
bool _isEnabled;
bool _canResize;
bool _fullScreenActive;
SystemDecorations _decorations;
@ -22,6 +24,8 @@ private:
bool _transitioningWindowState;
bool _isClientAreaExtended;
bool _isDialog;
WindowImpl* _parent;
std::list<WindowImpl*> _children;
AvnExtendClientAreaChromeHints _extendClientHints;
FORWARD_IUNKNOWN()
@ -88,9 +92,17 @@ BEGIN_INTERFACE_MAP()
virtual HRESULT SetWindowState (AvnWindowState state) override;
virtual bool IsDialog() override;
virtual void BringToFront () override;
bool CanBecomeKeyWindow ();
protected:
virtual NSWindowStyleMask GetStyle() override;
private:
void OnInitialiseNSWindow();
NSString *_lastTitle;
};
#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H

145
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -10,6 +10,8 @@
#include "WindowProtocol.h"
WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) {
_isEnabled = true;
_children = std::list<WindowImpl*>();
_isClientAreaExtended = false;
_extendClientHints = AvnDefaultChrome;
_fullScreenActive = false;
@ -19,10 +21,11 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
_inSetWindowState = false;
_lastWindowState = Normal;
_actualWindowState = Normal;
_lastTitle = @"";
_parent = nullptr;
WindowEvents = events;
[Window disableCursorRects];
[Window setTabbingMode:NSWindowTabbingModeDisallowed];
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
OnInitialiseNSWindow();
}
void WindowImpl::HideOrShowTrafficLights() {
@ -30,23 +33,27 @@ void WindowImpl::HideOrShowTrafficLights() {
return;
}
for (id subview in Window.contentView.superview.subviews) {
if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
NSView *titlebarView = [subview subviews][0];
for (id button in titlebarView.subviews) {
if ([button isKindOfClass:[NSButton class]]) {
if (_isClientAreaExtended) {
auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
[[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights];
[[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights];
[[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights];
}
[button setHidden:!wantsChrome];
} else {
[button setHidden:(_decorations != SystemDecorationsFull)];
}
void WindowImpl::OnInitialiseNSWindow(){
[GetWindowProtocol() setCanBecomeKeyWindow:true];
[Window disableCursorRects];
[Window setTabbingMode:NSWindowTabbingModeDisallowed];
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
[button setWantsLayer:true];
}
}
}
[Window setTitle:_lastTitle];
if(_isClientAreaExtended)
{
[GetWindowProtocol() setIsExtended:true];
SetExtendClientArea(true);
}
}
@ -56,19 +63,8 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) {
@autoreleasepool {
_isDialog = isDialog;
bool created = Window == nullptr;
WindowBaseImpl::Show(activate, isDialog);
if(created)
{
if(_isClientAreaExtended)
{
[GetWindowProtocol() setIsExtended:true];
SetExtendClientArea(true);
}
}
HideOrShowTrafficLights();
return SetWindowState(_lastWindowState);
@ -79,7 +75,9 @@ HRESULT WindowImpl::SetEnabled(bool enable) {
START_COM_CALL;
@autoreleasepool {
_isEnabled = enable;
[GetWindowProtocol() setEnabled:enable];
UpdateStyle();
return S_OK;
}
}
@ -88,26 +86,68 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) {
START_COM_CALL;
@autoreleasepool {
if (parent == nullptr)
return E_POINTER;
if(_parent != nullptr)
{
_parent->_children.remove(this);
_parent->BringToFront();
}
auto cparent = dynamic_cast<WindowImpl *>(parent);
if (cparent == nullptr)
return E_INVALIDARG;
// If one tries to show a child window with a minimized parent window, then the parent window will be
// restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
// state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
if (cparent->WindowState() == Minimized)
cparent->SetWindowState(Normal);
_parent = cparent;
if(_parent != nullptr && Window != nullptr){
// If one tries to show a child window with a minimized parent window, then the parent window will be
// restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
// state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
if (cparent->WindowState() == Minimized)
cparent->SetWindowState(Normal);
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
cparent->_children.push_back(this);
UpdateStyle();
}
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
return S_OK;
}
}
UpdateStyle();
void WindowImpl::BringToFront()
{
if(Window != nullptr)
{
if(IsDialog())
{
Activate();
}
else
{
[Window orderFront:nullptr];
}
[Window invalidateShadow];
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
{
(*iterator)->BringToFront();
}
}
}
return S_OK;
bool WindowImpl::CanBecomeKeyWindow()
{
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
{
if((*iterator)->IsDialog())
{
return false;
}
}
return true;
}
void WindowImpl::StartStateTransition() {
@ -430,6 +470,9 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
START_COM_CALL;
@autoreleasepool {
auto currentState = _actualWindowState;
_lastWindowState = state;
if (Window == nullptr) {
return S_OK;
}
@ -440,9 +483,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
_inSetWindowState = true;
auto currentState = _actualWindowState;
_lastWindowState = state;
if (currentState == Normal) {
_preZoomSize = [Window frame];
}
@ -521,7 +561,12 @@ bool WindowImpl::IsDialog() {
}
NSWindowStyleMask WindowImpl::GetStyle() {
unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless;
unsigned long s = NSWindowStyleMaskBorderless;
if(_actualWindowState == FullScreen)
{
s |= NSWindowStyleMaskFullScreen;
}
switch (_decorations) {
case SystemDecorationsNone:
@ -533,15 +578,15 @@ NSWindowStyleMask WindowImpl::GetStyle() {
break;
case SystemDecorationsFull:
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless;
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
if (_canResize) {
if (_canResize && _isEnabled) {
s = s | NSWindowStyleMaskResizable;
}
break;
}
if ([Window parentWindow] == nullptr) {
if (!IsDialog()) {
s |= NSWindowStyleMaskMiniaturizable;
}

5
native/Avalonia.Native/src/OSX/WindowProtocol.h

@ -11,7 +11,6 @@
@protocol AvnWindowProtocol
-(void) pollModalSession: (NSModalSession _Nonnull) session;
-(void) restoreParentWindow;
-(bool) shouldTryToHandleEvents;
-(void) setEnabled: (bool) enable;
-(void) showAppMenuOnly;
@ -22,4 +21,6 @@
-(void) setIsExtended:(bool)value;
-(void) disconnectParent;
-(bool) isDialog;
@end
-(void) setCanBecomeKeyWindow:(bool)value;
@end

11
native/Avalonia.Native/src/OSX/app.mm

@ -82,6 +82,17 @@ ComPtr<IAvnApplicationEvents> _events;
_isHandlingSendEvent = oldHandling;
}
}
// This is needed for certain embedded controls DO NOT REMOVE..
- (BOOL) isHandlingSendEvent
{
return _isHandlingSendEvent;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent
{
_isHandlingSendEvent = handlingSendEvent;
}
@end
extern void InitializeAvnApp(IAvnApplicationEvents* events)

1
nukebuild/Build.cs

@ -221,7 +221,6 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
RunCoreTest("Avalonia.PlatformSupport.UnitTests");
});
Target RunRenderTests => _ => _

3
nukebuild/_build.csproj

@ -8,11 +8,10 @@
<IsPackable>False</IsPackable>
<NoWarn>CS0649;CS0169</NoWarn>
</PropertyGroup>
<Import Project="..\build\JetBrains.dotMemoryUnit.props" />
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="5.0.0" />
<PackageReference Include="xunit.runner.console" Version="2.3.1" />
<PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" />
<PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
<PackageReference Include="ILRepack.NETStandard" Version="2.0.4" />
<PackageReference Include="MicroCom.CodeGenerator" Version="0.10.4" />

15
readme.md

@ -70,11 +70,15 @@ For more information see the [.NET Foundation Code of Conduct](https://dotnetfou
Avalonia is licenced under the [MIT licence](licence.md).
## Support Avalonia
## Donate
**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx
Donating to the project is a fantastic way to thank our valued contributors for their hard work. Your donations are shared among our community and awarded for significant contributions.
If you need support see Commercial Support section below.
Donate with BTC or use [Open Collective](https://opencollective.com/avalonia).
This will be shared with the community and awarded for significant contributions.
**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx
### Backers
@ -98,6 +102,11 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
<a href="https://opencollective.com/Avalonia/sponsor/9/website" target="_blank"><img src="https://opencollective.com/Avalonia/sponsor/9/avatar.svg"></a>
<a href="https://baseheadinc.com/" target="_blank"><img height="50" src="https://baseheadinc.com/wp-content/uploads/2020/09/BH-Logo-for-Site-Header-New.png"></a>
## Commercial Support
We have a range of [support plans available](https://avaloniaui.net/support.html) for those looking to partner with the creators of Avalonia, enabling access to the best support at every step of the development process.
*Please note that donations are not considered payment for commercial support agreements. Please contact us to discuss your needs first. [team@avaloniaui.net](mailto://team@avaloniaui.net)*
## .NET Foundation
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).

1
samples/BindingDemo/BindingDemo.csproj

@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
</ItemGroup>

7
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -20,10 +20,13 @@
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<DebugSymbols>False</DebugSymbols>
<UseInterpreter>False</UseInterpreter>
<RunAOTCompilation>True</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>
<AndroidAotAdditionalArguments>no-write-symbols,nodebug</AndroidAotAdditionalArguments>
<AndroidAotMode>Hybrid</AndroidAotMode>
<AndroidGenerateJniMarshalMethods>True</AndroidGenerateJniMarshalMethods>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

35
samples/ControlCatalog.Android/EmbedSample.Android.cs

@ -0,0 +1,35 @@
using System;
using Avalonia.Platform;
using Avalonia.Android;
using ControlCatalog.Pages;
namespace ControlCatalog.Android;
public class EmbedSampleAndroid : INativeDemoControl
{
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
{
var parentContext = (parent as AndroidViewControlHandle)?.View.Context
?? global::Android.App.Application.Context;
if (isSecond)
{
var webView = new global::Android.Webkit.WebView(parentContext);
webView.LoadUrl("https://www.android.com/");
return new AndroidViewControlHandle(webView);
}
else
{
var button = new global::Android.Widget.Button(parentContext) { Text = "Hello world" };
var clickCount = 0;
button.Click += (sender, args) =>
{
clickCount++;
button.Text = $"Click count {clickCount}";
};
return new AndroidViewControlHandle(button);
}
}
}

6
samples/ControlCatalog.Android/MainActivity.cs

@ -10,7 +10,11 @@ namespace ControlCatalog.Android
{
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
return base.CustomizeAppBuilder(builder);
return base.CustomizeAppBuilder(builder)
.AfterSetup(_ =>
{
Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
});
}
}
}

14
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -4,6 +4,7 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(RunNativeAotCompilation)' == 'true'">
@ -12,6 +13,16 @@
<NativeAotCompilerVersion>7.0.0-*</NativeAotCompilerVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs" Link="NativeControls\Gtk\Gtk.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="NativeControls\Gtk\nodes.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
@ -20,6 +31,8 @@
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
<!-- For native controls test -->
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
</ItemGroup>
<ItemGroup Condition="'$(RunNativeAotCompilation)' == 'true'">
@ -32,6 +45,7 @@
<PropertyGroup>
<!-- For Microsoft.CodeAnalysis -->
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<Import Project="..\..\build\SampleApp.props" />

35
samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs

@ -0,0 +1,35 @@
using System.IO;
using System.Diagnostics;
using Avalonia.Platform;
using Avalonia.Controls.Platform;
using System;
using ControlCatalog.Pages;
namespace ControlCatalog.NetCore;
public class EmbedSampleGtk : INativeDemoControl
{
private Process _mplayer;
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
{
if (isSecond)
{
var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle);
if (chooser != null)
return chooser;
}
var control = createDefault();
var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName,
"..",
"nodes.mp4"));
_mplayer = Process.Start(new ProcessStartInfo("mplayer",
$"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"")
{
UseShellExecute = false,
});
return control;
}
}

58
samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs

@ -0,0 +1,58 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.Platform.Interop;
using Avalonia.X11.NativeDialogs;
using static Avalonia.X11.NativeDialogs.Gtk;
using static Avalonia.X11.NativeDialogs.Glib;
namespace ControlCatalog.NetCore;
internal class GtkHelper
{
private static Task<bool> s_gtkTask;
class FileChooser : INativeControlHostDestroyableControlHandle
{
private readonly IntPtr _widget;
public FileChooser(IntPtr widget, IntPtr xid)
{
_widget = widget;
Handle = xid;
}
public IntPtr Handle { get; }
public string HandleDescriptor => "XID";
public void Destroy()
{
RunOnGlibThread(() =>
{
gtk_widget_destroy(_widget);
return 0;
}).Wait();
}
}
public static INativeControlHostDestroyableControlHandle CreateGtkFileChooser(IntPtr parentXid)
{
if (s_gtkTask == null)
s_gtkTask = StartGtk();
if (!s_gtkTask.Result)
return null;
return RunOnGlibThread(() =>
{
using (var title = new Utf8Buffer("Embedded"))
{
var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder,
IntPtr.Zero);
gtk_widget_realize(widget);
var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
gtk_window_present(widget);
return new FileChooser(widget, xid);
}
}).Result;
}
}

0
samples/interop/NativeEmbedSample/nodes-license.md → samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md

0
samples/interop/NativeEmbedSample/nodes.mp4 → samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4

29
samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs

@ -0,0 +1,29 @@
using System;
using Avalonia.Platform;
using Avalonia.Threading;
using ControlCatalog.Pages;
using MonoMac.Foundation;
using MonoMac.WebKit;
namespace ControlCatalog.NetCore;
public class EmbedSampleMac : INativeDemoControl
{
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
{
// Note: We are using MonoMac for example purposes
// It shouldn't be used in production apps
MacHelper.EnsureInitialized();
var webView = new WebView();
Dispatcher.UIThread.Post(() =>
{
webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl(
isSecond ? "https://bing.com" : "https://google.com/")));
});
return new MacOSViewHandle(webView);
}
}

38
samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs

@ -0,0 +1,38 @@
using System;
using Avalonia.Controls.Platform;
using MonoMac.AppKit;
namespace ControlCatalog.NetCore;
internal class MacHelper
{
private static bool _isInitialized;
public static void EnsureInitialized()
{
if (_isInitialized)
return;
_isInitialized = true;
NSApplication.Init();
}
}
internal class MacOSViewHandle : INativeControlHostDestroyableControlHandle
{
private NSView _view;
public MacOSViewHandle(NSView view)
{
_view = view;
}
public IntPtr Handle => _view?.Handle ?? IntPtr.Zero;
public string HandleDescriptor => "NSView";
public void Destroy()
{
_view.Dispose();
_view = null;
}
}

45
samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs

@ -0,0 +1,45 @@
using System;
using System.Text;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using ControlCatalog.Pages;
namespace ControlCatalog.NetCore;
public class EmbedSampleWin : INativeDemoControl
{
private const string RichText =
@"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;}
{\*\generator Riched20 6.3.9600}\viewkind4\uc1
\pard\sa200\sl276\slmult1\f0\fs22\lang9 <PREFIX>I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par
}";
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
{
WinApi.LoadLibrary("Msftedit.dll");
var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W",
@"Rich Edit",
0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle,
IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero);
var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 };
var text = RichText.Replace("<PREFIX>", isSecond ? "\\qr " : "");
var bytes = Encoding.UTF8.GetBytes(text);
WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes);
return new Win32WindowControlHandle(handle, "HWND");
}
}
internal class Win32WindowControlHandle : PlatformHandle, INativeControlHostDestroyableControlHandle
{
public Win32WindowControlHandle(IntPtr handle, string descriptor) : base(handle, descriptor)
{
}
public void Destroy()
{
_ = WinApi.DestroyWindow(Handle);
}
}

73
samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs

@ -0,0 +1,73 @@
using System;
using System.Runtime.InteropServices;
namespace ControlCatalog.NetCore;
internal unsafe class WinApi
{
public enum CommonControls : uint
{
ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header
ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips
ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips
ICC_TAB_CLASSES = 0x00000008, // tab, tooltips
ICC_UPDOWN_CLASS = 0x00000010, // updown
ICC_PROGRESS_CLASS = 0x00000020, // progress
ICC_HOTKEY_CLASS = 0x00000040, // hotkey
ICC_ANIMATE_CLASS = 0x00000080, // animate
ICC_WIN95_CLASSES = 0x000000FF,
ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown
ICC_USEREX_CLASSES = 0x00000200, // comboex
ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control
ICC_INTERNET_CLASSES = 0x00000800,
ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller
ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control
ICC_STANDARD_CLASSES = 0x00004000,
ICC_LINK_CLASS = 0x00008000
}
[StructLayout(LayoutKind.Sequential)]
public struct INITCOMMONCONTROLSEX
{
public int dwSize;
public uint dwICC;
}
[DllImport("Comctl32.dll")]
public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool DestroyWindow(IntPtr hwnd);
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lib);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
string lpClassName,
string lpWindowName,
uint dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[StructLayout(LayoutKind.Sequential)]
public struct SETTEXTEX
{
public uint Flags;
public uint Codepage;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam);
}

8
samples/ControlCatalog.NetCore/Program.cs

@ -7,11 +7,12 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Dialogs;
using Avalonia.Headless;
using Avalonia.LogicalTree;
using Avalonia.Threading;
using ControlCatalog.Pages;
namespace ControlCatalog.NetCore
{
static class Program
@ -123,6 +124,11 @@ namespace ControlCatalog.NetCore
{
StartupScreenIndex = 1,
});
EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin()
: OperatingSystem.IsMacOS() ? new EmbedSampleMac()
: OperatingSystem.IsLinux() ? new EmbedSampleGtk()
: null;
})
.LogToTrace();

28
samples/ControlCatalog.NetCore/app.manifest

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="ControlCatalog.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

4
samples/ControlCatalog.Web/App.razor.cs

@ -7,6 +7,10 @@ public partial class App
protected override void OnParametersSet()
{
WebAppBuilder.Configure<ControlCatalog.App>()
.AfterSetup(_ =>
{
ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
})
.SetupWithSingleViewLifetime();
base.OnParametersSet();

34
samples/ControlCatalog.Web/EmbedSample.Browser.cs

@ -0,0 +1,34 @@
using System;
using Avalonia;
using Avalonia.Platform;
using Avalonia.Web.Blazor;
using ControlCatalog.Pages;
using Microsoft.JSInterop;
namespace ControlCatalog.Web;
public class EmbedSampleWeb : INativeDemoControl
{
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
{
var runtime = AvaloniaLocator.Current.GetRequiredService<IJSInProcessRuntime>();
if (isSecond)
{
var iframe = runtime.Invoke<IJSInProcessObjectReference>("document.createElement", "iframe");
iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70");
return new JSObjectControlHandle(iframe);
}
else
{
// window.createAppButton source is defined in "app.js" file.
var button = runtime.Invoke<IJSInProcessObjectReference>("window.createAppButton");
return new JSObjectControlHandle(button);
}
}
}

70
samples/ControlCatalog.Web/Shared/MainLayout.razor.css

@ -1,70 +0,0 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
.main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row:not(.auth) {
display: none;
}
.top-row.auth {
justify-content: space-between;
}
.top-row a, .top-row .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.main > div {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}

44
samples/ControlCatalog.Web/wwwroot/css/app.css

@ -44,47 +44,13 @@ a, .btn-link {
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
.canvas-container {
opacity:1;
background-color:#ccc;
position:fixed;
width:100%;
height:100%;
top:0px;
left:0px;
z-index:500;
}
canvas
{
opacity:1;
background-color:#ccc;
position:fixed;
width:100%;
height:100%;
top:0px;
left:0px;
z-index:500;
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
#app, .page {
height: 100%;
}
.overlay{
opacity:0.0;
background-color:#ccc;
position:fixed;
width:100vw;
height:100vh;
top:0px;
left:0px;
z-index:1000;
}

11
samples/ControlCatalog.Web/wwwroot/js/app.js

@ -1 +1,10 @@

window.createAppButton = function () {
var button = document.createElement('button');
button.innerText = 'Hello world';
var clickCount = 0;
button.onclick = () => {
clickCount++;
button.innerText = 'Click count ' + clickCount;
};
return button;
}

9
samples/ControlCatalog.iOS/AppDelegate.cs

@ -13,6 +13,13 @@ namespace ControlCatalog
[Register("AppDelegate")]
public partial class AppDelegate : AvaloniaAppDelegate<App>
{
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
return base.CustomizeAppBuilder(builder)
.AfterSetup(_ =>
{
Pages.EmbedSample.Implementation = new EmbedSampleIOS();
});
}
}
}

38
samples/ControlCatalog.iOS/EmbedSample.iOS.cs

@ -0,0 +1,38 @@
using System;
using Avalonia.Platform;
using CoreGraphics;
using Foundation;
using UIKit;
using WebKit;
using Avalonia.iOS;
using ControlCatalog.Pages;
namespace ControlCatalog;
public class EmbedSampleIOS : INativeDemoControl
{
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault)
{
if (isSecond)
{
var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration());
webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/")));
return new UIViewControlHandle(webView);
}
else
{
var button = new UIButton();
var clickCount = 0;
button.SetTitle("Hello world", UIControlState.Normal);
button.BackgroundColor = UIColor.Blue;
button.AddTarget((_, _) =>
{
clickCount++;
button.SetTitle($"Click count {clickCount}", UIControlState.Normal);
}, UIControlEvent.TouchDown);
return new UIViewControlHandle(button);
}
}
}

20
samples/ControlCatalog/ControlCatalog.csproj

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">
@ -13,6 +14,9 @@
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
</ItemGroup>
<ItemGroup>
<None Remove="Pages\NativeEmbedPage.xaml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" />
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-BoldItalic.ttf" />
@ -25,9 +29,23 @@
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
<ProjectReference Include="..\SampleControls\ControlSamples.csproj" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Update="Pages\NativeEmbedPage.xaml">
<Generator>MSBuild:Compile</Generator>
</AvaloniaResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Pages\NativeEmbedPage.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

10
samples/ControlCatalog/Converter/MathSubtractConverter.cs

@ -6,12 +6,16 @@ namespace ControlCatalog.Converter;
public class MathSubtractConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return (double)value - (double)parameter;
if (value is double dv && parameter is double dp)
{
return dv - dp;
}
return double.NaN;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotSupportedException();
}

10
samples/ControlCatalog/DecoratedWindow.xaml.cs

@ -15,7 +15,7 @@ namespace ControlCatalog
void SetupSide(string name, StandardCursorType cursor, WindowEdge edge)
{
var ctl = this.FindControl<Control>(name);
var ctl = this.Get<Control>(name);
ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += (i, e) =>
{
@ -26,7 +26,7 @@ namespace ControlCatalog
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.FindControl<Control>("TitleBar").PointerPressed += (i, e) =>
this.Get<Control>("TitleBar").PointerPressed += (i, e) =>
{
PlatformImpl?.BeginMoveDrag(e);
};
@ -38,12 +38,12 @@ namespace ControlCatalog
SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast);
SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest);
SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast);
this.FindControl<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
this.FindControl<Button>("MaximizeButton").Click += delegate
this.Get<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
this.Get<Button>("MaximizeButton").Click += delegate
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
};
this.FindControl<Button>("CloseButton").Click += delegate
this.Get<Button>("CloseButton").Click += delegate
{
Close();
};

7
samples/ControlCatalog/MainView.xaml

@ -2,8 +2,8 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:ControlSamples;assembly=ControlSamples"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:models="clr-namespace:ControlCatalog.Models">
xmlns:models="clr-namespace:ControlCatalog.Models"
xmlns:pages="clr-namespace:ControlCatalog.Pages">
<Grid>
<Grid.Styles>
<Style Selector="TextBlock.h2">
@ -157,6 +157,9 @@
<TabItem Header="Viewbox">
<pages:ViewboxPage />
</TabItem>
<TabItem Header="Native Embed">
<pages:NativeEmbedPage />
</TabItem>
<TabItem Header="Window Customizations">
<pages:WindowCustomizationsPage />
</TabItem>

16
samples/ControlCatalog/MainView.xaml.cs

@ -18,9 +18,9 @@ namespace ControlCatalog
{
AvaloniaXamlLoader.Load(this);
var sideBar = this.FindControl<TabControl>("Sidebar");
var sideBar = this.Get<TabControl>("Sidebar");
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsDesktop)
if (AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo().IsDesktop == true)
{
IList tabItems = ((IList)sideBar.Items);
tabItems.Add(new TabItem()
@ -36,7 +36,7 @@ namespace ControlCatalog
}
var themes = this.Find<ComboBox>("Themes");
var themes = this.Get<ComboBox>("Themes");
themes.SelectionChanged += (sender, e) =>
{
if (themes.SelectedItem is CatalogTheme theme)
@ -80,7 +80,7 @@ namespace ControlCatalog
}
};
var flowDirections = this.Find<ComboBox>("FlowDirection");
var flowDirections = this.Get<ComboBox>("FlowDirection");
flowDirections.SelectionChanged += (sender, e) =>
{
if (flowDirections.SelectedItem is FlowDirection flowDirection)
@ -89,7 +89,7 @@ namespace ControlCatalog
}
};
var decorations = this.Find<ComboBox>("Decorations");
var decorations = this.Get<ComboBox>("Decorations");
decorations.SelectionChanged += (sender, e) =>
{
if (VisualRoot is Window window
@ -99,8 +99,8 @@ namespace ControlCatalog
}
};
var transparencyLevels = this.Find<ComboBox>("TransparencyLevels");
IDisposable backgroundSetter = null, paneBackgroundSetter = null;
var transparencyLevels = this.Get<ComboBox>("TransparencyLevels");
IDisposable? backgroundSetter = null, paneBackgroundSetter = null;
transparencyLevels.SelectionChanged += (sender, e) =>
{
backgroundSetter?.Dispose();
@ -118,7 +118,7 @@ namespace ControlCatalog
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var decorations = this.Find<ComboBox>("Decorations");
var decorations = this.Get<ComboBox>("Decorations");
if (VisualRoot is Window window)
decorations.SelectedIndex = (int)window.SystemDecorations;
}

8
samples/ControlCatalog/MainWindow.xaml.cs

@ -12,7 +12,7 @@ namespace ControlCatalog
public class MainWindow : Window
{
private WindowNotificationManager _notificationArea;
private NativeMenu _recentMenu;
private NativeMenu? _recentMenu;
public MainWindow()
{
@ -28,9 +28,7 @@ namespace ControlCatalog
};
DataContext = new MainWindowViewModel(_notificationArea);
_recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu;
ExtendClientAreaChromeHints = Avalonia.Platform.ExtendClientAreaChromeHints.OSXThickTitleBar;
_recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu;
}
public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit";
@ -41,7 +39,7 @@ namespace ControlCatalog
public void OnOpenClicked(object sender, EventArgs args)
{
_recentMenu.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
_recentMenu?.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
}
public void OnCloseClicked(object sender, EventArgs args)

4
samples/ControlCatalog/Models/Countries.cs

@ -237,7 +237,7 @@ namespace ControlCatalog.Models
yield return new Country("Zimbabwe", "SUB-SAHARAN AFRICA", 12236805, 390580, 31.3, 0, 0, 67.69, 1900, 90.7, 26.8, 28.01, 21.84);
}
static IReadOnlyList<Country> _all;
static IReadOnlyList<Country>? _all;
public static IReadOnlyList<Country> All
{
@ -253,4 +253,4 @@ namespace ControlCatalog.Models
}
}
}
}

4
samples/ControlCatalog/Models/GDPValueConverter.cs

@ -14,7 +14,7 @@ namespace ControlCatalog.Models
{
public class GDPValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is int gdp)
{
@ -29,7 +29,7 @@ namespace ControlCatalog.Models
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

16
samples/ControlCatalog/Models/Person.cs

@ -13,8 +13,8 @@ namespace ControlCatalog.Models
{
public class Person : INotifyDataErrorInfo, INotifyPropertyChanged
{
string _firstName;
string _lastName;
string _firstName = string.Empty;
string _lastName = string.Empty;
bool _isBanned;
private int _age;
@ -76,7 +76,7 @@ namespace ControlCatalog.Models
Dictionary<string, List<string>> _errorLookup = new Dictionary<string, List<string>>();
void SetError(string propertyName, string error)
void SetError(string propertyName, string? error)
{
if (string.IsNullOrEmpty(error))
{
@ -88,11 +88,11 @@ namespace ControlCatalog.Models
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList))
{
errorList.Clear();
errorList.Add(error);
errorList.Add(error!);
}
else
{
var errors = new List<string> { error };
var errors = new List<string> { error! };
_errorLookup.Add(propertyName, errors);
}
@ -102,8 +102,8 @@ namespace ControlCatalog.Models
public bool HasErrors => _errorLookup.Count > 0;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
public event PropertyChangedEventHandler? PropertyChanged;
void OnErrorsChanged(string propertyName)
{
@ -114,7 +114,7 @@ namespace ControlCatalog.Models
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public IEnumerable GetErrors(string propertyName)
public IEnumerable? GetErrors(string propertyName)
{
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList))
return errorList;

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

@ -126,13 +126,13 @@ namespace ControlCatalog.Pages
binding.Bindings.Add(new Binding("Name"));
binding.Bindings.Add(new Binding("Abbreviation"));
var multibindingBox = this.FindControl<AutoCompleteBox>("MultiBindingBox");
var multibindingBox = this.Get<AutoCompleteBox>("MultiBindingBox");
multibindingBox.ValueMemberBinding = binding;
var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox");
var asyncBox = this.Get<AutoCompleteBox>("AsyncBox");
asyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.FindControl<AutoCompleteBox>("CustomAutocompleteBox");
var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox");
customAutocompleteBox.Items = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord;
@ -144,11 +144,12 @@ namespace ControlCatalog.Pages
.OfType<AutoCompleteBox>();
}
private bool StringContains(string str, string query)
private bool StringContains(string str, string? query)
{
if (query == null) return false;
return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
}
private async Task<IEnumerable<object>> PopulateAsync(string searchText, CancellationToken cancellationToken)
private async Task<IEnumerable<object>> PopulateAsync(string? searchText, CancellationToken cancellationToken)
{
await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken);
@ -157,14 +158,14 @@ namespace ControlCatalog.Pages
.ToList();
}
private bool LastWordContains(string searchText, string item)
private bool LastWordContains(string? searchText, string? item)
{
var words = searchText.Split(' ');
var words = searchText?.Split(' ') ?? Array.Empty<string>();
var options = Sentences.Select(x => x.First).ToArray();
for (var i = 0; i < words.Length; ++i)
{
var word = words[i];
for (var j = 0; j < options.Length; ++j)
for (var j = 0; word is { } && j < options.Length; ++j)
{
var option = options[j];
if (option == null)
@ -183,14 +184,18 @@ namespace ControlCatalog.Pages
return options.Any(x => x != null && x.Value == item);
}
private string AppendWord(string text, string item)
private string AppendWord(string? text, string? item)
{
string[] parts = text.Split(' ');
if (parts.Length == 0)
return item;
if (item is { })
{
string[] parts = text?.Split(' ') ?? Array.Empty<string>();
if (parts.Length == 0)
return item;
parts[parts.Length - 1] = item;
return string.Join(" ", parts);
parts[parts.Length - 1] = item;
return string.Join(" ", parts);
}
return string.Empty;
}
private void InitializeComponent()

2
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs

@ -23,7 +23,7 @@ namespace ControlCatalog.Pages
var spinner = (ButtonSpinner)sender;
var txtBox = (TextBlock)spinner.Content;
int value = Array.IndexOf(_mountains, txtBox.Text);
int value = Array.IndexOf(_mountains, txtBox?.Text);
if (e.Direction == SpinDirection.Increase)
value++;
else

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

@ -11,7 +11,7 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
this.FindControl<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
this.Get<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
}
private void InitializeComponent()
@ -22,7 +22,7 @@ namespace ControlCatalog.Pages
public void OnRepeatButtonClick(object sender, object args)
{
repeatButtonClickCount++;
var textBlock = this.FindControl<TextBlock>("RepeatButtonTextBlock");
var textBlock = this.Get<TextBlock>("RepeatButtonTextBlock");
textBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
}
}

3
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml

@ -10,8 +10,7 @@
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<StackPanel Orientation="Vertical"
Width="200">
<StackPanel Orientation="Vertical">
<TextBlock Text="SelectedDateFormat: Short"/>
<CalendarDatePicker Name="DatePicker1"
SelectedDateFormat="Short"

12
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs

@ -10,11 +10,11 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
var dp1 = this.FindControl<CalendarDatePicker>("DatePicker1");
var dp2 = this.FindControl<CalendarDatePicker>("DatePicker2");
var dp3 = this.FindControl<CalendarDatePicker>("DatePicker3");
var dp4 = this.FindControl<CalendarDatePicker>("DatePicker4");
var dp5 = this.FindControl<CalendarDatePicker>("DatePicker5");
var dp1 = this.Get<CalendarDatePicker>("DatePicker1");
var dp2 = this.Get<CalendarDatePicker>("DatePicker2");
var dp3 = this.Get<CalendarDatePicker>("DatePicker3");
var dp4 = this.Get<CalendarDatePicker>("DatePicker4");
var dp5 = this.Get<CalendarDatePicker>("DatePicker5");
dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10);
@ -23,7 +23,7 @@ namespace ControlCatalog.Pages
dp4.TemplateApplied += (s, e) =>
{
dp4.BlackoutDates.AddDatesInPast();
dp4.BlackoutDates?.AddDatesInPast();
};
}

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

@ -11,11 +11,11 @@ namespace ControlCatalog.Pages
this.InitializeComponent();
var today = DateTime.Today;
var cal1 = this.FindControl<Calendar>("DisplayDatesCalendar");
var cal1 = this.Get<Calendar>("DisplayDatesCalendar");
cal1.DisplayDateStart = today.AddDays(-25);
cal1.DisplayDateEnd = today.AddDays(25);
var cal2 = this.FindControl<Calendar>("BlackoutDatesCalendar");
var cal2 = this.Get<Calendar>("BlackoutDatesCalendar");
cal2.BlackoutDates.AddDatesInPast();
cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
}

1
samples/ControlCatalog/Pages/CarouselPage.xaml

@ -29,6 +29,7 @@
<ComboBoxItem>None</ComboBoxItem>
<ComboBoxItem>Slide</ComboBoxItem>
<ComboBoxItem>Crossfade</ComboBoxItem>
<ComboBoxItem>3D Rotation</ComboBoxItem>
</ComboBox>
</StackPanel>

14
samples/ControlCatalog/Pages/CarouselPage.xaml.cs

@ -16,6 +16,11 @@ namespace ControlCatalog.Pages
public CarouselPage()
{
this.InitializeComponent();
_carousel = this.Get<Carousel>("carousel");
_left = this.Get<Button>("left");
_right = this.Get<Button>("right");
_transition = this.Get<ComboBox>("transition");
_orientation = this.Get<ComboBox>("orientation");
_left.Click += (s, e) => _carousel.Previous();
_right.Click += (s, e) => _carousel.Next();
_transition.SelectionChanged += TransitionChanged;
@ -25,11 +30,7 @@ namespace ControlCatalog.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_carousel = this.FindControl<Carousel>("carousel");
_left = this.FindControl<Button>("left");
_right = this.FindControl<Button>("right");
_transition = this.FindControl<ComboBox>("transition");
_orientation = this.FindControl<ComboBox>("orientation");
}
private void TransitionChanged(object sender, SelectionChangedEventArgs e)
@ -45,6 +46,9 @@ namespace ControlCatalog.Pages
case 2:
_carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25));
break;
case 3:
_carousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
break;
}
}
}

22
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs

@ -18,29 +18,29 @@ namespace ControlCatalog.Pages
DataContext = new ContextPageViewModel();
_textBox = this.FindControl<TextBox>("TextBox");
_textBox = this.Get<TextBox>("TextBox");
var cutButton = this.FindControl<Button>("CutButton");
var cutButton = this.Get<Button>("CutButton");
cutButton.Click += CloseFlyout;
var copyButton = this.FindControl<Button>("CopyButton");
var copyButton = this.Get<Button>("CopyButton");
copyButton.Click += CloseFlyout;
var pasteButton = this.FindControl<Button>("PasteButton");
var pasteButton = this.Get<Button>("PasteButton");
pasteButton.Click += CloseFlyout;
var clearButton = this.FindControl<Button>("ClearButton");
var clearButton = this.Get<Button>("ClearButton");
clearButton.Click += CloseFlyout;
var customContextRequestedBorder = this.FindControl<Border>("CustomContextRequestedBorder");
var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.FindControl<Border>("CancellableContextBorder");
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextFlyout!.Closing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextFlyout!.Opening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel _model;
private ContextPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)
@ -55,7 +55,7 @@ namespace ControlCatalog.Pages
private void ContextFlyoutPage_Closing(object sender, CancelEventArgs e)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
e.Cancel = cancelCloseCheckBox.IsChecked ?? false;
e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
}
private void ContextFlyoutPage_Opening(object sender, EventArgs e)
@ -63,13 +63,13 @@ namespace ControlCatalog.Pages
if (e is CancelEventArgs cancelArgs)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox");
cancelArgs.Cancel = cancelCloseCheckBox.IsChecked ?? false;
cancelArgs.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
}
}
private void CloseFlyout(object sender, RoutedEventArgs e)
{
_textBox.ContextFlyout.Hide();
_textBox.ContextFlyout?.Hide();
}
public void CustomContextRequested(object sender, ContextRequestedEventArgs e)

6
samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs

@ -15,15 +15,15 @@ namespace ControlCatalog.Pages
this.InitializeComponent();
DataContext = new ContextPageViewModel();
var customContextRequestedBorder = this.FindControl<Border>("CustomContextRequestedBorder");
var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.FindControl<Border>("CancellableContextBorder");
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextMenu!.ContextMenuClosing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextMenu!.ContextMenuOpening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel _model;
private ContextPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)

8
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@ -21,7 +21,7 @@ namespace ControlCatalog.Pages
var dataGridSortDescription = DataGridSortDescription.FromPath(nameof(Country.Region), ListSortDirection.Ascending, new ReversedStringComparer());
var collectionView1 = new DataGridCollectionView(Countries.All);
collectionView1.SortDescriptions.Add(dataGridSortDescription);
var dg1 = this.FindControl<DataGrid>("dataGrid1");
var dg1 = this.Get<DataGrid>("dataGrid1");
dg1.IsReadOnly = true;
dg1.LoadingRow += Dg1_LoadingRow;
dg1.Sorting += (s, a) =>
@ -37,7 +37,7 @@ namespace ControlCatalog.Pages
};
dg1.Items = collectionView1;
var dg2 = this.FindControl<DataGrid>("dataGridGrouping");
var dg2 = this.Get<DataGrid>("dataGridGrouping");
dg2.IsReadOnly = true;
var collectionView2 = new DataGridCollectionView(Countries.All);
@ -45,7 +45,7 @@ namespace ControlCatalog.Pages
dg2.Items = collectionView2;
var dg3 = this.FindControl<DataGrid>("dataGridEdit");
var dg3 = this.Get<DataGrid>("dataGridEdit");
dg3.IsReadOnly = false;
var items = new List<Person>
@ -58,7 +58,7 @@ namespace ControlCatalog.Pages
dg3.Items = collectionView3;
var addButton = this.FindControl<Button>("btnAdd");
var addButton = this.Get<Button>("btnAdd");
addButton.Click += (a, b) => collectionView3.AddNew();
}

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

@ -9,12 +9,12 @@ namespace ControlCatalog.Pages
public DateTimePickerPage()
{
this.InitializeComponent();
this.FindControl<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
this.Get<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
"for example to schedule an appointment. The DatePicker displays three controls for month, day, and year. " +
"These controls are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"Order of month, day, and year is dynamically set based on user date settings";
this.FindControl<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
this.Get<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
"to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " +
"are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"12 - hour or 24 - hour clock and visiblility of AM / PM is dynamically set based on user time settings, or can be overridden.";

2
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -20,7 +20,7 @@
Text="Window dialogs" />
<Button Name="DecoratedWindow">Decorated _window</Button>
<Button Name="DecoratedWindowDialog">Decorated w_indow (dialog)</Button>
<Button Name="Dialog">_Dialog</Button>
<Button Name="Dialog" ToolTip.Tip="Shows a dialog">_Dialog</Button>
<Button Name="DialogNoTaskbar">Dialog (_No taskbar icon)</Button>
<Button Name="OwnedWindow">Own_ed window</Button>
<Button Name="OwnedWindowNoTaskbar">Owned window (No tas_kbar icon)</Button>

65
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -8,7 +8,6 @@ using Avalonia.Dialogs;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
#pragma warning disable 4014
namespace ControlCatalog.Pages
{
public class DialogsPage : UserControl
@ -17,14 +16,14 @@ namespace ControlCatalog.Pages
{
this.InitializeComponent();
var results = this.FindControl<ItemsPresenter>("PickerLastResults");
var resultsVisible = this.FindControl<TextBlock>("PickerLastResultsVisible");
var results = this.Get<ItemsPresenter>("PickerLastResults");
var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible");
string lastSelectedDirectory = null;
string? lastSelectedDirectory = null;
List<FileDialogFilter> GetFilters()
List<FileDialogFilter>? GetFilters()
{
if (this.FindControl<CheckBox>("UseFilters").IsChecked != true)
if (this.Get<CheckBox>("UseFilters").IsChecked != true)
return null;
return new List<FileDialogFilter>
{
@ -40,20 +39,24 @@ namespace ControlCatalog.Pages
};
}
this.FindControl<Button>("OpenFile").Click += async delegate
this.Get<Button>("OpenFile").Click += async delegate
{
// Almost guaranteed to exist
var fullPath = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
var initialFileName = fullPath == null ? null : System.IO.Path.GetFileName(fullPath);
var initialDirectory = fullPath == null ? null : System.IO.Path.GetDirectoryName(fullPath);
var result = await new OpenFileDialog()
{
Title = "Open file",
Filters = GetFilters(),
Directory = lastSelectedDirectory,
// Almost guaranteed to exist
InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName
Directory = initialDirectory,
InitialFileName = initialFileName
}.ShowAsync(GetWindow());
results.Items = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.FindControl<Button>("OpenMultipleFiles").Click += async delegate
this.Get<Button>("OpenMultipleFiles").Click += async delegate
{
var result = await new OpenFileDialog()
{
@ -65,7 +68,7 @@ namespace ControlCatalog.Pages
results.Items = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.FindControl<Button>("SaveFile").Click += async delegate
this.Get<Button>("SaveFile").Click += async delegate
{
var result = await new SaveFileDialog()
{
@ -77,18 +80,23 @@ namespace ControlCatalog.Pages
results.Items = new[] { result };
resultsVisible.IsVisible = result != null;
};
this.FindControl<Button>("SelectFolder").Click += async delegate
this.Get<Button>("SelectFolder").Click += async delegate
{
var result = await new OpenFolderDialog()
{
Title = "Select folder",
Directory = lastSelectedDirectory,
}.ShowAsync(GetWindow());
lastSelectedDirectory = result;
if (!string.IsNullOrEmpty(result))
{
lastSelectedDirectory = result;
}
results.Items = new [] { result };
resultsVisible.IsVisible = result != null;
};
this.FindControl<Button>("OpenBoth").Click += async delegate
this.Get<Button>("OpenBoth").Click += async delegate
{
var result = await new OpenFileDialog()
{
@ -102,35 +110,35 @@ namespace ControlCatalog.Pages
results.Items = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.FindControl<Button>("DecoratedWindow").Click += delegate
this.Get<Button>("DecoratedWindow").Click += delegate
{
new DecoratedWindow().Show();
};
this.FindControl<Button>("DecoratedWindowDialog").Click += delegate
this.Get<Button>("DecoratedWindowDialog").Click += delegate
{
new DecoratedWindow().ShowDialog(GetWindow());
};
this.FindControl<Button>("Dialog").Click += delegate
this.Get<Button>("Dialog").Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowDialog(GetWindow());
};
this.FindControl<Button>("DialogNoTaskbar").Click += delegate
this.Get<Button>("DialogNoTaskbar").Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowInTaskbar = false;
window.ShowDialog(GetWindow());
};
this.FindControl<Button>("OwnedWindow").Click += delegate
this.Get<Button>("OwnedWindow").Click += delegate
{
var window = CreateSampleWindow();
window.Show(GetWindow());
};
this.FindControl<Button>("OwnedWindowNoTaskbar").Click += delegate
this.Get<Button>("OwnedWindowNoTaskbar").Click += delegate
{
var window = CreateSampleWindow();
@ -143,6 +151,7 @@ namespace ControlCatalog.Pages
private Window CreateSampleWindow()
{
Button button;
Button dialogButton;
var window = new Window
{
@ -159,6 +168,12 @@ namespace ControlCatalog.Pages
HorizontalAlignment = HorizontalAlignment.Center,
Content = "Click to close",
IsDefault = true
}),
(dialogButton = new Button
{
HorizontalAlignment = HorizontalAlignment.Center,
Content = "Dialog",
IsDefault = false
})
}
},
@ -166,11 +181,17 @@ namespace ControlCatalog.Pages
};
button.Click += (_, __) => window.Close();
dialogButton.Click += (_, __) =>
{
var dialog = CreateSampleWindow();
dialog.Height = 200;
dialog.ShowDialog(window);
};
return window;
}
Window GetWindow() => (Window)this.VisualRoot;
Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner");
private void InitializeComponent()
{

6
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -14,7 +14,7 @@ namespace ControlCatalog.Pages
public DragAndDropPage()
{
this.InitializeComponent();
_DropState = this.Find<TextBlock>("DropState");
_DropState = this.Get<TextBlock>("DropState");
int textCount = 0;
SetupDnd("Text", d => d.Set(DataFormats.Text,
@ -26,8 +26,8 @@ namespace ControlCatalog.Pages
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
{
var dragMe = this.Find<Border>("DragMe" + suffix);
var dragState = this.Find<TextBlock>("DragState"+suffix);
var dragMe = this.Get<Border>("DragMe" + suffix);
var dragState = this.Get<TextBlock>("DragState"+suffix);
async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e)
{

8
samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs

@ -35,7 +35,7 @@ namespace ControlCatalog.Pages
private void SetXamlTexts()
{
var bfxt = this.FindControl<TextBlock>("ButtonFlyoutXamlText");
var bfxt = this.Get<TextBlock>("ButtonFlyoutXamlText");
bfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <Flyout>\n" +
@ -45,7 +45,7 @@ namespace ControlCatalog.Pages
" </Flyout>\n" +
" </Button.Flyout>\n</Button>";
var mfxt = this.FindControl<TextBlock>("MenuFlyoutXamlText");
var mfxt = this.Get<TextBlock>("MenuFlyoutXamlText");
mfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <MenuFlyout>\n" +
@ -54,7 +54,7 @@ namespace ControlCatalog.Pages
" </MenuFlyout>\n" +
" </Button.Flyout>\n</Button>";
var afxt = this.FindControl<TextBlock>("AttachedFlyoutXamlText");
var afxt = this.Get<TextBlock>("AttachedFlyoutXamlText");
afxt.Text = "<Panel Name=\"AttachedFlyoutPanel\">\n" +
" <FlyoutBase.AttachedFlyout>\n" +
" <Flyout>\n" +
@ -66,7 +66,7 @@ namespace ControlCatalog.Pages
"\n\n In DoubleTapped handler:\n" +
"FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);";
var sfxt = this.FindControl<TextBlock>("SharedFlyoutXamlText");
var sfxt = this.Get<TextBlock>("SharedFlyoutXamlText");
sfxt.Text = "Declare a flyout in Resources:\n" +
"<Window.Resources>\n" +
" <Flyout x:Key=\"SharedFlyout\">\n" +

13
samples/ControlCatalog/Pages/ImagePage.xaml.cs

@ -17,9 +17,9 @@ namespace ControlCatalog.Pages
public ImagePage()
{
InitializeComponent();
_bitmapImage = this.FindControl<Image>("bitmapImage");
_drawingImage = this.FindControl<Image>("drawingImage");
_croppedImage = this.FindControl<Image>("croppedImage");
_bitmapImage = this.Get<Image>("bitmapImage");
_drawingImage = this.Get<Image>("drawingImage");
_croppedImage = this.Get<Image>("croppedImage");
}
private void InitializeComponent()
@ -50,8 +50,11 @@ namespace ControlCatalog.Pages
if (_croppedImage != null)
{
var comboxBox = (ComboBox)sender;
var croppedBitmap = _croppedImage.Source as CroppedBitmap;
croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
if (_croppedImage.Source is CroppedBitmap croppedBitmap)
{
croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
}
}
}

26
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -24,11 +24,11 @@ namespace ControlCatalog.Pages
public ItemsRepeaterPage()
{
this.InitializeComponent();
_repeater = this.FindControl<ItemsRepeater>("repeater");
_scroller = this.FindControl<ScrollViewer>("scroller");
_scrollToLast = this.FindControl<Button>("scrollToLast");
_scrollToRandom = this.FindControl<Button>("scrollToRandom");
_scrollToSelected = this.FindControl<Button>("scrollToSelected");
_repeater = this.Get<ItemsRepeater>("repeater");
_scroller = this.Get<ScrollViewer>("scroller");
_scrollToLast = this.Get<Button>("scrollToLast");
_scrollToRandom = this.Get<Button>("scrollToRandom");
_scrollToSelected = this.Get<Button>("scrollToSelected");
_repeater.PointerPressed += RepeaterClick;
_repeater.KeyDown += RepeaterOnKeyDown;
_scrollToLast.Click += scrollToLast_Click;
@ -44,8 +44,10 @@ namespace ControlCatalog.Pages
public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
{
var item = (ItemsRepeaterPageViewModel.Item)e.DataContext;
e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
if (e.DataContext is ItemsRepeaterPageViewModel.Item item)
{
e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
}
}
private void LayoutChanged(object sender, SelectionChangedEventArgs e)
@ -115,7 +117,7 @@ namespace ControlCatalog.Pages
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
var layoutManager = ((TopLevel)VisualRoot!).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();
@ -123,9 +125,11 @@ namespace ControlCatalog.Pages
private void RepeaterClick(object sender, PointerPressedEventArgs e)
{
var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
_viewModel.SelectedItem = item;
_selectedIndex = _viewModel.Items.IndexOf(item);
if ((e.Source as TextBlock)?.DataContext is ItemsRepeaterPageViewModel.Item item)
{
_viewModel.SelectedItem = item;
_selectedIndex = _viewModel.Items.IndexOf(item);
}
}
private void RepeaterOnKeyDown(object sender, KeyEventArgs e)

2
samples/ControlCatalog/Pages/LabelsPage.axaml.cs

@ -7,7 +7,7 @@ namespace ControlCatalog.Pages
{
public class LabelsPage : UserControl
{
private Person _person;
private Person? _person;
public LabelsPage()
{

2
samples/ControlCatalog/Pages/MenuPage.xaml.cs

@ -22,7 +22,7 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
private MenuPageViewModel _model;
private MenuPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)

68
samples/ControlCatalog/Pages/NativeEmbedPage.xaml

@ -0,0 +1,68 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:ControlCatalog.Pages"
d:DesignHeight="800"
d:DesignWidth="400"
x:Class="ControlCatalog.Pages.NativeEmbedPage">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Test">
<MenuItem Header="SubMenu">
<MenuItem Header="Item 1"/>
<MenuItem Header="Item 2"/>
<MenuItem Header="Item 3"/>
</MenuItem>
<MenuItem Header="Item 1"/>
<MenuItem Header="Item 2"/>
<MenuItem Header="Item 3"/>
</MenuItem>
</Menu>
<DockPanel DockPanel.Dock="Top">
<Button DockPanel.Dock="Right" Click="ShowPopupDelay">Show popup (delay)</Button>
<Button DockPanel.Dock="Right" Click="ShowPopup">Show popup</Button>
<Border DockPanel.Dock="Right" Background="#c0c0c0">
<ToolTip.Tip>
<ToolTip>
<TextBlock>Text</TextBlock>
</ToolTip>
</ToolTip.Tip>
<TextBlock VerticalAlignment="Center">Tooltip</TextBlock>
</Border>
<TextBox Text="Lorem ipsum dolor sit amet"/>
</DockPanel>
<Grid ColumnDefinitions="*,5,*"
RowDefinitions="*,5,*">
<Grid.Styles>
<Style Selector="DockPanel#FirstPanel:not(.mobile), DockPanel#SecondPanel:not(.mobile)">
<Setter Property="Grid.RowSpan" Value="3" />
</Style>
<Style Selector="DockPanel#SecondPanel:not(.mobile)">
<Setter Property="Grid.Column" Value="2" />
</Style>
<Style Selector="DockPanel#FirstPanel.mobile, DockPanel#SecondPanel.mobile">
<Setter Property="Grid.ColumnSpan" Value="3" />
</Style>
<Style Selector="DockPanel#SecondPanel.mobile">
<Setter Property="Grid.Row" Value="2" />
</Style>
</Grid.Styles>
<DockPanel x:Name="FirstPanel">
<CheckBox x:Name="firstVisible" DockPanel.Dock="Top"
IsChecked="True" Content="Visible" />
<local:EmbedSample IsVisible="{Binding #firstVisible.IsChecked}"/>
</DockPanel>
<GridSplitter Grid.Row="0" Grid.RowSpan="3" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<GridSplitter Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" Height="5" VerticalAlignment="Stretch" />
<DockPanel x:Name="SecondPanel">
<CheckBox x:Name="secondVisible" DockPanel.Dock="Top"
IsChecked="True" Content="Visible" />
<local:EmbedSample IsSecond="True" IsVisible="{Binding #secondVisible.IsChecked}"/>
</DockPanel>
</Grid>
</DockPanel>
</UserControl>

86
samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Interactivity;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Markup.Xaml;
using Avalonia;
namespace ControlCatalog.Pages
{
public class NativeEmbedPage : UserControl
{
public NativeEmbedPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public async void ShowPopupDelay(object sender, RoutedEventArgs args)
{
await Task.Delay(3000);
ShowPopup(sender, args);
}
public void ShowPopup(object sender, RoutedEventArgs args)
{
new ContextMenu()
{
Items = new List<MenuItem>
{
new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" }
}
}.Open((Control)sender);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == BoundsProperty)
{
var isMobile = change.GetNewValue<Rect>().Width < 1200;
this.Find<DockPanel>("FirstPanel")!.Classes.Set("mobile", isMobile);
this.Find<DockPanel>("SecondPanel")!.Classes.Set("mobile", isMobile);
}
}
}
public class EmbedSample : NativeControlHost
{
public static INativeDemoControl? Implementation { get; set; }
static EmbedSample()
{
}
public bool IsSecond { get; set; }
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
return Implementation?.CreateControl(IsSecond, parent, () => base.CreateNativeControlCore(parent))
?? base.CreateNativeControlCore(parent);
}
protected override void DestroyNativeControlCore(IPlatformHandle control)
{
base.DestroyNativeControlCore(control);
}
}
public interface INativeDemoControl
{
/// <param name="isSecond">Used to specify which control should be displayed as a demo</param>
/// <param name="parent"></param>
/// <param name="createDefault"></param>
IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault);
}
}

2
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -65,7 +65,7 @@
Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Value:</TextBlock>
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center"
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding DecimalValue}" VerticalAlignment="Center"
Margin="2" HorizontalAlignment="Center"/>

10
samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs

@ -28,16 +28,16 @@ namespace ControlCatalog.Pages
public class NumbersPageViewModel : ViewModelBase
{
private IList<FormatObject> _formats;
private IList<FormatObject>? _formats;
private FormatObject _selectedFormat;
private IList<Location> _spinnerLocations;
private IList<Location>? _spinnerLocations;
private double _doubleValue;
private decimal _decimalValue;
public NumbersPageViewModel()
{
SelectedFormat = Formats.FirstOrDefault();
_selectedFormat = Formats.FirstOrDefault();
}
public double DoubleValue
@ -103,7 +103,7 @@ namespace ControlCatalog.Pages
public class FormatObject
{
public string Value { get; set; }
public string Name { get; set; }
public string? Value { get; set; }
public string? Name { get; set; }
}
}

2
samples/ControlCatalog/Pages/ScreenPage.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
Window w = (Window)VisualRoot;
Window w = (Window)VisualRoot!;
w.PositionChanged += (sender, args) => InvalidateVisual();
}

12
samples/ControlCatalog/Pages/TabControlPage.xaml.cs

@ -52,7 +52,7 @@ namespace ControlCatalog.Pages
private IBitmap LoadBitmap(string uri)
{
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
return new Bitmap(assets.Open(new Uri(uri)));
}
@ -60,7 +60,7 @@ namespace ControlCatalog.Pages
{
private Dock _tabPlacement;
public TabItemViewModel[] Tabs { get; set; }
public TabItemViewModel[]? Tabs { get; set; }
public Dock TabPlacement
{
@ -71,10 +71,10 @@ namespace ControlCatalog.Pages
private class TabItemViewModel
{
public string Header { get; set; }
public string Text { get; set; }
public IBitmap Image { get; set; }
public bool IsEnabled { get; set; } = true;
public string? Header { get; set; }
public string? Text { get; set; }
public IBitmap? Image { get; set; }
public bool IsEnabled { get; set; } = true;
}
}
}

2
samples/ControlCatalog/Pages/TabStripPage.xaml.cs

@ -38,7 +38,7 @@ namespace ControlCatalog.Pages
private class TabStripItemViewModel
{
public string Header { get; set; }
public string? Header { get; set; }
public bool IsEnabled { get; set; } = true;
}
}

6
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -66,6 +66,12 @@
FontFamily="Comic Sans MS"
InputMethod.IsInputMethodEnabled="False"
Foreground="Red"/>
<TextBox AcceptsReturn="True"
TextWrapping="Wrap"
Width="200"
Height="125"
LineHeight="32"
Text="Multiline TextBox with TextWrapping and increased LineHeight.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8" Margin="8">
<Label Classes="h2" Target="{Binding #firstResMFont}">res_m fonts</Label>

2
samples/ControlCatalog/ViewModels/ApplicationViewModel.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.ViewModels
{
ExitCommand = MiniCommand.Create(() =>
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
lifetime.Shutdown();
}

2
samples/ControlCatalog/ViewModels/ContextPageViewModel.cs

@ -9,7 +9,7 @@ namespace ControlCatalog.ViewModels
{
public class ContextPageViewModel
{
public Control View { get; set; }
public Control? View { get; set; }
public ContextPageViewModel()
{
OpenCommand = MiniCommand.CreateFromTask(Open);

2
samples/ControlCatalog/ViewModels/CursorPageViewModel.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels
.Select(x => new StandardCursorModel(x))
.ToList();
var loader = AvaloniaLocator.Current.GetService<IAssetLoader>();
var loader = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png"));
var bitmap = new Bitmap(s);
CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16));

19
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -14,7 +14,7 @@ namespace ControlCatalog.ViewModels
public ItemsRepeaterPageViewModel()
{
Items = CreateItems();
_items = CreateItems();
}
public ObservableCollection<Item> Items
@ -23,12 +23,12 @@ namespace ControlCatalog.ViewModels
set => this.RaiseAndSetIfChanged(ref _items, value);
}
public Item SelectedItem { get; set; }
public Item? SelectedItem { get; set; }
public void AddItem()
{
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" });
Items.Insert(index + 1, new Item(index + 1, $"New Item {_newItemIndex++}"));
}
public void RemoveItem()
@ -66,19 +66,20 @@ namespace ControlCatalog.ViewModels
_newGenerationIndex++;
return new ObservableCollection<Item>(
Enumerable.Range(1, 100000).Select(i => new Item(i)
{
Text = $"Item {i.ToString()} {suffix}"
}));
Enumerable.Range(1, 100000).Select(i => new Item(i, $"Item {i.ToString()} {suffix}")));
}
public class Item : ViewModelBase
{
private double _height = double.NaN;
public Item(int index) => Index = index;
public Item(int index, string text)
{
Index = index;
Text = text;
}
public int Index { get; }
public string Text { get; set; }
public string Text { get; }
public double Height
{

13
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -16,11 +16,11 @@ namespace ControlCatalog.ViewModels
private bool _isMenuItemChecked = true;
private WindowState _windowState;
private WindowState[] _windowStates;
private WindowState[] _windowStates = Array.Empty<WindowState>();
private int _transparencyLevel;
private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome;
private bool _extendClientAreaEnabled;
private bool _systemTitleBarEnabled;
private bool _systemTitleBarEnabled;
private bool _preferSystemChromeEnabled;
private double _titleBarHeight;
@ -47,14 +47,15 @@ namespace ControlCatalog.ViewModels
{
var dialog = new AboutAvaloniaDialog();
var mainWindow = (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
await dialog.ShowDialog(mainWindow);
if ((App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow is { } mainWindow)
{
await dialog.ShowDialog(mainWindow);
}
});
ExitCommand = MiniCommand.Create(() =>
{
(App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown();
(App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown();
});
ToggleMenuItemCheckedCommand = MiniCommand.Create(() =>

8
samples/ControlCatalog/ViewModels/MenuItemViewModel.cs

@ -5,9 +5,9 @@ namespace ControlCatalog.ViewModels
{
public class MenuItemViewModel
{
public string Header { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
public IList<MenuItemViewModel> Items { get; set; }
public string? Header { get; set; }
public ICommand? Command { get; set; }
public object? CommandParameter { get; set; }
public IList<MenuItemViewModel>? Items { get; set; }
}
}

2
samples/ControlCatalog/ViewModels/MenuPageViewModel.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.ViewModels
{
public class MenuPageViewModel
{
public Control View { get; set; }
public Control? View { get; set; }
public MenuPageViewModel()
{
OpenCommand = MiniCommand.CreateFromTask(Open);

4
samples/ControlCatalog/ViewModels/NotificationViewModel.cs

@ -19,8 +19,8 @@ namespace ControlCatalog.ViewModels
});
}
public string Title { get; set; }
public string Message { get; set; }
public string? Title { get; set; }
public string? Message { get; set; }
public MiniCommand YesCommand { get; }

16
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@ -19,7 +19,7 @@ namespace ControlCatalog.ViewModels
{
public TransitioningContentControlPageViewModel()
{
var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>();
var assetLoader = AvaloniaLocator.Current?.GetService<IAssetLoader>()!;
var images = new string[]
{
@ -36,8 +36,8 @@ namespace ControlCatalog.ViewModels
SetupTransitions();
SelectedTransition = PageTransitions[1];
SelectedImage = Images[0];
_SelectedTransition = PageTransitions[1];
_SelectedImage = Images[0];
}
public List<PageTransition> PageTransitions { get; } = new List<PageTransition>();
@ -45,12 +45,12 @@ namespace ControlCatalog.ViewModels
public List<Bitmap> Images { get; } = new List<Bitmap>();
private Bitmap? _SelectedImage;
private Bitmap _SelectedImage;
/// <summary>
/// Gets or Sets the selected image
/// </summary>
public Bitmap? SelectedImage
public Bitmap SelectedImage
{
get { return _SelectedImage; }
set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); }
@ -160,12 +160,12 @@ namespace ControlCatalog.ViewModels
public string DisplayTitle { get; }
private IPageTransition _Transition;
private IPageTransition? _Transition;
/// <summary>
/// Gets or sets the transition
/// </summary>
public IPageTransition Transition
public IPageTransition? Transition
{
get { return _Transition; }
set { this.RaiseAndSetIfChanged(ref _Transition, value); }
@ -201,7 +201,7 @@ namespace ControlCatalog.ViewModels
/// </summary>
public TimeSpan Duration { get; set; }
public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken)
public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{

4
samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs

@ -92,7 +92,7 @@ namespace ControlCatalog.ViewModels
public class Node
{
private ObservableCollection<Node> _children;
private ObservableCollection<Node>? _children;
private int _childIndex = 10;
public Node()
@ -106,7 +106,7 @@ namespace ControlCatalog.ViewModels
Header = parent.Header + ' ' + index;
}
public Node Parent { get; }
public Node? Parent { get; }
public string Header { get; }
public bool AreChildrenInitialized => _children != null;
public ObservableCollection<Node> Children => _children ??= CreateChildren();

3
samples/IntegrationTestApp/IntegrationTestApp.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" />

1
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@ -7,6 +7,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
</ItemGroup>

3
samples/Previewer/Previewer.csproj

@ -9,6 +9,9 @@
</Compile>
<EmbeddedResource Include="**\*.xaml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
</ItemGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" />

3
samples/RenderDemo/MainWindow.xaml

@ -72,5 +72,8 @@
<TabItem Header="Brushes">
<pages:BrushesPage />
</TabItem>
<TabItem Header="3D Transformation">
<pages:Transform3DPage />
</TabItem>
</controls:HamburgerMenu>
</Window>

12
samples/RenderDemo/Pages/GlyphRunPage.xaml

@ -2,13 +2,13 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RenderDemo.Pages"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RenderDemo.Pages.GlyphRunPage">
<Border
<Grid
ColumnDefinitions="*,*"
Background="White">
<Image
x:Name="imageControl"
Stretch="None">
</Image>
</Border>
<local:GlyphRunControl Grid.Column="0"/>
<local:GlyphRunGeometryControl Grid.Column="1"/>
</Grid>
</UserControl>

113
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@ -9,14 +9,6 @@ namespace RenderDemo.Pages
{
public class GlyphRunPage : UserControl
{
private Image _imageControl;
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
public GlyphRunPage()
{
this.InitializeComponent();
@ -25,19 +17,43 @@ namespace RenderDemo.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
public class GlyphRunControl : Control
{
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
_imageControl = this.FindControl<Image>("imageControl");
_imageControl.Source = new DrawingImage();
private DispatcherTimer _timer;
DispatcherTimer.Run(() =>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_timer.Tick += (s,e) =>
{
UpdateGlyphRun();
InvalidateVisual();
};
_timer.Start();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer.Stop();
return true;
}, TimeSpan.FromSeconds(1));
_timer = null;
}
private void UpdateGlyphRun()
public override void Render(DrawingContext context)
{
var c = (char)_rand.Next(65, 90);
@ -57,27 +73,70 @@ namespace RenderDemo.Pages
_characters[0] = c;
var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight;
var glyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices);
var drawingGroup = new DrawingGroup();
context.DrawGlyphRun(Brushes.Black, glyphRun);
}
}
var glyphRunDrawing = new GlyphRunDrawing
public class GlyphRunGeometryControl : Control
{
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
private DispatcherTimer _timer;
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer = new DispatcherTimer
{
Foreground = Brushes.Black,
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices)
Interval = TimeSpan.FromSeconds(1)
};
drawingGroup.Children.Add(glyphRunDrawing);
var geometryDrawing = new GeometryDrawing
_timer.Tick += (s, e) =>
{
Pen = new Pen(Brushes.Black),
Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) }
InvalidateVisual();
};
drawingGroup.Children.Add(geometryDrawing);
_timer.Start();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer.Stop();
_timer = null;
}
public override void Render(DrawingContext context)
{
var c = (char)_rand.Next(65, 90);
if (_fontSize + _direction > 200)
{
_direction = -10;
}
if (_fontSize + _direction < 20)
{
_direction = 10;
}
_fontSize += _direction;
_glyphIndices[0] = _glyphTypeface.GetGlyph(c);
_characters[0] = c;
var glyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices);
var geometry = glyphRun.BuildGeometry();
(_imageControl.Source as DrawingImage).Drawing = drawingGroup;
context.DrawGeometry(Brushes.Green, null, geometry);
}
}
}

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

Loading…
Cancel
Save