Browse Source

Merge branch 'master' into style-inspector

pull/5537/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
521c784234
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      native/Avalonia.Native/src/OSX/common.h
  2. 17
      native/Avalonia.Native/src/OSX/main.mm
  3. 85
      native/Avalonia.Native/src/OSX/menu.mm
  4. 16
      src/Avalonia.Animation/Animation.cs
  5. 2
      src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
  6. 70
      src/Avalonia.Controls/ComboBox.cs
  7. 55
      src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
  8. 2
      src/Avalonia.Layout/UniformGridLayout.cs
  9. 10
      src/Avalonia.Layout/UniformGridLayoutState.cs
  10. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  11. 2
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  12. 1
      src/Avalonia.Native/avn.idl
  13. 3
      src/Avalonia.Themes.Default/MenuItem.xaml
  14. 3
      src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
  15. 36
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
  16. 1
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  17. 38
      tests/Avalonia.LeakTests/ControlTests.cs
  18. 6
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs

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

@ -28,6 +28,8 @@ extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
extern void SetAutoGenerateDefaultAppMenuItems (bool enabled);
extern bool GetAutoGenerateDefaultAppMenuItems ();
extern void InitializeAvnApp();
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;

17
native/Avalonia.Native/src/OSX/main.mm

@ -2,6 +2,7 @@
#define COM_GUIDS_MATERIALIZE
#include "common.h"
static bool s_generateDefaultAppMenuItems = true;
static NSString* s_appTitle = @"Avalonia";
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
@ -122,6 +123,12 @@ public:
? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory;
return S_OK;
}
virtual HRESULT SetDisableDefaultApplicationMenuItems (bool enabled) override
{
SetAutoGenerateDefaultAppMenuItems(!enabled);
return S_OK;
}
};
/// See "Using POSIX Threads in a Cocoa Application" section here:
@ -310,3 +317,13 @@ CGFloat PrimaryDisplayHeight()
{
return NSMaxY([[[NSScreen screens] firstObject] frame]);
}
void SetAutoGenerateDefaultAppMenuItems (bool enabled)
{
s_generateDefaultAppMenuItems = enabled;
}
bool GetAutoGenerateDefaultAppMenuItems ()
{
return s_generateDefaultAppMenuItems;
}

85
native/Avalonia.Native/src/OSX/menu.mm

@ -445,47 +445,50 @@ extern void SetAppMenu (NSString* appName, IAvnMenu* menu)
auto appMenu = [s_appMenuItem submenu];
[appMenu addItem:[NSMenuItem separatorItem]];
// Services item and menu
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services";
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:servicesItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Hide Application
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
[appMenu addItem:hideItem];
// Hide Others
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[appMenu addItem:hideAllOthersItem];
// Show All
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:showAllItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Quit Application
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.keyEquivalent = @"q";
quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
if(GetAutoGenerateDefaultAppMenuItems())
{
[appMenu addItem:[NSMenuItem separatorItem]];
// Services item and menu
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services";
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:servicesItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Hide Application
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
[appMenu addItem:hideItem];
// Hide Others
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[appMenu addItem:hideAllOthersItem];
// Show All
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:showAllItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Quit Application
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.keyEquivalent = @"q";
quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
}
}
else
{

16
src/Avalonia.Animation/Animation.cs

@ -22,7 +22,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DurationProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_duration),
nameof(Duration),
o => o._duration,
(o, v) => o._duration = v);
@ -31,7 +31,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, IterationCount> IterationCountProperty =
AvaloniaProperty.RegisterDirect<Animation, IterationCount>(
nameof(_iterationCount),
nameof(IterationCount),
o => o._iterationCount,
(o, v) => o._iterationCount = v);
@ -40,7 +40,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, PlaybackDirection> PlaybackDirectionProperty =
AvaloniaProperty.RegisterDirect<Animation, PlaybackDirection>(
nameof(_playbackDirection),
nameof(PlaybackDirection),
o => o._playbackDirection,
(o, v) => o._playbackDirection = v);
@ -49,7 +49,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, FillMode> FillModeProperty =
AvaloniaProperty.RegisterDirect<Animation, FillMode>(
nameof(_fillMode),
nameof(FillMode),
o => o._fillMode,
(o, v) => o._fillMode = v);
@ -58,7 +58,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, Easing> EasingProperty =
AvaloniaProperty.RegisterDirect<Animation, Easing>(
nameof(_easing),
nameof(Easing),
o => o._easing,
(o, v) => o._easing = v);
@ -67,7 +67,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delay),
nameof(Delay),
o => o._delay,
(o, v) => o._delay = v);
@ -76,7 +76,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayBetweenIterationsProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delayBetweenIterations),
nameof(DelayBetweenIterations),
o => o._delayBetweenIterations,
(o, v) => o._delayBetweenIterations = v);
@ -85,7 +85,7 @@ namespace Avalonia.Animation
/// </summary>
public static readonly DirectProperty<Animation, double> SpeedRatioProperty =
AvaloniaProperty.RegisterDirect<Animation, double>(
nameof(_speedRatio),
nameof(SpeedRatio),
o => o._speedRatio,
(o, v) => o._speedRatio = v,
defaultBindingMode: BindingMode.TwoWay);

2
src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs

@ -8,6 +8,7 @@ using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls
@ -22,6 +23,7 @@ namespace Avalonia.Controls
o => o.CellTemplate,
(o, v) => o.CellTemplate = v);
[Content]
public IDataTemplate CellTemplate
{
get { return _cellTemplate; }

70
src/Avalonia.Controls/ComboBox.cs

@ -10,6 +10,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -76,6 +77,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>();
/// <summary>
/// Defines the <see cref="IsTextSearchEnabled"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsTextSearchEnabledProperty =
AvaloniaProperty.Register<ComboBox, bool>(nameof(IsTextSearchEnabled), true);
private string _textSearchTerm = string.Empty;
private DispatcherTimer _textSearchTimer;
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@ -164,6 +173,15 @@ namespace Avalonia.Controls
set { SetValue(VerticalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets a value that specifies whether a user can jump to a value by typing.
/// </summary>
public bool IsTextSearchEnabled
{
get { return GetValue(IsTextSearchEnabledProperty); }
set { SetValue(IsTextSearchEnabledProperty, value); }
}
/// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
@ -229,6 +247,32 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc />
protected override void OnTextInput(TextInputEventArgs e)
{
if (!IsTextSearchEnabled || e.Handled)
return;
StopTextSearchTimer();
_textSearchTerm += e.Text;
bool match(ItemContainerInfo info) =>
info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
var info = ItemContainerGenerator.Containers.FirstOrDefault(match);
if (info != null)
{
SelectedIndex = info.Index;
}
StartTextSearchTimer();
e.Handled = true;
}
/// <inheritdoc/>
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
@ -426,5 +470,31 @@ namespace Avalonia.Controls
SelectedIndex = prev;
}
private void StartTextSearchTimer()
{
_textSearchTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
_textSearchTimer.Tick += TextSearchTimer_Tick;
_textSearchTimer.Start();
}
private void StopTextSearchTimer()
{
if (_textSearchTimer == null)
{
return;
}
_textSearchTimer.Stop();
_textSearchTimer.Tick -= TextSearchTimer_Tick;
_textSearchTimer = null;
}
private void TextSearchTimer_Tick(object sender, EventArgs e)
{
_textSearchTerm = string.Empty;
StopTextSearchTimer();
}
}
}

55
src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs

@ -1,4 +1,10 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
using Avalonia.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Generators
{
@ -16,11 +22,15 @@ namespace Avalonia.Controls.Generators
{
var tabItem = (TabItem)base.CreateContainer(item);
tabItem[~TabControl.TabStripPlacementProperty] = Owner[~TabControl.TabStripPlacementProperty];
tabItem.Bind(TabItem.TabStripPlacementProperty, new OwnerBinding<Dock>(
tabItem,
TabControl.TabStripPlacementProperty));
if (tabItem.HeaderTemplate == null)
{
tabItem[~HeaderedContentControl.HeaderTemplateProperty] = Owner[~ItemsControl.ItemTemplateProperty];
tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding<IDataTemplate>(
tabItem,
TabControl.ItemTemplateProperty));
}
if (tabItem.Header == null)
@ -40,10 +50,49 @@ namespace Avalonia.Controls.Generators
if (!(tabItem.Content is IControl))
{
tabItem[~ContentControl.ContentTemplateProperty] = Owner[~TabControl.ContentTemplateProperty];
tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding<IDataTemplate>(
tabItem,
TabControl.ContentTemplateProperty));
}
return tabItem;
}
private class OwnerBinding<T> : SingleSubscriberObservableBase<T>
{
private readonly TabItem _item;
private readonly StyledProperty<T> _ownerProperty;
private IDisposable _ownerSubscription;
private IDisposable _propertySubscription;
public OwnerBinding(TabItem item, StyledProperty<T> ownerProperty)
{
_item = item;
_ownerProperty = ownerProperty;
}
protected override void Subscribed()
{
_ownerSubscription = ControlLocator.Track(_item, 0, typeof(TabControl)).Subscribe(OwnerChanged);
}
protected override void Unsubscribed()
{
_ownerSubscription?.Dispose();
_ownerSubscription = null;
}
private void OwnerChanged(ILogical c)
{
_propertySubscription?.Dispose();
_propertySubscription = null;
if (c is TabControl tabControl)
{
_propertySubscription = tabControl.GetObservable(_ownerProperty)
.Subscribe(x => PublishNext(x));
}
}
}
}
}

2
src/Avalonia.Layout/UniformGridLayout.cs

@ -447,7 +447,7 @@ namespace Avalonia.Layout
// and only use the layout when to clear it when it's done.
gridState.EnsureFirstElementOwnership(context);
return new Size(desiredSize.Width, desiredSize.Height);
return desiredSize;
}
protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)

10
src/Avalonia.Layout/UniformGridLayoutState.cs

@ -44,7 +44,7 @@ namespace Avalonia.Layout
Size availableSize,
VirtualizingLayoutContext context,
double layoutItemWidth,
double LayoutItemHeight,
double layoutItemHeight,
UniformGridLayoutItemsStretch stretch,
Orientation orientation,
double minRowSpacing,
@ -63,7 +63,7 @@ namespace Avalonia.Layout
if (realizedElement != null)
{
realizedElement.Measure(availableSize);
SetSize(realizedElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
SetSize(realizedElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
_cachedFirstElement = null;
}
else
@ -78,7 +78,7 @@ namespace Avalonia.Layout
_cachedFirstElement.Measure(availableSize);
SetSize(_cachedFirstElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
SetSize(_cachedFirstElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
// See if we can move ownership to the flow algorithm. If we can, we do not need a local cache.
bool added = FlowAlgorithm.TryAddElement0(_cachedFirstElement);
@ -93,7 +93,7 @@ namespace Avalonia.Layout
private void SetSize(
ILayoutable element,
double layoutItemWidth,
double LayoutItemHeight,
double layoutItemHeight,
Size availableSize,
UniformGridLayoutItemsStretch stretch,
Orientation orientation,
@ -107,7 +107,7 @@ namespace Avalonia.Layout
}
EffectiveItemWidth = (double.IsNaN(layoutItemWidth) ? element.DesiredSize.Width : layoutItemWidth);
EffectiveItemHeight = (double.IsNaN(LayoutItemHeight) ? element.DesiredSize.Height : LayoutItemHeight);
EffectiveItemHeight = (double.IsNaN(layoutItemHeight) ? element.DesiredSize.Height : layoutItemHeight);
var availableSizeMinor = orientation == Orientation.Horizontal ? availableSize.Width : availableSize.Height;
var minorItemSpacing = orientation == Orientation.Vertical ? minRowSpacing : minColumnSpacing;

2
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -92,6 +92,8 @@ namespace Avalonia.Native
var macOpts = AvaloniaLocator.Current.GetService<MacOSPlatformOptions>();
_factory.MacOptions.SetShowInDock(macOpts?.ShowInDock != false ? 1 : 0);
_factory.MacOptions.SetDisableDefaultApplicationMenuItems(
macOpts?.DisableDefaultApplicationMenuItems == true ? 1 : 0);
}
AvaloniaLocator.CurrentMutable

2
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -38,5 +38,7 @@ namespace Avalonia
public class MacOSPlatformOptions
{
public bool ShowInDock { get; set; } = true;
public bool DisableDefaultApplicationMenuItems { get; set; }
}
}

1
src/Avalonia.Native/avn.idl

@ -528,6 +528,7 @@ interface IAvnMacOptions : IUnknown
{
HRESULT SetShowInDock(int show);
HRESULT SetApplicationTitle(char* utf8string);
HRESULT SetDisableDefaultApplicationMenuItems(bool enabled);
}
[uuid(04c1b049-1f43-418a-9159-cae627ec1367)]

3
src/Avalonia.Themes.Default/MenuItem.xaml

@ -59,8 +59,7 @@
Grid.Column="4"/>
<Popup Name="PART_Popup"
PlacementMode="Right"
IsLightDismissEnabled="True"
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
IsLightDismissEnabled="False"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"

3
src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml

@ -112,9 +112,8 @@
<Popup Name="PART_Popup"
WindowManagerAddShadowHint="False"
PlacementMode="Right"
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
HorizontalOffset="{DynamicResource MenuFlyoutSubItemPopupHorizontalOffset}"
IsLightDismissEnabled="True"
IsLightDismissEnabled="False"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{DynamicResource MenuFlyoutPresenterBackground}"
BorderBrush="{DynamicResource MenuFlyoutPresenterBorderBrush}"

36
tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs

@ -1,7 +1,9 @@
using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.UnitTests;
@ -137,5 +139,39 @@ namespace Avalonia.Controls.UnitTests
Assert.True(other.IsFocused);
}
}
[Theory]
[InlineData(-1, 2, "c", "A item", "B item", "C item")]
[InlineData(0, 1, "b", "A item", "B item", "C item")]
[InlineData(2, 2, "x", "A item", "B item", "C item")]
public void TextSearch_Should_Have_Expected_SelectedIndex(
int initialSelectedIndex,
int expectedSelectedIndex,
string searchTerm,
params string[] items)
{
using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
{
var target = new ComboBox
{
Template = GetTemplate(),
Items = items.Select(x => new ComboBoxItem { Content = x })
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = initialSelectedIndex;
var args = new TextInputEventArgs
{
Text = searchTerm,
RoutedEvent = InputElement.TextInputEvent
};
target.RaiseEvent(args);
Assert.Equal(expectedSelectedIndex, target.SelectedIndex);
}
}
}
}

1
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -374,6 +374,7 @@ namespace Avalonia.Controls.UnitTests
new TextBlock { Tag = "bar", Text = x }),
Items = new[] { "Foo" },
};
var root = new TestRoot(target);
ApplyTemplate(target);
((ContentPresenter)target.ContentPart).UpdateChild();

38
tests/Avalonia.LeakTests/ControlTests.cs

@ -313,7 +313,6 @@ namespace Avalonia.LeakTests
}
}
[Fact]
public void Slider_Is_Freed()
{
@ -347,6 +346,43 @@ namespace Avalonia.LeakTests
}
}
[Fact]
public void TabItem_Is_Freed()
{
using (Start())
{
Func<Window> run = () =>
{
var window = new Window
{
Content = new TabControl
{
Items = new[] { new TabItem() }
}
};
window.Show();
// Do a layout and make sure that TabControl and TabItem gets added to visual tree.
window.LayoutManager.ExecuteInitialLayoutPass();
var tabControl = Assert.IsType<TabControl>(window.Presenter.Child);
Assert.IsType<TabItem>(tabControl.Presenter.Panel.Children[0]);
// Clear the items and ensure the TabItem is removed.
tabControl.Items = null;
window.LayoutManager.ExecuteLayoutPass();
Assert.Empty(tabControl.Presenter.Panel.Children);
return window;
};
var result = run();
dotMemory.Check(memory =>
Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TabItem>()).ObjectsCount));
}
}
[Fact]
public void RendererIsDisposed()
{

6
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs

@ -117,9 +117,11 @@ namespace Avalonia.Markup.UnitTests.Parsers
var result = run();
result.Item1.Subscribe(x => { });
GC.Collect();
// Mono trickery
GC.Collect(2);
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(2);
Assert.Null(result.Item2.Target);
}

Loading…
Cancel
Save