Browse Source

Merge branch 'master' into issues/1926

pull/5058/head
Andrey Kunchev 6 years ago
committed by GitHub
parent
commit
729b1e3af6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  2. 6
      src/Avalonia.Controls/ItemsControl.cs
  3. 4
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  4. 4
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  5. 4
      src/Avalonia.MicroCom/MicroComVtblBase.cs
  6. 2
      src/Avalonia.Native/WindowImplBase.cs
  7. 19
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  8. 34
      src/Windows/Avalonia.Win32/WindowImpl.cs
  9. 5
      src/tools/MicroComGenerator/CppGen.cs
  10. 92
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  11. 112
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

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

@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Dialogs;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
#pragma warning disable 4014
@ -112,11 +113,29 @@ namespace ControlCatalog.Pages
private Window CreateSampleWindow()
{
var window = new Window();
window.Height = 200;
window.Width = 200;
window.Content = new TextBlock { Text = "Hello world!" };
window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
Button button;
var window = new Window
{
Height = 200,
Width = 200,
Content = new StackPanel
{
Spacing = 4,
Children =
{
new TextBlock { Text = "Hello world!" },
(button = new Button
{
HorizontalAlignment = HorizontalAlignment.Center,
Content = "Click to close"
})
}
},
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
button.Click += (_, __) => window.Close();
return window;
}

6
src/Avalonia.Controls/ItemsControl.cs

@ -449,11 +449,7 @@ namespace Avalonia.Controls
if (_itemContainerGenerator != null)
{
_itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue;
if (e.OldValue != null && Presenter != null)
{
Presenter.ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
// TODO: Rebuild the item containers.
}
}

4
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -200,10 +200,6 @@ namespace Avalonia.Controls.Presenters
break;
case NotifyCollectionChangedAction.Reset:
Owner.ItemContainerGenerator.Clear();
VirtualizingPanel.Children.Clear();
FirstIndex = NextIndex = 0;
RecycleContainersOnRemove();
CreateAndRemoveContainers();
panel.ForceInvalidateMeasure();

4
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -57,8 +57,6 @@ namespace Avalonia.Controls.Presenters
set
{
var itemsReplaced = (_items != value);
_itemsSubscription?.Dispose();
_itemsSubscription = null;
@ -69,7 +67,7 @@ namespace Avalonia.Controls.Presenters
SetAndRaise(ItemsProperty, ref _items, value);
if (_createdPanel && itemsReplaced)
if (_createdPanel)
{
ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}

4
src/Avalonia.MicroCom/MicroComVtblBase.cs

@ -7,10 +7,10 @@ namespace Avalonia.MicroCom
public unsafe class MicroComVtblBase
{
private List<IntPtr> _methods = new List<IntPtr>();
[UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)]
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
private delegate int AddRefDelegate(Ccw* ccw);
[UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)]
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv);
public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable();

2
src/Avalonia.Native/WindowImplBase.cs

@ -174,7 +174,7 @@ namespace Avalonia.Native
void IAvnWindowBaseEvents.Resized(AvnSize* size)
{
if (_parent._native != null)
if (_parent?._native != null)
{
var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s;

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

@ -65,23 +65,10 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
// Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337
// We need to enable parent window before destroying child window to prevent OS from activating a random window behind us.
// This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks
// Our window closed callback will set enabled state to a correct value after child window gets destroyed.
// We need to verify if parent is still alive (perhaps it got destroyed somehow).
if (_parent != null && IsWindow(_parent._hwnd))
{
var wasActive = GetActiveWindow() == _hwnd;
_parent.SetEnabled(true);
BeforeCloseCleanup(false);
// We also need to activate our parent window since again OS might try to activate a window behind if it is not set.
if (wasActive)
{
SetActiveWindow(_parent._hwnd);
}
}
// Used to distinguish between programmatic and regular close requests.
_isCloseRequested = true;
break;
}

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

@ -83,6 +83,7 @@ namespace Avalonia.Win32
private POINT _maxTrackSize;
private WindowImpl _parent;
private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default;
private bool _isCloseRequested;
public WindowImpl()
{
@ -506,6 +507,13 @@ namespace Avalonia.Win32
if (_hwnd != IntPtr.Zero)
{
// Detect if we are being closed programmatically - this would mean that WM_CLOSE was not called
// and we didn't prepare this window for destruction.
if (!_isCloseRequested)
{
BeforeCloseCleanup(true);
}
DestroyWindow(_hwnd);
_hwnd = IntPtr.Zero;
}
@ -948,6 +956,32 @@ namespace Avalonia.Win32
SetFocus(_hwnd);
}
}
private void BeforeCloseCleanup(bool isDisposing)
{
// Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337
// We need to enable parent window before destroying child window to prevent OS from activating a random window behind us (or last active window).
// This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks
// We need to verify if parent is still alive (perhaps it got destroyed somehow).
if (_parent != null && IsWindow(_parent._hwnd))
{
var wasActive = GetActiveWindow() == _hwnd;
// We can only set enabled state if we are not disposing - generally Dispose happens after enabled state has been set.
// Ignoring this would cause us to enable a window that might be disabled.
if (!isDisposing)
{
// Our window closed callback will set enabled state to a correct value after child window gets destroyed.
_parent.SetEnabled(true);
}
// We also need to activate our parent window since again OS might try to activate a window behind if it is not set.
if (wasActive)
{
SetActiveWindow(_parent._hwnd);
}
}
}
private void MaximizeWithoutCoveringTaskbar()
{

5
src/tools/MicroComGenerator/CppGen.cs

@ -14,7 +14,10 @@ namespace MicroComGenerator
name = "unsigned char";
else if(name == "uint")
name = "unsigned int";
return name + new string('*', type.PointerLevel);
type = type.Clone();
type.Name = name;
return type.Format();
}
public static string GenerateCpp(AstIdlNode idl)

92
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -596,98 +596,6 @@ namespace Avalonia.Controls.UnitTests
root.Child = target;
}
[Fact]
public void Presenter_Items_Should_Be_In_Sync_When_Replacing_Items()
{
var target = new ItemsControl
{
Template = GetTemplate(),
Items = new[]
{
new Item("Item1")
}
};
var root = new TestRoot { Child = target };
var otherPanel = new StackPanel();
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value);
materializedEventCallCount++;
};
target.Items = new[]
{
new Item("Item2")
};
//Ensure that events are called one time only
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
}
[Fact]
public void Presenter_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate()
{
var target = new ItemsControl
{
Template = GetTemplate(),
Items = new[]
{
new Item("Item1")
},
ItemTemplate = new FuncDataTemplate<Item>((x, ns) => new TextBlock())
};
var root = new TestRoot { Child = target };
var otherPanel = new StackPanel();
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl);
contentPresenter.UpdateChild();
Assert.IsType<TextBlock>(contentPresenter.Child);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl);
contentPresenter.UpdateChild();
Assert.IsType<Canvas>(contentPresenter.Child);
materializedEventCallCount++;
};
target.ItemTemplate =
new FuncDataTemplate<Item>((x, ns) => new Canvas());
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
}
private class Item
{
public Item(string value)

112
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -454,118 +454,6 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void ListBox_Presenter_Items_Should_Be_In_Sync_When_Replacing_Items()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var wnd = new Window() { Width = 100, Height = 100, IsVisible = true };
var target = new ListBox()
{
VerticalAlignment = Layout.VerticalAlignment.Top,
AutoScrollToSelectedItem = true,
Width = 50,
VirtualizationMode = ItemVirtualizationMode.Simple,
Items = new[]
{
new Item("Item1")
},
};
wnd.Content = target;
var lm = wnd.LayoutManager;
lm.ExecuteInitialLayoutPass();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value);
materializedEventCallCount++;
};
target.Items = new[]
{
new Item("Item2")
};
//assert that materialize/dematerialize events are called exactly one time
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
}
}
[Fact]
public void ListBox_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var wnd = new Window() { Width = 100, Height = 100, IsVisible = true };
var target = new ListBox()
{
VerticalAlignment = Layout.VerticalAlignment.Top,
AutoScrollToSelectedItem = true,
Width = 50,
VirtualizationMode = ItemVirtualizationMode.Simple,
Items = new[]
{
new Item("Item1")
},
ItemTemplate =
new FuncDataTemplate<Item>((x, ns) => new Canvas())
};
wnd.Content = target;
var lm = wnd.LayoutManager;
lm.ExecuteInitialLayoutPass();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
Assert.IsType<Canvas>(((ListBoxItem)e.Containers[0].ContainerControl).Presenter.Child);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
ListBoxItem materializedListBoxItem = null;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
materializedListBoxItem = ((ListBoxItem)e.Containers[0].ContainerControl);
materializedEventCallCount++;
};
target.ItemTemplate =
new FuncDataTemplate<Item>((x, ns) => new TextBlock());
//ensure events are called only one time
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
wnd.LayoutManager.ExecuteLayoutPass();
//ensure that new template has been applied
Assert.IsType<TextBlock>(materializedListBoxItem.Presenter.Child);
}
}
private FuncControlTemplate ListBoxTemplate()
{
return new FuncControlTemplate<ListBox>((parent, scope) =>

Loading…
Cancel
Save