Ver código fonte

Added implementation of ContextMenus.

danwalmsley 9 anos atrás
pai
commit
f32f293f30

+ 14 - 2
samples/XamlTestApplicationPcl/Views/MainWindow.paml

@@ -38,8 +38,20 @@
           </TabItem>
           <TabItem Header="Text">
               <StackPanel Margin="10" Gap="4">
-                  <TextBlock Text="TextBlock" FontWeight="Medium" FontSize="20" Foreground="#212121">
-                    <ContextMenu.Menu></ContextMenu.Menu>
+                  <TextBlock Text="TextBlock with ContextMenu" FontWeight="Medium" FontSize="20" Foreground="#212121">
+                    <ContextMenu.ContextMenu>
+                      <ContextMenu>
+                        <MenuItem Header="Testing 1" />
+                        <MenuItem Header="Testing 2" />
+                        <MenuItem Header="Testing 3" />
+                        <MenuItem Header="Testing 4">
+                          <MenuItem Header="SubItem 1" />
+                          <MenuItem Header="SubItem 2" />
+                          <MenuItem Header="SubItem 3" />
+                          <MenuItem Header="SubItem 4" />
+                        </MenuItem>
+                      </ContextMenu>
+                    </ContextMenu.ContextMenu>
                   </TextBlock>
                   <TextBlock Text="A control for displaying text."
                               FontSize="13"

+ 148 - 6
src/Perspex.Controls/ContextMenu.cs

@@ -1,20 +1,162 @@
 namespace Perspex.Controls
 {
+    using Input;
+    using Interactivity;
+    using LogicalTree;
     using Primitives;
+    using System;
+    using System.Reactive.Linq;
 
     public class ContextMenu : SelectingItemsControl
     {
-        public static readonly AttachedProperty<ContextMenu> MenuProperty =
-       PerspexProperty.RegisterAttached<ContextMenu, TextBlock, ContextMenu>("Menu");
+        private bool _isOpen;
 
-        public static ContextMenu GetMenu(TextBlock element)
+        /// <summary>
+        /// Defines the ContextMenu.Menu attached property.
+        /// </summary>
+        //public static readonly AttachedProperty<object> ContextMenuProperty =
+        //    PerspexProperty.RegisterAttached<ContextMenu, Control, object>("Menu");
+
+        /// <summary>
+        /// The popup window used to display the active context menu.
+        /// </summary>
+        private static PopupRoot s_popup;
+
+        /// <summary>
+        /// The control that the currently visible context menu is attached to.
+        /// </summary>
+        private static Control s_current;
+
+        /// <summary>
+        /// Initializes static members of the <see cref="ToolTip"/> class.
+        /// </summary>
+        static ContextMenu()
+        {
+            ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
+
+            MenuItem.ClickEvent.AddClassHandler<ContextMenu>(x => x.OnContextMenuClick);
+        }
+
+        /// <summary>
+        /// Gets the value of the ToolTip.Tip attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <returns>
+        /// The content to be displayed in the control's tooltip.
+        /// </returns>
+        public static object GetContextMenu(Control element)
+        {
+            return element.GetValue(ContextMenuProperty);
+        }
+
+        /// <summary>
+        /// Sets the value of the ToolTip.Tip attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <param name="value">The content to be displayed in the control's tooltip.</param>
+        public static void SetContextMenu(Control element, object value)
+        {
+            element.SetValue(ContextMenuProperty, value);
+        }
+
+        /// <summary>
+        /// called when the <see cref="ContextMenuProperty"/> property changes on a control.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        private static void ContextMenuChanged(PerspexPropertyChangedEventArgs e)
+        {
+            var control = (Control)e.Sender;
+
+            if (e.OldValue != null)
+            {
+                control.PointerReleased -= ControlPointerReleased;
+            }
+
+            if (e.NewValue != null)
+            {
+                control.PointerReleased += ControlPointerReleased;
+            }
+        }
+
+        /// <summary>
+        /// Called when a submenu is clicked somewhere in the menu.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        private void OnContextMenuClick(RoutedEventArgs e)
         {
-            return element.GetValue(MenuProperty);
+            Hide();
+            FocusManager.Instance.Focus(null);
+            e.Handled = true;
         }
 
-        public static void SetMenu(TextBlock element, ContextMenu value)
+        /// <summary>
+        /// Closes the menu.
+        /// </summary>
+        public void Hide()
         {
-            element.SetValue(MenuProperty, value);
+            foreach (MenuItem i in this.GetLogicalChildren())
+            {
+                i.IsSubMenuOpen = false;
+            }
+
+            if (s_popup != null && s_popup.IsVisible)
+            {
+                s_popup.Hide();
+            }
+
+            SelectedIndex = -1;
+
+            _isOpen = false;
+        }
+
+        /// <summary>
+        /// Shows a tooltip for the specified control.
+        /// </summary>
+        /// <param name="control">The control.</param>
+        private static void Show(Control control)
+        {
+            if (control != null)
+            {
+                if (s_popup == null)
+                {
+                    s_popup = new PopupRoot
+                    {
+                        Content = new ToolTip(),
+                    };
+
+                    ((ISetLogicalParent)s_popup).SetParent(control);
+                }
+
+                var cp = MouseDevice.Instance?.GetPosition(control);
+                var position = control.PointToScreen(cp ?? new Point(0, 0));
+
+                ((ToolTip)s_popup.Content).Content = GetContextMenu(control);
+                s_popup.Position = position;
+                s_popup.Show();
+
+                s_current = control;
+
+                control.ContextMenu._isOpen = true;
+            }
+        }
+
+        private static void ControlPointerReleased(object sender, PointerReleasedEventArgs e)
+        {
+            var control = (Control)sender;
+
+            if (e.MouseButton == MouseButton.Right)
+            {
+                if(control.ContextMenu._isOpen)
+                {
+                    control.ContextMenu.Hide();
+                }
+
+                Show(control);
+            }
+            else
+            {
+                control.ContextMenu.Hide();
+            }
         }
     }
 }

+ 16 - 0
src/Perspex.Controls/Control.cs

@@ -65,6 +65,13 @@ namespace Perspex.Controls
         public static readonly StyledProperty<ITemplatedControl> TemplatedParentProperty =
             PerspexProperty.Register<Control, ITemplatedControl>(nameof(TemplatedParent), inherits: true);
 
+        /// <summary>
+        /// Defines the <see cref="ContextMenu"/> property.
+        /// </summary>
+        public static readonly StyledProperty<ContextMenu> ContextMenuProperty =
+            PerspexProperty.Register<Control, ContextMenu>(nameof(ContextMenu));        
+
+
         /// <summary>
         /// Event raised when an element wishes to be scrolled into view.
         /// </summary>
@@ -204,6 +211,15 @@ namespace Perspex.Controls
         /// </summary>
         public IControl Parent => _parent;
 
+        /// <summary>
+        /// Gets or sets a context menu to the control.
+        /// </summary>
+        public ContextMenu ContextMenu
+        {
+            get { return GetValue(ContextMenuProperty); }
+            set { SetValue(ContextMenuProperty, value); }
+        }
+
         /// <summary>
         /// Gets or sets a user-defined object attached to the control.
         /// </summary>

+ 2 - 2
src/Perspex.Themes.Default/ContextMenu.paml

@@ -5,8 +5,8 @@
               BorderBrush="{TemplateBinding BorderBrush}"
               BorderThickness="{TemplateBinding BorderThickness}"
               Padding="{TemplateBinding Padding}">
-        <ItemsPresenter Name="PART_ItemsPresenter" 
-                        Items="{TemplateBinding Items}" 
+        <ItemsPresenter Name="PART_ItemsPresenter"
+                        Items="{TemplateBinding Items}"
                         ItemsPanel="{TemplateBinding ItemsPanel}"
                         KeyboardNavigation.TabNavigation="Continue"/>
       </Border>