diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..dc57a73f48
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at steven@avaloniaui.net. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/src/Avalonia.Animation/Properties/AssemblyInfo.cs b/src/Avalonia.Animation/Properties/AssemblyInfo.cs
index eb38a66a84..8523b9537d 100644
--- a/src/Avalonia.Animation/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Animation/Properties/AssemblyInfo.cs
@@ -10,3 +10,4 @@ using System.Runtime.CompilerServices;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Animators")]
[assembly: InternalsVisibleTo("Avalonia.LeakTests")]
+[assembly: InternalsVisibleTo("Avalonia.Animation.UnitTests")]
diff --git a/src/Avalonia.Animation/TransitionInstance.cs b/src/Avalonia.Animation/TransitionInstance.cs
index 10ea6bf523..a69ad50a4b 100644
--- a/src/Avalonia.Animation/TransitionInstance.cs
+++ b/src/Avalonia.Animation/TransitionInstance.cs
@@ -28,7 +28,7 @@ namespace Avalonia.Animation
private void TimerTick(TimeSpan t)
{
- var interpVal = (double)t.Ticks / _duration.Ticks;
+ var interpVal = _duration.Ticks == 0 ? 1d : (double)t.Ticks / _duration.Ticks;
// Clamp interpolation value.
if (interpVal >= 1d | interpVal < 0d)
diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index 41b57b6e70..027028480c 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -159,6 +159,11 @@ namespace Avalonia.Utilities
/// The clamped value.
public static int Clamp(int val, int min, int max)
{
+ if (min > max)
+ {
+ throw new ArgumentException($"{min} cannot be greater than {max}.");
+ }
+
if (val < min)
{
return min;
diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs
index 1bdbd4ca7c..e06c5996c9 100644
--- a/src/Avalonia.Base/ValueStore.cs
+++ b/src/Avalonia.Base/ValueStore.cs
@@ -57,7 +57,8 @@ namespace Avalonia
{
if (priority == (int)BindingPriority.LocalValue)
{
- _propertyValues.SetValue(property, Validate(property, value));
+ Validate(property, ref value);
+ _propertyValues.SetValue(property, value);
Changed(property, priority, v, value);
return;
}
@@ -78,7 +79,8 @@ namespace Avalonia
if (priority == (int)BindingPriority.LocalValue)
{
- _propertyValues.AddValue(property, Validate(property, value));
+ Validate(property, ref value);
+ _propertyValues.AddValue(property, value);
Changed(property, priority, AvaloniaProperty.UnsetValue, value);
return;
}
@@ -166,16 +168,14 @@ namespace Avalonia
validate2);
}
- private object Validate(AvaloniaProperty property, object value)
+ private void Validate(AvaloniaProperty property, ref object value)
{
var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType());
if (validate != null && value != AvaloniaProperty.UnsetValue)
{
- return validate(_owner, value);
+ value = validate(_owner, value);
}
-
- return value;
}
private DeferredSetter GetDeferredSetter(AvaloniaProperty property)
diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index d9be9171ed..3b644191c2 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -125,9 +125,8 @@ namespace Avalonia.Controls
});
// Copy-pasted because we can't call extension methods due to generic constraints
- var lifetime = new ClassicDesktopStyleApplicationLifetime(Instance) {ShutdownMode = ShutdownMode.OnMainWindowClose};
- Instance.ApplicationLifetime = lifetime;
- SetupWithoutStarting();
+ var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = ShutdownMode.OnMainWindowClose};
+ SetupWithLifetime(lifetime);
lifetime.Start(Array.Empty());
}
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
index 2533191ae4..6dd5b8cc81 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
@@ -5,12 +5,12 @@ using System.Threading;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
+using Avalonia.Threading;
namespace Avalonia.Controls.ApplicationLifetimes
{
public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable
{
- private readonly Application _app;
private int _exitCode;
private CancellationTokenSource _cts;
private bool _isShuttingDown;
@@ -34,12 +34,11 @@ namespace Avalonia.Controls.ApplicationLifetimes
_activeLifetime?._windows.Add((Window)sender);
}
- public ClassicDesktopStyleApplicationLifetime(Application app)
+ public ClassicDesktopStyleApplicationLifetime()
{
if (_activeLifetime != null)
throw new InvalidOperationException(
"Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed");
- _app = app;
_activeLifetime = this;
}
@@ -103,7 +102,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args));
_cts = new CancellationTokenSource();
MainWindow?.Show();
- _app.Run(_cts.Token);
+ Dispatcher.UIThread.MainLoop(_cts.Token);
Environment.ExitCode = _exitCode;
return _exitCode;
}
@@ -124,7 +123,7 @@ namespace Avalonia
this T builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
where T : AppBuilderBase, new()
{
- var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode};
+ var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = shutdownMode};
builder.SetupWithLifetime(lifetime);
return lifetime.Start(args);
}
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index ce4358648b..64db832a81 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -704,7 +704,7 @@ namespace Avalonia.Controls
added.Add(e.NewValue);
}
- OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, removed, added));
+ OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, added, removed));
}
///
diff --git a/src/Avalonia.Controls/ColumnDefinition.cs b/src/Avalonia.Controls/ColumnDefinition.cs
index 9c520c434e..293b6326d6 100644
--- a/src/Avalonia.Controls/ColumnDefinition.cs
+++ b/src/Avalonia.Controls/ColumnDefinition.cs
@@ -26,6 +26,16 @@ namespace Avalonia.Controls
public static readonly StyledProperty WidthProperty =
AvaloniaProperty.Register(nameof(Width), new GridLength(1, GridUnitType.Star));
+ ///
+ /// Initializes static members of the class.
+ ///
+ static ColumnDefinition()
+ {
+ AffectsParentMeasure(MinWidthProperty, MaxWidthProperty);
+
+ WidthProperty.Changed.AddClassHandler(OnUserSizePropertyChanged);
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -68,7 +78,6 @@ namespace Avalonia.Controls
}
set
{
- Parent?.InvalidateMeasure();
SetValue(MaxWidthProperty, value);
}
}
@@ -84,7 +93,6 @@ namespace Avalonia.Controls
}
set
{
- Parent?.InvalidateMeasure();
SetValue(MinWidthProperty, value);
}
}
@@ -100,7 +108,6 @@ namespace Avalonia.Controls
}
set
{
- Parent?.InvalidateMeasure();
SetValue(WidthProperty, value);
}
}
diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs
index a68fe1265f..e4ae777453 100644
--- a/src/Avalonia.Controls/DefinitionBase.cs
+++ b/src/Avalonia.Controls/DefinitionBase.cs
@@ -7,9 +7,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-
-using Avalonia;
-using Avalonia.Collections;
using Avalonia.Utilities;
namespace Avalonia.Controls
@@ -50,6 +47,8 @@ namespace Avalonia.Controls
}
}
}
+
+ Parent?.InvalidateMeasure();
}
///
@@ -63,6 +62,8 @@ namespace Avalonia.Controls
_sharedState.RemoveMember(this);
_sharedState = null;
}
+
+ Parent?.InvalidateMeasure();
}
///
@@ -114,6 +115,36 @@ namespace Avalonia.Controls
}
}
+ ///
+ /// Notifies parent or size scope that definition size has been changed.
+ ///
+ internal static void OnUserSizePropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (definition.Parent == null)
+ {
+ return;
+ }
+
+ if (definition._sharedState != null)
+ {
+ definition._sharedState.Invalidate();
+ }
+ else
+ {
+ GridUnitType oldUnitType = ((GridLength)e.OldValue).GridUnitType;
+ GridUnitType newUnitType = ((GridLength)e.NewValue).GridUnitType;
+
+ if (oldUnitType != newUnitType)
+ {
+ definition.Parent.Invalidate();
+ }
+ else
+ {
+ definition.Parent.InvalidateMeasure();
+ }
+ }
+ }
+
///
/// Returns true if this definition is a part of shared group.
///
@@ -730,5 +761,22 @@ namespace Avalonia.Controls
SharedSizeGroupProperty.Changed.AddClassHandler(OnSharedSizeGroupPropertyChanged);
PrivateSharedSizeScopeProperty.Changed.AddClassHandler(OnPrivateSharedSizeScopePropertyChanged);
}
+
+ ///
+ /// Marks a property on a definition as affecting the parent grid's measurement.
+ ///
+ /// The properties.
+ protected static void AffectsParentMeasure(params AvaloniaProperty[] properties)
+ {
+ void Invalidate(AvaloniaPropertyChangedEventArgs e)
+ {
+ (e.Sender as DefinitionBase)?.Parent?.InvalidateMeasure();
+ }
+
+ foreach (var property in properties)
+ {
+ property.Changed.Subscribe(Invalidate);
+ }
+ }
}
}
diff --git a/src/Avalonia.Controls/DesktopApplicationExtensions.cs b/src/Avalonia.Controls/DesktopApplicationExtensions.cs
index ff6705cdc0..ddd4e57a40 100644
--- a/src/Avalonia.Controls/DesktopApplicationExtensions.cs
+++ b/src/Avalonia.Controls/DesktopApplicationExtensions.cs
@@ -50,6 +50,7 @@ namespace Avalonia.Controls
/// On desktop-style platforms runs the application's main loop with custom CancellationToken
/// without setting a lifetime.
///
+ /// The application.
/// The token to track.
public static void Run(this Application app, CancellationToken token)
{
diff --git a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
index a6a64e570b..d99648a158 100644
--- a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
@@ -48,11 +48,6 @@ namespace Avalonia.Controls.Generators
tabItem[~ContentControl.ContentTemplateProperty] = Owner[~TabControl.ContentTemplateProperty];
}
- if (tabItem.Content == null)
- {
- tabItem[~ContentControl.ContentProperty] = tabItem[~StyledElement.DataContextProperty];
- }
-
return tabItem;
}
}
diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs
index 8ecfe349f8..23c1cd4794 100644
--- a/src/Avalonia.Controls/Grid.cs
+++ b/src/Avalonia.Controls/Grid.cs
@@ -6,6 +6,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -178,6 +179,7 @@ namespace Avalonia.Controls
if (_data == null) { _data = new ExtendedData(); }
_data.ColumnDefinitions = value;
_data.ColumnDefinitions.Parent = this;
+ InvalidateMeasure();
}
}
@@ -198,6 +200,7 @@ namespace Avalonia.Controls
if (_data == null) { _data = new ExtendedData(); }
_data.RowDefinitions = value;
_data.RowDefinitions.Parent = this;
+ InvalidateMeasure();
}
}
@@ -569,6 +572,15 @@ namespace Avalonia.Controls
return (arrangeSize);
}
+ ///
+ ///
+ ///
+ protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ CellsStructureDirty = true;
+ base.ChildrenChanged(sender, e);
+ }
+
///
/// Invalidates grid caches and makes the grid dirty for measure.
///
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index b027da6d0c..bf22f0a08a 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -359,6 +359,12 @@ namespace Avalonia.Controls
UpdateItemCount();
RemoveControlItemsFromLogicalChildren(oldValue);
AddControlItemsToLogicalChildren(newValue);
+
+ if (Presenter != null)
+ {
+ Presenter.Items = newValue;
+ }
+
SubscribeToItems(newValue);
}
@@ -370,6 +376,8 @@ namespace Avalonia.Controls
/// The event args.
protected virtual void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
+ UpdateItemCount();
+
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
@@ -381,7 +389,7 @@ namespace Avalonia.Controls
break;
}
- UpdateItemCount();
+ Presenter?.ItemsChanged(e);
var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs
index 42311dc781..c4acf1ebef 100644
--- a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs
@@ -1,12 +1,19 @@
// 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 System.Collections;
+using System.Collections.Specialized;
+
namespace Avalonia.Controls.Presenters
{
public interface IItemsPresenter : IPresenter
{
+ IEnumerable Items { get; set; }
+
IPanel Panel { get; }
+ void ItemsChanged(NotifyCollectionChangedEventArgs e);
+
void ScrollIntoView(object item);
}
}
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
index 0f0cdc37cf..ef1f277162 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
+++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
@@ -63,7 +63,7 @@ namespace Avalonia.Controls.Presenters
_itemsSubscription?.Dispose();
_itemsSubscription = null;
- if (_createdPanel && value is INotifyCollectionChanged incc)
+ if (!IsHosted && _createdPanel && value is INotifyCollectionChanged incc)
{
_itemsSubscription = incc.WeakSubscribe(ItemsCollectionChanged);
}
@@ -130,6 +130,8 @@ namespace Avalonia.Controls.Presenters
private set;
}
+ protected bool IsHosted => TemplatedParent is IItemsPresenterHost;
+
///
public override sealed void ApplyTemplate()
{
@@ -144,6 +146,15 @@ namespace Avalonia.Controls.Presenters
{
}
+ ///
+ void IItemsPresenter.ItemsChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (Panel != null)
+ {
+ ItemsChanged(e);
+ }
+ }
+
///
/// Creates the for the control.
///
@@ -215,7 +226,7 @@ namespace Avalonia.Controls.Presenters
_createdPanel = true;
- if (_itemsSubscription == null && Items is INotifyCollectionChanged incc)
+ if (!IsHosted && _itemsSubscription == null && Items is INotifyCollectionChanged incc)
{
_itemsSubscription = incc.WeakSubscribe(ItemsCollectionChanged);
}
diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs
index 74a6d288f4..caaf58b25b 100644
--- a/src/Avalonia.Controls/Primitives/PopupRoot.cs
+++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs
@@ -41,6 +41,8 @@ namespace Avalonia.Controls.Primitives
///
/// Initializes a new instance of the class.
///
+ /// The popup parent.
+ /// The popup implementation.
///
/// The dependency resolver to use. If null the default dependency resolver will be used.
///
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 7fddee1012..b6ae567123 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -302,13 +302,24 @@ namespace Avalonia.Controls.Primitives
///
protected override void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
- base.ItemsCollectionChanged(sender, e);
-
if (_updateCount > 0)
{
+ base.ItemsCollectionChanged(sender, e);
return;
}
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ _selection.ItemsInserted(e.NewStartingIndex, e.NewItems.Count);
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ _selection.ItemsRemoved(e.OldStartingIndex, e.OldItems.Count);
+ break;
+ }
+
+ base.ItemsCollectionChanged(sender, e);
+
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
@@ -318,14 +329,12 @@ namespace Avalonia.Controls.Primitives
}
else
{
- _selection.ItemsInserted(e.NewStartingIndex, e.NewItems.Count);
UpdateSelectedItem(_selection.First(), false);
}
break;
case NotifyCollectionChangedAction.Remove:
- _selection.ItemsRemoved(e.OldStartingIndex, e.OldItems.Count);
UpdateSelectedItem(_selection.First(), false);
ResetSelectedItems();
break;
@@ -1088,9 +1097,15 @@ namespace Avalonia.Controls.Primitives
}
else
{
- SelectedIndex = _updateSelectedIndex != int.MinValue ?
- _updateSelectedIndex :
- AlwaysSelected ? 0 : -1;
+ if (_updateSelectedIndex != int.MinValue)
+ {
+ SelectedIndex = _updateSelectedIndex;
+ }
+
+ if (AlwaysSelected && SelectedIndex == -1)
+ {
+ SelectedIndex = 0;
+ }
}
}
}
diff --git a/src/Avalonia.Controls/RowDefinition.cs b/src/Avalonia.Controls/RowDefinition.cs
index 1f2f738670..85e7ed6519 100644
--- a/src/Avalonia.Controls/RowDefinition.cs
+++ b/src/Avalonia.Controls/RowDefinition.cs
@@ -26,6 +26,16 @@ namespace Avalonia.Controls
public static readonly StyledProperty HeightProperty =
AvaloniaProperty.Register(nameof(Height), new GridLength(1, GridUnitType.Star));
+ ///
+ /// Initializes static members of the class.
+ ///
+ static RowDefinition()
+ {
+ AffectsParentMeasure(MaxHeightProperty, MinHeightProperty);
+
+ HeightProperty.Changed.AddClassHandler(OnUserSizePropertyChanged);
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -68,7 +78,6 @@ namespace Avalonia.Controls
}
set
{
- Parent?.InvalidateMeasure();
SetValue(MaxHeightProperty, value);
}
}
@@ -84,7 +93,6 @@ namespace Avalonia.Controls
}
set
{
- Parent?.InvalidateMeasure();
SetValue(MinHeightProperty, value);
}
}
@@ -100,7 +108,6 @@ namespace Avalonia.Controls
}
set
{
- Parent?.InvalidateMeasure();
SetValue(HeightProperty, value);
}
}
diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs
index 384a9db0cf..d90729e8a5 100644
--- a/src/Avalonia.Controls/ToolTipService.cs
+++ b/src/Avalonia.Controls/ToolTipService.cs
@@ -1,6 +1,7 @@
using System;
using Avalonia.Input;
using Avalonia.Threading;
+using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@@ -79,7 +80,10 @@ namespace Avalonia.Controls
{
StopTimer();
- ToolTip.SetIsOpen(control, true);
+ if ((control as IVisual).IsAttachedToVisualTree)
+ {
+ ToolTip.SetIsOpen(control, true);
+ }
}
private void Close(Control control)
diff --git a/src/Avalonia.Controls/WrapPanel.cs b/src/Avalonia.Controls/WrapPanel.cs
index 7c88401615..d06a71a9f8 100644
--- a/src/Avalonia.Controls/WrapPanel.cs
+++ b/src/Avalonia.Controls/WrapPanel.cs
@@ -42,7 +42,7 @@ namespace Avalonia.Controls
///
static WrapPanel()
{
- AffectsMeasure(OrientationProperty);
+ AffectsMeasure(OrientationProperty, ItemWidthProperty, ItemHeightProperty);
}
///
diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs
index c84596b913..4dcf0eee53 100644
--- a/src/Avalonia.Input/MouseDevice.cs
+++ b/src/Avalonia.Input/MouseDevice.cs
@@ -14,13 +14,14 @@ namespace Avalonia.Input
///
/// Represents a mouse device.
///
- public class MouseDevice : IMouseDevice
+ public class MouseDevice : IMouseDevice, IDisposable
{
private int _clickCount;
private Rect _lastClickRect;
private ulong _lastClickTime;
private readonly Pointer _pointer;
+ private bool _disposed;
public MouseDevice(Pointer pointer = null)
{
@@ -126,7 +127,9 @@ namespace Avalonia.Input
{
Contract.Requires(e != null);
- var mouse = (IMouseDevice)e.Device;
+ var mouse = (MouseDevice)e.Device;
+ if(mouse._disposed)
+ return;
Position = e.Root.PointToScreen(e.Position);
var props = CreateProperties(e);
@@ -441,5 +444,11 @@ namespace Avalonia.Input
el = (IInputElement)el.VisualParent;
}
}
+
+ public void Dispose()
+ {
+ _disposed = true;
+ _pointer?.Dispose();
+ }
}
}
diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Input/Pointer.cs
index 80d803abb1..819d231b31 100644
--- a/src/Avalonia.Input/Pointer.cs
+++ b/src/Avalonia.Input/Pointer.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Input
{
if (Captured != null)
Captured.DetachedFromVisualTree -= OnCaptureDetached;
- var oldCapture = control;
+ var oldCapture = Captured;
Captured = control;
PlatformCapture(control);
if (oldCapture != null)
diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs
index b231c9fff4..d6ad836f37 100644
--- a/src/Avalonia.Input/TouchDevice.cs
+++ b/src/Avalonia.Input/TouchDevice.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Input.Raw;
@@ -11,10 +12,11 @@ namespace Avalonia.Input
/// This class is supposed to be used on per-toplevel basis, don't use a shared one
///
///
- public class TouchDevice : IInputDevice
+ public class TouchDevice : IInputDevice, IDisposable
{
- Dictionary _pointers = new Dictionary();
-
+ private readonly Dictionary _pointers = new Dictionary();
+ private bool _disposed;
+
KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) =>
(KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask);
@@ -28,6 +30,8 @@ namespace Avalonia.Input
public void ProcessRawEvent(RawInputEventArgs ev)
{
+ if(_disposed)
+ return;
var args = (RawTouchEventArgs)ev;
if (!_pointers.TryGetValue(args.TouchPointId, out var pointer))
{
@@ -82,6 +86,17 @@ namespace Avalonia.Input
}
+
+ public void Dispose()
+ {
+ if(_disposed)
+ return;
+ var values = _pointers.Values.ToList();
+ _pointers.Clear();
+ _disposed = true;
+ foreach (var p in values)
+ p.Dispose();
+ }
}
}
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index ddb71b61bb..fab3ce36b8 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -95,7 +95,6 @@ namespace Avalonia.Native
.Bind().ToConstant(new CursorFactory(_factory.CreateCursorFactory()))
.Bind().ToSingleton()
.Bind().ToConstant(KeyboardDevice)
- .Bind().ToConstant(MouseDevice)
.Bind().ToConstant(this)
.Bind().ToConstant(this)
.Bind().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index fe7458d583..7f1fab4b1c 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Native
private object _syncRoot = new object();
private bool _deferredRendering = false;
private bool _gpu = false;
- private readonly IMouseDevice _mouse;
+ private readonly MouseDevice _mouse;
private readonly IKeyboardDevice _keyboard;
private readonly IStandardCursorFactory _cursorFactory;
private Size _savedLogicalSize;
@@ -38,7 +38,7 @@ namespace Avalonia.Native
_deferredRendering = opts.UseDeferredRendering;
_keyboard = AvaloniaLocator.Current.GetService();
- _mouse = AvaloniaLocator.Current.GetService();
+ _mouse = new MouseDevice();
_cursorFactory = AvaloniaLocator.Current.GetService();
}
@@ -142,6 +142,7 @@ namespace Avalonia.Native
{
n?.Dispose();
}
+ _parent._mouse.Dispose();
}
void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();
diff --git a/src/Avalonia.Themes.Default/ContextMenu.xaml b/src/Avalonia.Themes.Default/ContextMenu.xaml
index 53d7c5abb4..75f8f7c23d 100644
--- a/src/Avalonia.Themes.Default/ContextMenu.xaml
+++ b/src/Avalonia.Themes.Default/ContextMenu.xaml
@@ -11,19 +11,11 @@
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
-
-
-
diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml
index a794d15577..93989d3782 100644
--- a/src/Avalonia.Themes.Default/MenuItem.xaml
+++ b/src/Avalonia.Themes.Default/MenuItem.xaml
@@ -4,14 +4,14 @@