Browse Source

Merge branch 'master' into features/modal-windows

pull/1542/head
Dan Walmsley 8 years ago
parent
commit
82f6160e86
  1. 17
      .github/PULL_REQUEST_TEMPLATE.md
  2. 12
      samples/BindingTest/MainWindow.xaml
  3. 4
      samples/ControlCatalog/DecoratedWindow.xaml
  4. 2
      samples/ControlCatalog/MainView.xaml
  5. 2
      samples/ControlCatalog/MainWindow.xaml
  6. 2
      samples/RenderTest/MainWindow.xaml
  7. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  8. 13
      src/Avalonia.Base/AvaloniaObject.cs
  9. 16
      src/Avalonia.Base/Platform/IAssetLoader.cs
  10. 10
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  11. 5
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  12. 14
      src/Avalonia.Controls/Window.cs
  13. 5
      src/Avalonia.Controls/WindowBase.cs
  14. 4
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  15. 2
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  16. 8
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  17. 8
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  18. 2
      src/Avalonia.Diagnostics/Views/TreePageView.xaml
  19. 12
      src/Avalonia.Input/DragDropDevice.cs
  20. 27
      src/Avalonia.Input/DragEventArgs.cs
  21. 31
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  22. 15
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  23. 3
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  24. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  25. 238
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  26. 227
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs
  27. 7
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  28. 14
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
  29. 8
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  30. 3
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs
  31. 4
      src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
  32. 18
      src/OSX/Avalonia.MonoMac/WindowImpl.cs
  33. 23
      src/Shared/PlatformSupport/AssetLoader.cs
  34. 1
      src/Windows/Avalonia.Win32/DataObject.cs
  35. 53
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  36. 5
      src/Windows/Avalonia.Win32/OleDataObject.cs
  37. 1
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  38. 6
      src/Windows/Avalonia.Win32/Win32Platform.cs
  39. 84
      src/Windows/Avalonia.Win32/WindowImpl.cs
  40. 4
      src/iOS/Avalonia.iOS/EmbeddableImpl.cs
  41. 27
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs
  42. 71
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs
  43. 79
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs
  44. 15
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  45. 5
      tests/Avalonia.UnitTests/MockAssetLoader.cs

17
.github/PULL_REQUEST_TEMPLATE.md

@ -0,0 +1,17 @@
This template is not intended to be prescriptive, but to help us review pull requests it would be useful if you included as much of the following information as possible:
- What does the pull request do?
- What is the current behavior?
- What is the updated/expected behavior with this PR?
- How was the solution implemented (if it's not obvious)?
Checklist:
- [ ] Added unit tests (if possible)?
- [ ] Added XML documentation to any related classes?
- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation
If the pull request fixes issue(s) list them like this:
Fixes #123
Fixes #456

12
samples/BindingTest/MainWindow.xaml

@ -1,11 +1,15 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:vm="clr-namespace:BindingTest.ViewModels;assembly=BindingTest"
xmlns:local="clr-namespace:BindingTest;assembly=BindingTest">
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:vm="clr-namespace:BindingTest.ViewModels"
xmlns:local="clr-namespace:BindingTest">
<Window.Styles>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="18"/>
</Style>
</Window.Styles>
<Window.Resources>
<vm:TestItem x:Key="SharedItem" StringValue="shared" />
</Window.Resources>
<TabControl>
<TabItem Header="Basic">
@ -40,6 +44,10 @@
<TextBlock FontSize="16" Text="Binding Sources"/>
<TextBox Watermark="Value of first TextBox" UseFloatingWatermark="True"
Text="{Binding #first.Text, Mode=TwoWay}"/>
<TextBox Watermark="Value of SharedItem.StringValue" UseFloatingWatermark="True"
Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/>
<TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True"
Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Margin="18" Gap="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Scheduler"/>

4
samples/ControlCatalog/DecoratedWindow.xaml

@ -1,7 +1,7 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
xmlns:local="clr-namespace:ControlCatalog;assembly=ControlCatalog" HasSystemDecorations="False">
Icon="resm:ControlCatalog.Assets.test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False">
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" >
<Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto">

2
samples/ControlCatalog/MainView.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl Classes="sidebar" Name="Sidebar">
<TabControl.Transition>

2
samples/ControlCatalog/MainWindow.xaml

@ -1,6 +1,6 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
xmlns:local="clr-namespace:ControlCatalog;assembly=ControlCatalog">
xmlns:local="clr-namespace:ControlCatalog">
<local:MainView/>
</Window>

2
samples/RenderTest/MainWindow.xaml

@ -1,6 +1,6 @@
<Window xmlns="https://github.com/avaloniaui"
Title="Avalonia Render Test"
xmlns:pages="clr-namespace:RenderTest.Pages;assembly=RenderTest">
xmlns:pages="clr-namespace:RenderTest.Pages">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Rendering">

6
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -36,7 +36,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_clientSize = value;
UpdateParams();
}
public void SetMinMaxSize(Size minSize, Size maxSize)
{
}
public IScreenImpl Screen { get; }
public Point Position

13
src/Avalonia.Base/AvaloniaObject.cs

@ -71,7 +71,8 @@ namespace Avalonia
public AvaloniaObject()
{
VerifyAccess();
foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegistered(this))
void Notify(AvaloniaProperty property)
{
object value = property.IsDirect ?
((IDirectPropertyAccessor)property).GetValue(this) :
@ -86,6 +87,16 @@ namespace Avalonia
property.NotifyInitialized(e);
}
foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegistered(this))
{
Notify(property);
}
foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(this.GetType()))
{
Notify(property);
}
}
/// <summary>

16
src/Avalonia.Base/Platform/IAssetLoader.cs

@ -43,5 +43,21 @@ namespace Avalonia.Platform
/// The resource was not found.
/// </exception>
Stream Open(Uri uri, Uri baseUri = null);
/// <summary>
/// Opens the resource with the requested URI and returns the resource string and the
/// assembly containing the resource.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>
/// The stream containing the resource contents together with the assembly.
/// </returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
Tuple<Stream, Assembly> OpenAndGetAssembly(Uri uri, Uri baseUri = null);
}
}

10
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -55,7 +55,7 @@ namespace Avalonia.Platform
/// Gets the platform window handle.
/// </summary>
IPlatformHandle Handle { get; }
/// <summary>
/// Gets the maximum size of a window on the system.
/// </summary>
@ -65,7 +65,13 @@ namespace Avalonia.Platform
/// Sets the client size of the toplevel.
/// </summary>
void Resize(Size clientSize);
/// <summary>
/// Minimum width of the window.
/// </summary>
///
void SetMinMaxSize(Size minSize, Size maxSize);
/// <summary>
/// Gets platform specific display information
/// </summary>

5
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -47,6 +47,11 @@ namespace Avalonia.Platform
/// </summary>
void ShowTaskbarIcon(bool value);
/// <summary>
/// Enables or disables resizing of the window
/// </summary>
void CanResize(bool value);
/// <summary>
/// Gets or sets a method called before the underlying implementation is destroyed.
/// Return true to prevent the underlying implementation from closing.

14
src/Avalonia.Controls/Window.cs

@ -95,6 +95,9 @@ namespace Avalonia.Controls
o => o.WindowStartupLocation,
(o, v) => o.WindowStartupLocation = v);
public static readonly StyledProperty<bool> CanResizeProperty =
AvaloniaProperty.Register<Window, bool>(nameof(CanResize), true);
private readonly NameScope _nameScope = new NameScope();
private object _dialogResult;
private readonly Size _maxPlatformClientSize;
@ -114,6 +117,8 @@ namespace Avalonia.Controls
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
CanResizeProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue));
OwnerProperty.Changed.AddClassHandler<Window>((s, e) =>
{
s.PlatformImpl?.SetOwner(((Window)e.NewValue).PlatformImpl);
@ -213,6 +218,15 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Enables or disables resizing of the window
/// </summary>
public bool CanResize
{
get { return GetValue(CanResizeProperty); }
set { SetValue(CanResizeProperty, value); }
}
/// <summary>
/// Gets or sets the icon of the window.
/// </summary>

5
src/Avalonia.Controls/WindowBase.cs

@ -47,6 +47,11 @@ namespace Avalonia.Controls
{
IsVisibleProperty.OverrideDefaultValue<WindowBase>(false);
IsVisibleProperty.Changed.AddClassHandler<WindowBase>(x => x.IsVisibleChanged);
MinWidthProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight)));
MinHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight)));
MaxWidthProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight)));
MaxHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue)));
}
public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)

4
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
@ -30,7 +31,8 @@ namespace Avalonia.DesignerSupport
new Uri("resm:Fake.xaml?assembly=" + Path.GetFileNameWithoutExtension(assemblyPath));
}
var loaded = loader.Load(stream, null, baseUri);
var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null;
var loaded = loader.Load(stream, localAsm, null, baseUri);
var styles = loaded as Styles;
if (styles != null)
{

2
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -24,7 +24,7 @@ namespace Avalonia.DesignerSupport
var loader = new AvaloniaXamlLoader();
var baseLight = (IStyle)loader.Load(
new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"));
new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"), null);
Styles.Add(baseLight);
}
}

8
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -67,6 +67,10 @@ namespace Avalonia.DesignerSupport.Remote
RenderIfNeeded();
}
public void SetMinMaxSize(Size minSize, Size maxSize)
{
}
public IScreenImpl Screen { get; } = new ScreenStub();
public void Activate()
@ -94,6 +98,10 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void CanResize(bool value)
{
}
public void SetOwner(IWindowImpl owner)
{

8
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -78,6 +78,10 @@ namespace Avalonia.DesignerSupport.Remote
public IScreenImpl Screen { get; } = new ScreenStub();
public void SetMinMaxSize(Size minSize, Size maxSize)
{
}
public void SetTitle(string title)
{
}
@ -96,6 +100,10 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public void CanResize(bool value)
{
}
public void SetOwner(IWindowImpl owner)
{
}

2
src/Avalonia.Diagnostics/Views/TreePageView.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels;assembly=Avalonia.Diagnostics">
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels">
<Grid ColumnDefinitions="*,4,3*">
<TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
<TreeView.DataTemplates>

12
src/Avalonia.Input/DragDropDevice.cs

@ -19,11 +19,11 @@ namespace Avalonia.Input
return null;
}
private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data)
private DragDropEffects RaiseDragEvent(Interactive target, IInputElement inputRoot, Point point, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data)
{
if (target == null)
return DragDropEffects.None;
var args = new DragEventArgs(routedEvent, data)
var args = new DragEventArgs(routedEvent, data, target, inputRoot.TranslatePoint(point, target))
{
RoutedEvent = routedEvent,
DragEffects = operation
@ -35,7 +35,7 @@ namespace Avalonia.Input
private DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects)
{
_lastTarget = GetTarget(inputRoot, point);
return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, effects, data);
return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data);
}
private DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects)
@ -43,13 +43,13 @@ namespace Avalonia.Input
var target = GetTarget(inputRoot, point);
if (target == _lastTarget)
return RaiseDragEvent(target, DragDrop.DragOverEvent, effects, data);
return RaiseDragEvent(target, inputRoot, point, DragDrop.DragOverEvent, effects, data);
try
{
if (_lastTarget != null)
_lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
return RaiseDragEvent(target, DragDrop.DragEnterEvent, effects, data);
return RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data);
}
finally
{
@ -75,7 +75,7 @@ namespace Avalonia.Input
{
try
{
return RaiseDragEvent(_lastTarget, DragDrop.DropEvent, effects, data);
return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DropEvent, effects, data);
}
finally
{

27
src/Avalonia.Input/DragEventArgs.cs

@ -1,17 +1,40 @@
using Avalonia.Interactivity;
using System;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace Avalonia.Input
{
public class DragEventArgs : RoutedEventArgs
{
private Interactive _target;
private Point _targetLocation;
public DragDropEffects DragEffects { get; set; }
public IDataObject Data { get; private set; }
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data)
public Point GetPosition(IVisual relativeTo)
{
var point = new Point(0, 0);
if (relativeTo == null)
{
throw new ArgumentNullException(nameof(relativeTo));
}
if (_target != null)
{
point = _target.TranslatePoint(_targetLocation, relativeTo);
}
return point;
}
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation)
: base(routedEvent)
{
this.Data = data;
this._target = target;
this._targetLocation = targetLocation;
}
}

31
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -115,6 +115,8 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_set_title(GtkWindow gtkWindow, Utf8Buffer title);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_set_resizable(GtkWindow gtkWindow, bool resizable);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated);
@ -263,7 +265,7 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_close(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
@ -395,6 +397,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_screen_get_monitor_geometry GdkScreenGetMonitorGeometry;
public static D.gdk_screen_get_monitor_workarea GdkScreenGetMonitorWorkarea;
public static D.gtk_window_set_decorated GtkWindowSetDecorated;
public static D.gtk_window_set_resizable GtkWindowSetResizable;
public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint;
public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint;
public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint;
@ -421,6 +424,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_window_set_override_redirect GdkWindowSetOverrideRedirect;
public static D.gtk_widget_set_size_request GtkWindowSetSizeRequest;
public static D.gtk_window_set_default_size GtkWindowSetDefaultSize;
public static D.gtk_window_set_geometry_hints GtkWindowSetGeometryHints;
public static D.gtk_window_get_position GtkWindowGetPosition;
public static D.gtk_window_move GtkWindowMove;
public static D.gtk_file_chooser_dialog_new GtkFileChooserDialogNew;
@ -502,6 +506,7 @@ namespace Avalonia.Gtk3.Interop
public static D.cairo_set_font_size CairoSetFontSize;
public static D.cairo_move_to CairoMoveTo;
public static D.cairo_destroy CairoDestroy;
public const int G_TYPE_OBJECT = 80;
}
@ -739,19 +744,19 @@ namespace Avalonia.Gtk3.Interop
}
[StructLayout(LayoutKind.Sequential)]
struct GdkGeometry
public struct GdkGeometry
{
gint min_width;
gint min_height;
gint max_width;
gint max_height;
gint base_width;
gint base_height;
gint width_inc;
gint height_inc;
gdouble min_aspect;
gdouble max_aspect;
gint win_gravity;
public gint min_width;
public gint min_height;
public gint max_width;
public gint max_height;
public gint base_width;
public gint base_height;
public gint width_inc;
public gint height_inc;
public gdouble min_aspect;
public gdouble max_aspect;
public gint win_gravity;
}
enum GdkWindowHints

15
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -341,6 +341,20 @@ namespace Avalonia.Gtk3
}
}
public void SetMinMaxSize(Size minSize, Size maxSize)
{
if (GtkWidget.IsClosed)
return;
GdkGeometry geometry = new GdkGeometry();
geometry.min_width = minSize.Width > 0 ? (int)minSize.Width : -1;
geometry.min_height = minSize.Height > 0 ? (int)minSize.Height : -1;
geometry.max_width = !Double.IsInfinity(maxSize.Width) && maxSize.Width > 0 ? (int)maxSize.Width : 999999;
geometry.max_height = !Double.IsInfinity(maxSize.Height) && maxSize.Height > 0 ? (int)maxSize.Height : 999999;
Native.GtkWindowSetGeometryHints(GtkWidget, IntPtr.Zero, ref geometry, GdkWindowHints.GDK_HINT_MIN_SIZE | GdkWindowHints.GDK_HINT_MAX_SIZE);
}
public IMouseDevice MouseDevice => Gtk3Platform.Mouse;
public double Scaling => LastKnownScaleFactor = (int) (Native.GtkWidgetGetScaleFactor?.Invoke(GtkWidget) ?? 1);
@ -431,6 +445,7 @@ namespace Avalonia.Gtk3
{
if (GtkWidget.IsClosed)
return;
Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
if (OverrideRedirect)
{

3
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@ -62,6 +62,9 @@ namespace Avalonia.Gtk3
public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value);
public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value);
public void SetOwner(IWindowImpl owner)
{
throw new NotImplementedException();

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -29,7 +29,6 @@
<Compile Include="..\..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AvaloniaXamlLoaderPortableXaml.cs" />
<Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="Converters\CornerRadiusTypeConverter.cs" />
<Compile Include="Converters\MatrixTypeConverter.cs" />

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

@ -1,11 +1,237 @@
namespace Avalonia.Markup.Xaml
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace Avalonia.Markup.Xaml
{
public class AvaloniaXamlLoader : AvaloniaXamlLoaderPortableXaml
/// <summary>
/// Loads XAML for a avalonia application.
/// </summary>
public class AvaloniaXamlLoader
{
public static object Parse(string xaml)
=> new AvaloniaXamlLoader().Load(xaml);
private readonly AvaloniaXamlSchemaContext _context = GetContext();
private static AvaloniaXamlSchemaContext GetContext()
{
var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
if (result == null)
{
result = AvaloniaXamlSchemaContext.Create();
AvaloniaLocator.CurrentMutable
.Bind<AvaloniaXamlSchemaContext>()
.ToConstant(result);
}
return result;
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
/// </summary>
public AvaloniaXamlLoader()
{
}
/// <summary>
/// Loads the XAML into a Avalonia component.
/// </summary>
/// <param name="obj">The object to load the XAML into.</param>
public static void Load(object obj)
{
Contract.Requires<ArgumentNullException>(obj != null);
var loader = new AvaloniaXamlLoader();
loader.Load(obj.GetType(), obj);
}
/// <summary>
/// Loads the XAML for a type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(Type type, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(type != null);
// HACK: Currently Visual Studio is forcing us to change the extension of xaml files
// in certain situations, so we try to load .xaml and if that's not found we try .xaml.
// Ideally we'd be able to use .xaml everywhere
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>();
if (assetLocator == null)
{
throw new InvalidOperationException(
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
foreach (var uri in GetUrisFor(type))
{
if (assetLocator.Exists(uri))
{
using (var stream = assetLocator.Open(uri))
{
var initialize = rootInstance as ISupportInitialize;
initialize?.BeginInit();
try
{
return Load(stream, type.Assembly, rootInstance, uri);
}
finally
{
initialize?.EndInit();
}
}
}
}
throw new FileNotFoundException("Unable to find view for " + type.FullName);
}
/// <summary>
/// Loads XAML from a URI.
/// </summary>
/// <param name="uri">The URI of the XAML file.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(uri != null);
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>();
if (assetLocator == null)
{
throw new InvalidOperationException(
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
using (var stream = asset.Item1)
{
try
{
return Load(stream, asset.Item2, rootInstance, uri);
}
catch (Exception e)
{
var uriString = uri.ToString();
if (!uri.IsAbsoluteUri)
{
uriString = new Uri(baseUri, uri).AbsoluteUri;
}
throw new XamlLoadException("Error loading xaml at " + uriString, e);
}
}
}
/// <summary>
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace:</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(string xaml, Assembly localAssembly = null, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(xaml != null);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
{
return Load(stream, localAssembly, rootInstance);
}
}
/// <summary>
/// Loads XAML from a stream.
/// </summary>
/// <param name="stream">The stream containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <param name="uri">The URI of the XAML</param>
/// <returns>The loaded object.</returns>
public object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null)
{
var readerSettings = new XamlXmlReaderSettings()
{
BaseUri = uri,
LocalAssembly = localAssembly
};
var reader = new XamlXmlReader(stream, _context, readerSettings);
object result = LoadFromReader(
reader,
AvaloniaXamlContext.For(readerSettings, rootInstance));
var topLevel = result as TopLevel;
if (topLevel != null)
{
DelayedBinding.ApplyBindings(topLevel);
}
return result;
}
internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null)
{
var writer = AvaloniaXamlObjectWriter.Create(
reader.SchemaContext,
context,
parentAmbientProvider);
XamlServices.Transform(reader, writer);
writer.ApplyAllDelayedProperties();
return writer.Result;
}
internal static object LoadFromReader(XamlReader reader)
{
//return XamlServices.Load(reader);
return LoadFromReader(reader, null);
}
/// <summary>
/// Gets the URI for a type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The URI.</returns>
private static IEnumerable<Uri> GetUrisFor(Type type)
{
var asm = type.GetTypeInfo().Assembly.GetName().Name;
var typeName = type.FullName;
yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm);
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm);
}
public static object Parse(string xaml, Assembly localAssembly = null)
=> new AvaloniaXamlLoader().Load(xaml, localAssembly);
public static T Parse<T>(string xaml)
=> (T)Parse(xaml);
public static T Parse<T>(string xaml, Assembly localAssembly = null)
=> (T)Parse(xaml, localAssembly);
}
}

227
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs

@ -1,227 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace Avalonia.Markup.Xaml
{
/// <summary>
/// Loads XAML for a avalonia application.
/// </summary>
public class AvaloniaXamlLoaderPortableXaml
{
private readonly AvaloniaXamlSchemaContext _context = GetContext();
private static AvaloniaXamlSchemaContext GetContext()
{
var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
if (result == null)
{
result = AvaloniaXamlSchemaContext.Create();
AvaloniaLocator.CurrentMutable
.Bind<AvaloniaXamlSchemaContext>()
.ToConstant(result);
}
return result;
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
/// </summary>
public AvaloniaXamlLoaderPortableXaml()
{
}
/// <summary>
/// Loads the XAML into a Avalonia component.
/// </summary>
/// <param name="obj">The object to load the XAML into.</param>
public static void Load(object obj)
{
Contract.Requires<ArgumentNullException>(obj != null);
var loader = new AvaloniaXamlLoader();
loader.Load(obj.GetType(), obj);
}
/// <summary>
/// Loads the XAML for a type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(Type type, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(type != null);
// HACK: Currently Visual Studio is forcing us to change the extension of xaml files
// in certain situations, so we try to load .xaml and if that's not found we try .xaml.
// Ideally we'd be able to use .xaml everywhere
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>();
if (assetLocator == null)
{
throw new InvalidOperationException(
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
foreach (var uri in GetUrisFor(type))
{
if (assetLocator.Exists(uri))
{
using (var stream = assetLocator.Open(uri))
{
var initialize = rootInstance as ISupportInitialize;
initialize?.BeginInit();
try
{
return Load(stream, rootInstance, uri);
}
finally
{
initialize?.EndInit();
}
}
}
}
throw new FileNotFoundException("Unable to find view for " + type.FullName);
}
/// <summary>
/// Loads XAML from a URI.
/// </summary>
/// <param name="uri">The URI of the XAML file.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(uri != null);
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>();
if (assetLocator == null)
{
throw new InvalidOperationException(
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
using (var stream = assetLocator.Open(uri, baseUri))
{
try
{
return Load(stream, rootInstance, uri);
}
catch (Exception e)
{
var uriString = uri.ToString();
if (!uri.IsAbsoluteUri)
{
uriString = new Uri(baseUri, uri).AbsoluteUri;
}
throw new XamlLoadException("Error loading xaml at " + uriString, e);
}
}
}
/// <summary>
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(string xaml, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(xaml != null);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
{
return Load(stream, rootInstance);
}
}
/// <summary>
/// Loads XAML from a stream.
/// </summary>
/// <param name="stream">The stream containing the XAML.</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <param name="uri">The URI of the XAML</param>
/// <returns>The loaded object.</returns>
public object Load(Stream stream, object rootInstance = null, Uri uri = null)
{
var readerSettings = new XamlXmlReaderSettings()
{
BaseUri = uri,
LocalAssembly = rootInstance?.GetType().GetTypeInfo().Assembly
};
var reader = new XamlXmlReader(stream, _context, readerSettings);
object result = LoadFromReader(
reader,
AvaloniaXamlContext.For(readerSettings, rootInstance));
var topLevel = result as TopLevel;
if (topLevel != null)
{
DelayedBinding.ApplyBindings(topLevel);
}
return result;
}
internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null)
{
var writer = AvaloniaXamlObjectWriter.Create(
reader.SchemaContext,
context);
XamlServices.Transform(reader, writer);
writer.ApplyAllDelayedProperties();
return writer.Result;
}
internal static object LoadFromReader(XamlReader reader)
{
//return XamlServices.Load(reader);
return LoadFromReader(reader, null);
}
/// <summary>
/// Gets the URI for a type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The URI.</returns>
private static IEnumerable<Uri> GetUrisFor(Type type)
{
var asm = type.GetTypeInfo().Assembly.GetName().Name;
var typeName = type.FullName;
yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm);
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm);
}
}
}

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

@ -36,13 +36,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// Look upwards though the ambient context for IResourceProviders which might be able
// to give us the resource.
//
// TODO: If we're in a template then only the ambient values since the root of the
// template wil be included here. We need some way to get hold of the parent ambient
// context and search that. See the test:
//
// StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File
//
foreach (var ambientValue in ambientValues)
{
// We override XamlType.CanAssignTo in BindingXamlType so the results we get back

14
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs

@ -11,7 +11,8 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{
public static AvaloniaXamlObjectWriter Create(
XamlSchemaContext schemaContext,
AvaloniaXamlContext context)
AvaloniaXamlContext context,
IAmbientProvider parentAmbientProvider = null)
{
var nameScope = new AvaloniaNameScope { Instance = context?.RootInstance };
@ -23,8 +24,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
};
return new AvaloniaXamlObjectWriter(schemaContext,
writerSettings.WithContext(context),
nameScope);
writerSettings.WithContext(context),
nameScope,
parentAmbientProvider);
}
private readonly DelayedValuesHelper _delayedValuesHelper = new DelayedValuesHelper();
@ -34,9 +36,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private AvaloniaXamlObjectWriter(
XamlSchemaContext schemaContext,
XamlObjectWriterSettings settings,
AvaloniaNameScope nameScope
)
: base(schemaContext, settings)
AvaloniaNameScope nameScope,
IAmbientProvider parentAmbientProvider)
: base(schemaContext, settings, parentAmbientProvider)
{
_nameScope = nameScope;
}

8
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@ -10,8 +10,10 @@ namespace Avalonia.Markup.Xaml.Templates
public class TemplateContent
{
public TemplateContent(IEnumerable<NamespaceDeclaration> namespaces, XamlReader reader)
public TemplateContent(IEnumerable<NamespaceDeclaration> namespaces, XamlReader reader,
IAmbientProvider ambientProvider)
{
ParentAmbientProvider = ambientProvider;
List = new XamlNodeList(reader.SchemaContext);
//we need to rpeserve all namespace and prefixes to writer
@ -26,9 +28,11 @@ namespace Avalonia.Markup.Xaml.Templates
public XamlNodeList List { get; }
private IAmbientProvider ParentAmbientProvider { get; }
public IControl Load()
{
return (IControl)AvaloniaXamlLoader.LoadFromReader(List.GetReader());
return (IControl)AvaloniaXamlLoader.LoadFromReader(List.GetReader(), parentAmbientProvider: ParentAmbientProvider);
}
public static IControl Load(object templateContent)

3
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs

@ -14,7 +14,8 @@ namespace Avalonia.Markup.Xaml.Templates
{
var tdc = (ITypeDescriptorContext)serviceProvider;
var ns = tdc.GetService<IXamlNamespaceResolver>();
return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader);
var ambientProvider = tdc.GetService<IAmbientProvider>();
return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader, ambientProvider);
}
public override XamlReader Save(object value, IServiceProvider serviceProvider)

4
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@ -161,6 +161,10 @@ namespace Avalonia.MonoMac
Position = pos;
}
public void SetMinMaxSize(Size minSize, Size maxSize)
{
}
public IScreenImpl Screen
{
get;

18
src/OSX/Avalonia.MonoMac/WindowImpl.cs

@ -9,6 +9,7 @@ namespace Avalonia.MonoMac
class WindowImpl : WindowBaseImpl, IWindowImpl
{
public bool IsDecorated = true;
public bool IsResizable = true;
public CGRect? UndecoratedLastUnmaximizedFrame;
public WindowImpl()
@ -76,10 +77,15 @@ namespace Avalonia.MonoMac
protected override NSWindowStyle GetStyle()
{
var windowStyle = NSWindowStyle.Borderless;
if (IsDecorated)
return NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable |
NSWindowStyle.Titled;
return NSWindowStyle.Borderless;
windowStyle |= NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled;
if (IsResizable)
windowStyle |= NSWindowStyle.Resizable;
return windowStyle;
}
public void SetSystemDecorations(bool enabled)
@ -88,6 +94,12 @@ namespace Avalonia.MonoMac
UpdateStyle();
}
public void CanResize(bool value)
{
IsResizable = value;
UpdateStyle();
}
public void SetTitle(string title) => Window.Title = title;
class ModalDisposable : IDisposable

23
src/Shared/PlatformSupport/AssetLoader.cs

@ -67,7 +67,23 @@ namespace Avalonia.Shared.PlatformSupport
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
public Stream Open(Uri uri, Uri baseUri = null)
public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
/// <summary>
/// Opens the resource with the requested URI and returns the resource string and the
/// assembly containing the resource.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>
/// The stream containing the resource contents together with the assembly.
/// </returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
public Tuple<Stream, Assembly> OpenAndGetAssembly(Uri uri, Uri baseUri = null)
{
var asset = GetAsset(uri, baseUri);
@ -76,7 +92,7 @@ namespace Avalonia.Shared.PlatformSupport
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
return asset.GetStream();
return Tuple.Create(asset.GetStream(), asset.Assembly);
}
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
@ -162,6 +178,7 @@ namespace Avalonia.Shared.PlatformSupport
private interface IAssetDescriptor
{
Stream GetStream();
Assembly Assembly { get; }
}
private class AssemblyResourceDescriptor : IAssetDescriptor
@ -179,6 +196,8 @@ namespace Avalonia.Shared.PlatformSupport
{
return _asm.GetManifestResourceStream(_name);
}
public Assembly Assembly => _asm;
}
private class AssemblyDescriptor

1
src/Windows/Avalonia.Win32/DataObject.cs

@ -7,7 +7,6 @@ using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

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

@ -558,7 +558,18 @@ namespace Avalonia.Win32.Interop
{
DIB_RGB_COLORS = 0, /* color table in RGBs */
DIB_PAL_COLORS /* color table in palette indices */
};
}
public enum WindowLongParam
{
GWL_WNDPROC = -4,
GWL_HINSTANCE = -6,
GWL_HWNDPARENT = -8,
GWL_ID = -12,
GWL_STYLE = -16,
GWL_EXSTYLE = -20,
GWL_USERDATA = -21
}
[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
@ -615,6 +626,16 @@ namespace Avalonia.Win32.Interop
public uint[] cols;
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
}
public const int SizeOf_BITMAPINFOHEADER = 40;
[DllImport("user32.dll")]
@ -849,6 +870,9 @@ namespace Avalonia.Win32.Interop
return SetClassLong64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", EntryPoint = "SetCursor")]
internal static extern IntPtr SetCursor(IntPtr hCursor);
[DllImport("ole32.dll", PreserveSig = true)]
internal static extern int CoCreateInstance(ref Guid clsid,
IntPtr ignore1, int ignore2, ref Guid iid, [MarshalAs(UnmanagedType.IUnknown), Out] out object pUnkOuter);
@ -974,7 +998,7 @@ namespace Avalonia.Win32.Interop
public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
public static extern void DoDragDrop(IDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect);
public static extern void DoDragDrop(IOleDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect);
@ -1366,13 +1390,13 @@ namespace Avalonia.Win32.Interop
internal interface IDropTarget
{
[PreserveSig]
UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
[PreserveSig]
UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
[PreserveSig]
UnmanagedMethods.HRESULT DragLeave();
[PreserveSig]
UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
}
[ComImport]
@ -1387,6 +1411,27 @@ namespace Avalonia.Win32.Interop
}
[ComImport]
[Guid("0000010E-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleDataObject
{
void GetData([In] ref FORMATETC format, out STGMEDIUM medium);
void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium);
[PreserveSig]
int QueryGetData([In] ref FORMATETC format);
[PreserveSig]
int GetCanonicalFormatEtc([In] ref FORMATETC formatIn, out FORMATETC formatOut);
void SetData([In] ref FORMATETC formatIn, [In] ref STGMEDIUM medium, [MarshalAs(UnmanagedType.Bool)] bool release);
IEnumFORMATETC EnumFormatEtc(DATADIR direction);
[PreserveSig]
int DAdvise([In] ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection);
void DUnadvise(int connection);
[PreserveSig]
int EnumDAdvise(out IEnumSTATDATA enumAdvise);
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct _DROPFILES
{

5
src/Windows/Avalonia.Win32/OleDataObject.cs

@ -8,15 +8,14 @@ using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Interop;
using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
namespace Avalonia.Win32
{
class OleDataObject : Avalonia.Input.IDataObject
{
private IDataObject _wrapped;
private IOleDataObject _wrapped;
public OleDataObject(IDataObject wrapped)
public OleDataObject(IOleDataObject wrapped)
{
_wrapped = wrapped;
}

1
src/Windows/Avalonia.Win32/OleDropTarget.cs

@ -3,7 +3,6 @@ using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
namespace Avalonia.Win32
{

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

@ -82,11 +82,11 @@ namespace Avalonia.Win32
.Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<IPlatformIconLoader>().ToConstant(s_instance);
if (OleContext.Current != null)
AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
UseDeferredRendering = deferredRendering;
_uiThread = UnmanagedMethods.GetCurrentThreadId();
if (OleContext.Current != null)
AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
}
public bool HasMessages()

84
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -31,10 +31,14 @@ namespace Avalonia.Win32
private IInputRoot _owner;
private bool _trackingMouse;
private bool _decorated = true;
private bool _resizable = true;
private double _scaling = 1;
private WindowState _showWindowState;
private FramebufferManager _framebuffer;
private OleDropTarget _dropTarget;
private Size _minSize;
private Size _maxSize;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
#endif
@ -77,8 +81,8 @@ namespace Avalonia.Win32
{
get
{
var style = UnmanagedMethods.GetWindowLong(_hwnd, -16);
var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, -20);
var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
var padding = new UnmanagedMethods.RECT();
if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle))
@ -102,6 +106,12 @@ namespace Avalonia.Win32
}
}
public void SetMinMaxSize(Size minSize, Size maxSize)
{
_minSize = minSize;
_maxSize = maxSize;
}
public IScreenImpl Screen
{
get;
@ -235,13 +245,19 @@ namespace Avalonia.Win32
return;
}
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -16);
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
var systemDecorationStyles = UnmanagedMethods.WindowStyles.WS_OVERLAPPED
| UnmanagedMethods.WindowStyles.WS_CAPTION
| UnmanagedMethods.WindowStyles.WS_SYSMENU
| UnmanagedMethods.WindowStyles.WS_MINIMIZEBOX
| UnmanagedMethods.WindowStyles.WS_MAXIMIZEBOX;
style |= systemDecorationStyles;
if (!value)
{
style ^= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
style ^= systemDecorationStyles;
}
UnmanagedMethods.RECT windowRect;
@ -251,7 +267,7 @@ namespace Avalonia.Win32
Rect newRect;
var oldThickness = BorderThickness;
UnmanagedMethods.SetWindowLong(_hwnd, -16, (uint)style);
UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style);
if (value)
{
@ -383,8 +399,11 @@ namespace Avalonia.Win32
public void SetCursor(IPlatformHandle cursor)
{
UnmanagedMethods.SetClassLong(_hwnd, UnmanagedMethods.ClassLongIndex.GCL_HCURSOR,
cursor?.Handle ?? DefaultCursor);
var hCursor = cursor?.Handle ?? DefaultCursor;
UnmanagedMethods.SetClassLong(_hwnd, UnmanagedMethods.ClassLongIndex.GCL_HCURSOR, hCursor);
if (_owner.IsPointerOver)
UnmanagedMethods.SetCursor(hCursor);
}
protected virtual IntPtr CreateWindowOverride(ushort atom)
@ -611,7 +630,26 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_MOVE:
PositionChanged?.Invoke(new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)));
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_GETMINMAXINFO:
MINMAXINFO mmi = Marshal.PtrToStructure<UnmanagedMethods.MINMAXINFO>(lParam);
if (_minSize.Width > 0)
mmi.ptMinTrackSize.X = (int)((_minSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
if (_minSize.Height > 0)
mmi.ptMinTrackSize.Y = (int)((_minSize.Height * Scaling) + BorderThickness.Top + BorderThickness.Bottom);
if (!Double.IsInfinity(_maxSize.Width) && _maxSize.Width > 0)
mmi.ptMaxTrackSize.X = (int)((_maxSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
if (!Double.IsInfinity(_maxSize.Height) && _maxSize.Height > 0)
mmi.ptMaxTrackSize.Y = (int)((_maxSize.Height * Scaling) + BorderThickness.Top + BorderThickness.Bottom);
Marshal.StructureToPtr(mmi, lParam, true);
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_DISPLAYCHANGE:
(Screen as ScreenImpl)?.InvalidateScreensCache();
return IntPtr.Zero;
@ -798,11 +836,11 @@ namespace Avalonia.Win32
public void ShowTaskbarIcon(bool value)
{
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -20);
style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE);
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW;
style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE);
style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW;
if (value)
style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW;
else
@ -813,11 +851,29 @@ namespace Avalonia.Win32
{
//Toggle to make the styles stick
UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide);
UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style);
UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE, (uint)style);
UnmanagedMethods.ShowWindow(_hwnd, windowPlacement.ShowCmd);
}
}
public void CanResize(bool value)
{
if (value == _resizable)
{
return;
}
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
if (value)
style |= UnmanagedMethods.WindowStyles.WS_SIZEFRAME;
else
style &= ~(UnmanagedMethods.WindowStyles.WS_SIZEFRAME);
UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style);
_resizable = value;
}
public void SetOwner(IWindowImpl owner)
{
UnmanagedMethods.SetWindowLong(_hwnd, -8, (uint)owner.Handle.Handle);

4
src/iOS/Avalonia.iOS/EmbeddableImpl.cs

@ -14,6 +14,10 @@ namespace Avalonia.iOS
}
public void SetMinMaxSize(Size minSize, Size maxSize)
{
}
public IDisposable ShowDialog()
{
return Disposable.Empty;

27
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs

@ -24,10 +24,27 @@ namespace Avalonia.Base.UnitTests
Assert.Throws<IndexOutOfRangeException>(() => target.SetValue(Class2.FooProperty, "throw"));
}
private class Class1 : AvaloniaObject
[Fact]
public void AvaloniaProperty_Initialized_Is_Called_For_Attached_Property()
{
bool raised = false;
using (Class1.FooProperty.Initialized.Subscribe(x => raised = true))
{
new Class3();
}
Assert.True(raised);
}
private class Base : AvaloniaObject
{
}
private class Class1 : Base
{
public static readonly AttachedProperty<string> FooProperty =
AvaloniaProperty.RegisterAttached<Class1, Class2, string>(
AvaloniaProperty.RegisterAttached<Class1, Base, string>(
"Foo",
"foodefault",
validate: ValidateFoo);
@ -43,10 +60,14 @@ namespace Avalonia.Base.UnitTests
}
}
private class Class2 : AvaloniaObject
private class Class2 : Base
{
public static readonly AttachedProperty<string> FooProperty =
Class1.FooProperty.AddOwner<Class2>();
}
private class Class3 : Base
{
}
}
}

71
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{
public class BindingExtensionTests
{
[Fact]
public void BindingExtension_Binds_To_Source()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<x:String x:Key='text'>foobar</x:String>
</Window.Resources>
<TextBlock Name='textBlock' Text='{Binding Source={StaticResource text}}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
window.Show();
Assert.Equal("foobar", textBlock.Text);
}
}
private IDisposable StyledWindow(params (string, string)[] assets)
{
var services = TestServices.StyledWindow.With(
assetLoader: new MockAssetLoader(assets),
theme: () => new Styles
{
WindowStyle(),
});
return UnitTestApplication.Start(services);
}
private Style WindowStyle()
{
return new Style(x => x.OfType<Window>())
{
Setters =
{
new Setter(
Window.TemplateProperty,
new FuncControlTemplate<Window>(x =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
}))
}
};
}
}
}

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

@ -323,7 +323,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
[Fact(Skip = "Not yet supported by Portable.Xaml")]
[Fact]
public void StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
{
var styleXaml = @"
@ -361,9 +361,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
var border = (Border)button.GetVisualChildren().Single();
var brush = (SolidColorBrush)border.Background;
// To make this work we somehow need to be able to get hold of the parent ambient
// context from Portable.Xaml. See TODO in StaticResourceExtension.
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
}
@ -417,6 +415,79 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
[Fact]
public void StaticResource_Can_Be_Assigned_To_Binding_Converter_In_DataTemplate()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Resources>
<local:TestValueConverter x:Key='converter' Append='bar'/>
<DataTemplate x:Key='PurpleData'>
<TextBlock Name='textBlock' Text='{Binding Converter={StaticResource converter}}'/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Name='presenter' Content='foo' ContentTemplate='{StaticResource PurpleData}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
window.DataContext = "foo";
var presenter = window.FindControl<ContentPresenter>("presenter");
window.Show();
var textBlock = (TextBlock)presenter.GetVisualChildren().Single();
Assert.NotNull(textBlock);
Assert.Equal("foobar", textBlock.Text);
}
}
[Fact]
public void StaticResource_Is_Correctly_Chosen_From_Within_DataTemplate()
{
// this tests if IAmbientProviders in DataTemplate contexts are in correct order
// if they wouldn't be, Purple brush would be bound to
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Resources>
<local:TestValueConverter x:Key='converter' Append='-bar'/>
<SolidColorBrush x:Key='brush' Color='Purple'/>
<DataTemplate x:Key='WhiteData'>
<Border>
<Border.Resources>
<SolidColorBrush x:Key='brush' Color='White'/>
</Border.Resources>
<TextBlock Name='textBlock' Text='{Binding Color, Source={StaticResource brush}, Converter={StaticResource converter}}' Foreground='{StaticResource brush}' />
</Border>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content='foo' ContentTemplate='{StaticResource WhiteData}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
window.Show();
var textBlock = window.GetVisualDescendants().OfType<TextBlock>().Single();
Assert.NotNull(textBlock);
Assert.Equal("White-bar", textBlock.Text);
}
}
[Fact]
public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed()
{

15
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -81,6 +81,21 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(21.0, TextBlock.GetFontSize(target));
}
[Fact]
public void Attached_Property_Is_Set_On_Control_Outside_Avalonia_Namspace()
{
// Test for issue #1548
var xaml =
@"<UserControl xmlns='https://github.com/avaloniaui'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<local:TestControl Grid.Column='2' />
</UserControl>";
var target = AvaloniaXamlLoader.Parse<UserControl>(xaml);
Assert.Equal(2, Grid.GetColumn((TestControl)target.Content));
}
[Fact]
public void Attached_Property_With_Namespace_Is_Set()
{

5
tests/Avalonia.UnitTests/MockAssetLoader.cs

@ -27,6 +27,11 @@ namespace Avalonia.UnitTests
{
return new MemoryStream(Encoding.UTF8.GetBytes(_assets[uri]));
}
public Tuple<Stream, Assembly> OpenAndGetAssembly(Uri uri, Uri baseUri = null)
{
return Tuple.Create(Open(uri, baseUri), (Assembly)null);
}
public void SetDefaultAssembly(Assembly asm)
{

Loading…
Cancel
Save