diff --git a/build/Microsoft.CSharp.props b/build/Microsoft.CSharp.props
index d0fa63bc3e..4f738dd254 100644
--- a/build/Microsoft.CSharp.props
+++ b/build/Microsoft.CSharp.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/Microsoft.Reactive.Testing.props b/build/Microsoft.Reactive.Testing.props
index 5ee6df708e..777bc4bb53 100644
--- a/build/Microsoft.Reactive.Testing.props
+++ b/build/Microsoft.Reactive.Testing.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props
index 11afefa8ad..acdfdd215a 100644
--- a/build/ReactiveUI.props
+++ b/build/ReactiveUI.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/Rx.props b/build/Rx.props
index 7078e31195..f4affcacac 100644
--- a/build/Rx.props
+++ b/build/Rx.props
@@ -1,9 +1,9 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/packages.cake b/packages.cake
index d633230189..9defa3004c 100644
--- a/packages.cake
+++ b/packages.cake
@@ -303,7 +303,7 @@ public class Packages
{
new NuSpecContent { Source = "Avalonia.Android.dll", Target = "lib/MonoAndroid10" }
},
- BasePath = context.Directory("./src/Android/Avalonia.Android/bin/" + parameters.DirSuffix + "/monoandroid44/"),
+ BasePath = context.Directory("./src/Android/Avalonia.Android/bin/" + parameters.DirSuffix + "/monoandroid44/MonoAndroid44/"),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
diff --git a/readme.md b/readme.md
index 345ad7fe9b..f345cbd9df 100644
--- a/readme.md
+++ b/readme.md
@@ -35,6 +35,9 @@ Install-Package Avalonia.Desktop
Try out the latest build of Avalonia available for download here:
https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master/artifacts
+or use nightly build feeds as described here:
+https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed
+
## Documentation
As mentioned above, Avalonia is still in beta and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/docs/quickstart/) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia).
diff --git a/samples/ControlCatalog/Pages/RadioButtonPage.xaml b/samples/ControlCatalog/Pages/RadioButtonPage.xaml
index 0882817a9a..9525f6187e 100644
--- a/samples/ControlCatalog/Pages/RadioButtonPage.xaml
+++ b/samples/ControlCatalog/Pages/RadioButtonPage.xaml
@@ -22,6 +22,19 @@
Three States: Option 3
Disabled
+
+ Group A: Option 1
+ Group A: Disabled
+ Group B: Option 1
+ Group B: Option 3
+
+
+ Group A: Option 2
+ Group B: Option 2
+ Group B: Option 4
+
\ No newline at end of file
diff --git a/scripts/ReplaceNugetCache.ps1 b/scripts/ReplaceNugetCache.ps1
index a03d442bff..6de50f978d 100644
--- a/scripts/ReplaceNugetCache.ps1
+++ b/scripts/ReplaceNugetCache.ps1
@@ -1,5 +1,6 @@
+copy ..\samples\ControlCatalog.Desktop\bin\Debug\net461\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\net461\
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.skia\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Gtk3.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Win32.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Skia.dll ~\.nuget\packages\avalonia.skia\$args\lib\netstandard2.0\
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index cf7acb3e8a..aa2a7a7a8e 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -69,7 +69,7 @@ namespace Avalonia.Threading
///
public void RunJobs()
{
- _jobRunner?.RunJobs(null);
+ _jobRunner.RunJobs(null);
}
///
@@ -82,14 +82,21 @@ namespace Avalonia.Threading
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
Contract.Requires(action != null);
- return _jobRunner?.InvokeAsync(action, priority);
+ return _jobRunner.InvokeAsync(action, priority);
+ }
+
+ ///
+ public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal)
+ {
+ Contract.Requires(function != null);
+ return _jobRunner.InvokeAsync(function, priority);
}
///
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
Contract.Requires(action != null);
- _jobRunner?.Post(action, priority);
+ _jobRunner.Post(action, priority);
}
///
diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs
index 4009dcdeab..1fdc9da5fe 100644
--- a/src/Avalonia.Base/Threading/IDispatcher.cs
+++ b/src/Avalonia.Base/Threading/IDispatcher.cs
@@ -28,12 +28,17 @@ namespace Avalonia.Threading
void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
///
- /// Post action that will be invoked on main thread
+ /// Posts an action that will be invoked on the dispatcher thread.
///
/// The method.
/// The priority with which to invoke the method.
- // TODO: The naming of this method is confusing: the Async suffix usually means return a task.
- // Remove this and rename InvokeTaskAsync as InvokeAsync. See #816.
Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
+
+ ///
+ /// Posts a function that will be invoked on the dispatcher thread.
+ ///
+ /// The method.
+ /// The priority with which to invoke the method.
+ Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal);
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Base/Threading/JobRunner.cs b/src/Avalonia.Base/Threading/JobRunner.cs
index c2040a0982..b922370e84 100644
--- a/src/Avalonia.Base/Threading/JobRunner.cs
+++ b/src/Avalonia.Base/Threading/JobRunner.cs
@@ -14,32 +14,16 @@ namespace Avalonia.Threading
///
internal class JobRunner
{
-
-
private IPlatformThreadingInterface _platform;
- private Queue[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1)
- .Select(_ => new Queue()).ToArray();
+ private readonly Queue[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1)
+ .Select(_ => new Queue()).ToArray();
public JobRunner(IPlatformThreadingInterface platform)
{
_platform = platform;
}
- Job GetNextJob(DispatcherPriority minimumPriority)
- {
- for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--)
- {
- var q = _queues[c];
- lock (q)
- {
- if (q.Count > 0)
- return q.Dequeue();
- }
- }
- return null;
- }
-
///
/// Runs continuations pushed on the loop.
///
@@ -52,24 +36,8 @@ namespace Avalonia.Threading
var job = GetNextJob(minimumPriority);
if (job == null)
return;
-
- if (job.TaskCompletionSource == null)
- {
- job.Action();
- }
- else
- {
- try
- {
- job.Action();
- job.TaskCompletionSource.SetResult(null);
- }
- catch (Exception e)
- {
- job.TaskCompletionSource.SetException(e);
- }
- }
+ job.Run();
}
}
@@ -83,7 +51,20 @@ namespace Avalonia.Threading
{
var job = new Job(action, priority, false);
AddJob(job);
- return job.TaskCompletionSource.Task;
+ return job.Task;
+ }
+
+ ///
+ /// Invokes a method on the main loop.
+ ///
+ /// The method.
+ /// The priority with which to invoke the method.
+ /// A task that can be used to track the method's execution.
+ public Task InvokeAsync(Func function, DispatcherPriority priority)
+ {
+ var job = new Job(function, priority);
+ AddJob(job);
+ return job.Task;
}
///
@@ -105,9 +86,9 @@ namespace Avalonia.Threading
_platform = AvaloniaLocator.Current.GetService();
}
- private void AddJob(Job job)
+ private void AddJob(IJob job)
{
- var needWake = false;
+ bool needWake;
var queue = _queues[(int) job.Priority];
lock (queue)
{
@@ -118,38 +99,129 @@ namespace Avalonia.Threading
_platform?.Signal(job.Priority);
}
+ private IJob GetNextJob(DispatcherPriority minimumPriority)
+ {
+ for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--)
+ {
+ var q = _queues[c];
+ lock (q)
+ {
+ if (q.Count > 0)
+ return q.Dequeue();
+ }
+ }
+ return null;
+ }
+
+ private interface IJob
+ {
+ ///
+ /// Gets the job priority.
+ ///
+ DispatcherPriority Priority { get; }
+
+ ///
+ /// Runs the job.
+ ///
+ void Run();
+ }
+
///
/// A job to run.
///
- private class Job
+ private sealed class Job : IJob
{
+ ///
+ /// The method to call.
+ ///
+ private readonly Action _action;
+ ///
+ /// The task completion source.
+ ///
+ private readonly TaskCompletionSource
/// The movement direction.
/// The control from which movement begins.
+ /// Whether to wrap around when the first or last item is reached.
/// The control.
- IInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from)
+ IInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from, bool wrap)
{
// TODO: Implement this
return null;
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 13f00bdc87..0accb284b6 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -1,16 +1,18 @@
+using System;
+using System.Reactive.Linq;
+using System.Linq;
+using System.ComponentModel;
+using Avalonia.Controls.Platform;
+using System.Collections.Generic;
+using Avalonia.Input;
+using Avalonia.LogicalTree;
+using Avalonia.Controls.Primitives;
+
namespace Avalonia.Controls
{
- using Input;
- using Interactivity;
- using LogicalTree;
- using Primitives;
- using System;
- using System.Reactive.Linq;
- using System.Linq;
- using System.ComponentModel;
-
- public class ContextMenu : SelectingItemsControl
+ public class ContextMenu : SelectingItemsControl, IMenu
{
+ private readonly IMenuInteractionHandler _interaction;
private bool _isOpen;
private Popup _popup;
@@ -20,6 +22,25 @@ namespace Avalonia.Controls
public static readonly DirectProperty IsOpenProperty =
AvaloniaProperty.RegisterDirect(nameof(IsOpen), o => o.IsOpen);
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ContextMenu()
+ {
+ _interaction = AvaloniaLocator.Current.GetService() ??
+ new DefaultMenuInteractionHandler();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The menu iteraction handler.
+ public ContextMenu(IMenuInteractionHandler interactionHandler)
+ {
+ Contract.Requires(interactionHandler != null);
+
+ _interaction = interactionHandler;
+ }
///
/// Initializes static members of the class.
@@ -27,8 +48,6 @@ namespace Avalonia.Controls
static ContextMenu()
{
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
-
- MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick, handledEventsToo: true);
}
///
@@ -36,6 +55,36 @@ namespace Avalonia.Controls
///
public bool IsOpen => _isOpen;
+ ///
+ IMenuInteractionHandler IMenu.InteractionHandler => _interaction;
+
+ ///
+ IMenuItem IMenuElement.SelectedItem
+ {
+ get
+ {
+ var index = SelectedIndex;
+ return (index != -1) ?
+ (IMenuItem)ItemContainerGenerator.ContainerFromIndex(index) :
+ null;
+ }
+ set
+ {
+ SelectedIndex = ItemContainerGenerator.IndexFromContainer(value);
+ }
+ }
+
+ ///
+ IEnumerable IMenuElement.SubItems
+ {
+ get
+ {
+ return ItemContainerGenerator.Containers
+ .Select(x => x.ContainerControl)
+ .OfType();
+ }
+ }
+
///
/// Occurs when the value of the
///
@@ -50,7 +99,6 @@ namespace Avalonia.Controls
///
public event CancelEventHandler ContextMenuClosing;
-
///
/// Called when the property changes on a control.
///
@@ -71,62 +119,53 @@ namespace Avalonia.Controls
}
///
- /// Called when a submenu is clicked somewhere in the menu.
+ /// Opens the menu.
///
- /// The event args.
- private void OnContextMenuClick(RoutedEventArgs e)
- {
- Hide();
- FocusManager.Instance.Focus(null);
- e.Handled = true;
- }
+ public void Open() => Open(null);
///
- /// Closes the menu.
+ /// Opens a context menu on the specified control.
///
- public void Hide()
+ /// The control.
+ public void Open(Control control)
{
- if (_popup != null && _popup.IsVisible)
+ if (_popup == null)
{
- _popup.IsOpen = false;
+ _popup = new Popup()
+ {
+ PlacementMode = PlacementMode.Pointer,
+ PlacementTarget = control,
+ StaysOpen = false,
+ ObeyScreenEdges = true
+ };
+
+ _popup.Closed += PopupClosed;
+ _interaction.Attach(this);
}
- SelectedIndex = -1;
+ ((ISetLogicalParent)_popup).SetParent(control);
+ _popup.Child = this;
+ _popup.IsOpen = true;
- SetAndRaise(IsOpenProperty, ref _isOpen, false);
+ SetAndRaise(IsOpenProperty, ref _isOpen, true);
}
///
- /// Shows a context menu for the specified control.
+ /// Closes the menu.
///
- /// The control.
- private void Show(Control control)
+ public void Close()
{
- if (control != null)
+ if (_popup != null && _popup.IsVisible)
{
- if (_popup == null)
- {
- _popup = new Popup()
- {
- PlacementMode = PlacementMode.Pointer,
- PlacementTarget = control,
- StaysOpen = false,
- ObeyScreenEdges = true
- };
-
- _popup.Closed += PopupClosed;
- }
-
- ((ISetLogicalParent)_popup).SetParent(control);
- _popup.Child = this;
+ _popup.IsOpen = false;
+ }
- _popup.IsOpen = true;
+ SelectedIndex = -1;
- SetAndRaise(IsOpenProperty, ref _isOpen, true);
- }
+ SetAndRaise(IsOpenProperty, ref _isOpen, false);
}
- private static void PopupClosed(object sender, EventArgs e)
+ private void PopupClosed(object sender, EventArgs e)
{
var contextMenu = (sender as Popup)?.Child as ContextMenu;
@@ -152,7 +191,7 @@ namespace Avalonia.Controls
if (contextMenu.CancelClosing())
return;
- control.ContextMenu.Hide();
+ control.ContextMenu.Close();
e.Handled = true;
}
@@ -161,7 +200,7 @@ namespace Avalonia.Controls
if (contextMenu.CancelOpening())
return;
- contextMenu.Show(control);
+ contextMenu.Open(control);
e.Handled = true;
}
}
@@ -179,5 +218,10 @@ namespace Avalonia.Controls
ContextMenuOpening?.Invoke(this, eventArgs);
return eventArgs.Cancel;
}
+
+ bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/src/Avalonia.Controls/IMenu.cs b/src/Avalonia.Controls/IMenu.cs
new file mode 100644
index 0000000000..e118ec043c
--- /dev/null
+++ b/src/Avalonia.Controls/IMenu.cs
@@ -0,0 +1,21 @@
+using System;
+using Avalonia.Controls.Platform;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Represents a or .
+ ///
+ public interface IMenu : IMenuElement
+ {
+ ///
+ /// Gets the menu interaction handler.
+ ///
+ IMenuInteractionHandler InteractionHandler { get; }
+
+ ///
+ /// Gets a value indicating whether the menu is open.
+ ///
+ bool IsOpen { get; }
+ }
+}
diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs
new file mode 100644
index 0000000000..c9fc04dcc8
--- /dev/null
+++ b/src/Avalonia.Controls/IMenuElement.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Input;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Represents an or .
+ ///
+ public interface IMenuElement : IControl
+ {
+ ///
+ /// Gets or sets the currently selected submenu item.
+ ///
+ IMenuItem SelectedItem { get; set; }
+
+ ///
+ /// Gets the submenu items.
+ ///
+ IEnumerable SubItems { get; }
+
+ ///
+ /// Opens the menu or menu item.
+ ///
+ void Open();
+
+ ///
+ /// Closes the menu or menu item.
+ ///
+ void Close();
+
+ ///
+ /// Moves the submenu selection in the specified direction.
+ ///
+ /// The direction.
+ /// Whether to wrap after the first or last item.
+ /// True if the selection was moved; otherwise false.
+ bool MoveSelection(NavigationDirection direction, bool wrap);
+ }
+}
diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs
new file mode 100644
index 0000000000..2657b1949f
--- /dev/null
+++ b/src/Avalonia.Controls/IMenuItem.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Represents a .
+ ///
+ public interface IMenuItem : IMenuElement
+ {
+ ///
+ /// Gets or sets a value that indicates whether the item has a submenu.
+ ///
+ bool HasSubMenu { get; }
+
+ ///
+ /// Gets a value indicating whether the mouse is currently over the menu item's submenu.
+ ///
+ bool IsPointerOverSubMenu { get; }
+
+ ///
+ /// Gets or sets a value that indicates whether the submenu of the is
+ /// open.
+ ///
+ bool IsSubMenuOpen { get; set; }
+
+ ///
+ /// Gets a value that indicates whether the is a top-level main menu item.
+ ///
+ bool IsTopLevel { get; }
+
+ ///
+ /// Gets the parent .
+ ///
+ new IMenuElement Parent { get; }
+
+ ///
+ /// Raises a click event on the menu item.
+ ///
+ void RaiseClick();
+ }
+}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 3cb997f615..676e0af3de 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -15,6 +15,7 @@ using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
+using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@@ -323,6 +324,46 @@ namespace Avalonia.Controls
LogicalChildren.RemoveAll(toRemove);
}
+ ///
+ /// Handles directional navigation within the .
+ ///
+ /// The key events.
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ if (!e.Handled)
+ {
+ var focus = FocusManager.Instance;
+ var direction = e.Key.ToNavigationDirection();
+ var container = Presenter?.Panel as INavigableContainer;
+
+ if (container == null ||
+ focus.Current == null ||
+ direction == null ||
+ direction.Value.IsTab())
+ {
+ return;
+ }
+
+ var current = focus.Current
+ .GetSelfAndVisualAncestors()
+ .OfType()
+ .FirstOrDefault(x => x.VisualParent == container);
+
+ if (current != null)
+ {
+ var next = GetNextControl(container, direction.Value, current, false);
+
+ if (next != null)
+ {
+ focus.Focus(next, NavigationMethod.Directional);
+ e.Handled = true;
+ }
+ }
+ }
+
+ base.OnKeyDown(e);
+ }
+
///
/// Caled when the property changes.
///
@@ -335,6 +376,7 @@ namespace Avalonia.Controls
var oldValue = e.OldValue as IEnumerable;
var newValue = e.NewValue as IEnumerable;
+ UpdateItemCount();
RemoveControlItemsFromLogicalChildren(oldValue);
AddControlItemsToLogicalChildren(newValue);
SubscribeToItems(newValue);
@@ -358,10 +400,8 @@ namespace Avalonia.Controls
RemoveControlItemsFromLogicalChildren(e.OldItems);
break;
}
-
- int? count = (Items as IList)?.Count;
- if (count != null)
- ItemCount = (int)count;
+
+ UpdateItemCount();
var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
@@ -445,5 +485,44 @@ namespace Avalonia.Controls
// TODO: Rebuild the item containers.
}
}
+
+ private void UpdateItemCount()
+ {
+ if (Items == null)
+ {
+ ItemCount = 0;
+ }
+ else if (Items is IList list)
+ {
+ ItemCount = list.Count;
+ }
+ else
+ {
+ ItemCount = Items.Count();
+ }
+ }
+
+ protected static IInputElement GetNextControl(
+ INavigableContainer container,
+ NavigationDirection direction,
+ IInputElement from,
+ bool wrap)
+ {
+ IInputElement result;
+
+ do
+ {
+ result = container.GetControl(direction, from, wrap);
+
+ if (result?.Focusable == true)
+ {
+ return result;
+ }
+
+ from = result;
+ } while (from != null);
+
+ return null;
+ }
}
}
diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs
index 994af9dab8..edd7ed489e 100644
--- a/src/Avalonia.Controls/Menu.cs
+++ b/src/Avalonia.Controls/Menu.cs
@@ -2,30 +2,23 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Collections.Generic;
using System.Linq;
-using System.Reactive.Disposables;
using Avalonia.Controls.Generators;
+using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
-using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
-using Avalonia.Rendering;
namespace Avalonia.Controls
{
///
/// A top-level menu control.
///
- public class Menu : SelectingItemsControl, IFocusScope, IMainMenu
+ public class Menu : SelectingItemsControl, IFocusScope, IMainMenu, IMenu
{
- ///
- /// Defines the default items panel used by a .
- ///
- private static readonly ITemplate DefaultPanel =
- new FuncTemplate(() => new StackPanel { Orientation = Orientation.Horizontal });
-
///
/// Defines the property.
///
@@ -34,12 +27,42 @@ namespace Avalonia.Controls
nameof(IsOpen),
o => o.IsOpen);
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent MenuOpenedEvent =
+ RoutedEvent.Register