RadDropDownButton MouseWheel handling for custom item

8 posts, 1 answers
  1. David
    David avatar
    9 posts
    Member since:
    Sep 2009

    Posted 17 Feb 2015 Link to this post

    We have a number of custom controls attached as RadDropDownButton items (one item per drop down button).

    Some of the controls have a scrollable list.  Expectation would be that the mousewheel would scroll the list when the control is displayed, but what happens is the popup disappears.  We've been trying to find out how to alter this behaviour, without success.  There is a similar thread (http://www.telerik.com/forums/radtreeview-in-raddropdownlist) that applies to a RadDropDownList, but the same actions for the RadDropDownButton aren't working.

    Simple reproduction of the issue is adding a RadDropDownButton to a form and initializing with this code:
    var tree = new RadTreeView();
    for (var i = 0; i < 100; i++)
    {
        tree.Nodes.Add(new RadTreeNode("Item " + i.ToString()));
    }
    var hostItem = new RadMenuHostItem(tree);
    radDropDownButton1.Items.Add(hostItem);

    Any suggestions anyone may have would be greatly appreciated!


  2. Dess
    Admin
    Dess avatar
    1609 posts

    Posted 18 Feb 2015 Link to this post

    Hello David,

    Thank you for writing.

    You can achieve similar functionality for RadDropDownButton by using the following code snippet:
    RadTreeView tree;
    MethodInfo mi;
    bool shouldClose = false;
     
    public Form1()
    {
        InitializeComponent();
         
        tree = new RadTreeView();
        tree.NodeMouseClick += tree_NodeMouseClick;
        for (var i = 0; i < 100; i++)
        {
            tree.Nodes.Add(new RadTreeNode("Item " + i.ToString()));
        }
        RadMenuHostItem hostItem = new RadMenuHostItem(tree);
        radDropDownButton1.Items.Add(hostItem);
        mi = tree.TreeViewElement.GetType().GetMethod("ProcessMouseWheel",
            BindingFlags.NonPublic | BindingFlags.Instance);
     
        this.radDropDownButton1.DropDownOpened += radDropDownButton1_DropDownOpened;
        this.radDropDownButton1.DropDownButtonElement.DropDownMenu.DropDownClosing += DropDownMenu_DropDownClosing;
    }
     
    private void tree_NodeMouseClick(object sender, RadTreeViewEventArgs e)
    {
        shouldClose = true;
        this.radDropDownButton1.DropDownButtonElement.DropDownMenu.ClosePopup(RadPopupCloseReason.Mouse);
    }
     
    private void DropDownMenu_DropDownClosing(object sender, RadPopupClosingEventArgs args)
    {
        args.Cancel = !shouldClose;
        shouldClose = false;
    }
     
    private void radDropDownButton1_DropDownOpened(object sender, EventArgs e)
    {
        RadDropDownButton ddb = sender as RadDropDownButton;
        ddb.MouseWheel -= ddle_MouseWheel;
        ddb.MouseWheel += ddle_MouseWheel;
    }
     
    private void ddle_MouseWheel(object sender, MouseEventArgs e)
    {
        if (tree != null && mi != null)
        {
            mi.Invoke(tree.TreeViewElement, new object[] { e });
        }
    }

    I hope this information helps. Should you have further questions, I would be glad to help.
     
    Regards,
    Dess
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  3. UI for WinForms is Visual Studio 2017 Ready
  4. David
    David avatar
    9 posts
    Member since:
    Sep 2009

    Posted 18 Feb 2015 in reply to Dess Link to this post

    Thank you so much, this is very helpful, it definitely brings us one step closer; when working with the code from the other thread we didn't think to have the "shouldClose" flag that keeps the dropdown open.

    The only problem now is that the behaviour of the control has changed from what users would expect.  The only way to close the control is by clicking a treeview item.  Clicking outside the control should cause the dropdown to close, but it no longer does so (because of the "shouldClose" flag).

    Looking at the CloseReason in the Closing event, the reason is "Mouse" for both clicking outside the control and for performing a mouse wheel action within the control, so there doesn't seem to be a way to change behaviour there.  (Ideally there would be a way to signal that the mouse wheel event was handled by the control and not bubbled up to the RadDropDownButton.)

    Is there any other way to prevent the dropdown from closing on mousewheel that you are aware of?
  5. David
    David avatar
    9 posts
    Member since:
    Sep 2009

    Posted 18 Feb 2015 in reply to David Link to this post

    I should also mention, most of our dropdowns are UserControls containing multiple child controls.  When the above works is applied to them, it works the same... and has the same problem of the dropdown not closing in with the mouse clicking outside the control, hitting Escape, and so forth.  (We have a completion event on these user controls, so closing through the control is no problem... it's the external close conditions that cause us problems.)

    I'm mentioning this because one thought we had was using the MouseEnter/MouseLeave and Key events to alter the state of the shouldClose flag, but that doesn't work well for a UserControl with multiple child controls.  If only the MouseEventsArgs for the wheel event had a "handled" flag.
  6. David
    David avatar
    9 posts
    Member since:
    Sep 2009

    Posted 18 Feb 2015 in reply to David Link to this post

    We found a workaround, but it's a bit involved... so we would still like to know if there is a better solution or approach.

    The issue seems to be rooted in the RadDropDownMenu class, where the OnMouseWheel method has this bit of code:
    if (element.ScrollPanel.VerticalScrollBar.Visibility != ElementVisibility.Visible)
    {
        return false;
    }

    This does seem a bit odd... and doesn't "work" like other Windows programs, menus typically do not auto-close like this when the mouse wheel is moved, but I've verified that it seems to be this was throughout the Telerik product by looking at the WinForms demo.  Perhaps there is a reason I can't see for this behaviour, or is it a bug?  The mouse wheel closes a menu, unless it's on a menu item with children in which case it does nothing... seems inconsistent.

    Note that the RadApplicationMenu has similar code to the RadDropDownMenu, but it does the opposite action:
    if (element.MenuElement.ScrollPanel.VerticalScrollBar.Visibility != ElementVisibility.Visible)
    {
         return true;
    }


    In any case, our workaround for this seems to work well; we're just not sure if it's the best solution.  It does capture the mouse wheel events, allows for a complex user control (hooking the wheel to a specific child control, which works for our use case), and doesn't interfere with the closing of the popup.  (Note there is a bit more in the test than strictly needed, since it's part of a larger general purpose usage.)

    These are the test form and test user control:
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
     
            var testControl = new TestUserControl();
            singleItemDropDownButton1.HostedControl = testControl;
            testControl.Completed += (sender, o) =>
            {
                label1.Text = (o == null ? null : o.ToString());
            };
        }
    }
     
    public partial class TestUserControl : UserControl, IPopupCompletion
    {
        public event EventHandler<object> Completed;
        private void OnCompleted(object value)
        {
            var handler = Completed;
            if (handler != null)
            {
                handler(this, value);
            }
        }
     
        public TestUserControl()
        {
            InitializeComponent();
                for (var i = 0; i < 100; i++)
            {
                radTreeView1.Nodes.Add(new RadTreeNode(String.Format("Item {0}", i)));
            }
                radTreeView1.NodeMouseClick += (sender, args) => OnCompleted(args.Node.Text);
            MouseWheel += (sender, args) => radTreeView1.CallOnMouseWheel(args);
        }
    }

    And the subclassed Telerik controls that these are using:
    public class SingleItemDropDownButton : RadDropDownButton
    {
        public Control HostedControl
        {
            set
            {
                if (Items.Count > 0)
                {
                    Items.Clear();
                }
                Items.Add(new RadMenuHostItem(value));
                ((SingleItemDropDownButtonPopup) DropDownButtonElement.DropDownMenu).HostedControl = value;
                var popupCompletion = value as IPopupCompletion;
                if (popupCompletion != null)
                {
                    popupCompletion.Completed += (sender, o) => HideDropDown();
                }
            }
        }
     
        protected override RadDropDownButtonElement CreateButtonElement()
        {
            return new SingleItemDropDownButtonElement();
        }
    }
     
    public class SingleItemDropDownButtonElement : RadDropDownButtonElement
    {
        protected override RadDropDownButtonPopup CreateDropDown()
        {
            var dropDownMenu = new SingleItemDropDownButtonPopup(this);
            if (IsInValidState(true))
            {
                dropDownMenu.ImageList = ElementTree.ComponentTreeHandler.ImageList;
            }
            return dropDownMenu;
        }
     
        protected override Type ThemeEffectiveType
        {
            get { return typeof(RadDropDownButtonElement); }
        }
    }
     
    public class SingleItemDropDownButtonPopup : RadDropDownButtonPopup
    {
        private Control _hostedControl;
        private MethodInfo _methodInfo;
     
        public Control HostedControl
        {
            set
            {
                _hostedControl = value;
                _methodInfo = value.GetType()
                                   .GetMethod("OnMouseWheel", BindingFlags.NonPublic | BindingFlags.Instance,
                                              null, new[] {typeof (MouseEventArgs)}, null);
            }
        }
         
        public SingleItemDropDownButtonPopup(RadDropDownButtonElement ownerElement)
            : base(ownerElement)
        {
        }
         
        public override bool OnMouseWheel(Control target, int delta)
        {
            if (_hostedControl == null || _methodInfo == null)
            {
                return false;
            }
            var mouseEventArgs = new MouseEventArgs(MouseButtons, 0, MousePosition.X, MousePosition.Y, delta);
            _methodInfo.Invoke(_hostedControl, new object[] {mouseEventArgs});
            return true;
        }
    }
     
    public interface IPopupCompletion
    {
        event EventHandler<object> Completed;
    }


    Again, please let us know if there is a better approach than the above.  And thank you again for your help earlier.

    Dave
  7. David
    David avatar
    9 posts
    Member since:
    Sep 2009

    Posted 18 Feb 2015 in reply to David Link to this post

    (Sorry... that observation about the differences between RadDropDownMenu and RadApplicationMenu handling of OnMouseWheel, should have instead referenced the RadApplicationMenuDropDown class.)
  8. Answer
    Dess
    Admin
    Dess avatar
    1609 posts

    Posted 23 Feb 2015 Link to this post

    Hello David,

    Thank you for writing back.

    By default, RadDropDownButton is not supposed to close automatically  when its pop up does not show all items and you perform scrolling. However, for the specific case, you should keep the pop up shown manually. Your approach seems good. Feel free to use it if it suits your requirement.

    An alternative solution that I can suggest is to handle globally the MouseDown event and manipulate the shouldClose flag if the element under the mouse is not the RadTreeView. You can have a look at the following links related to this topic:
    I hope this information helps. If you have any additional questions, please let me know.

    Regards,
    Dess
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  9. David
    David avatar
    9 posts
    Member since:
    Sep 2009

    Posted 23 Feb 2015 in reply to Dess Link to this post

    We understand.  I think we will continue to use our implementation, since it is working very well.  (We also had to override the OnKeyDown method of RadDropDownButtonPopup as well, since we wanted to handle the escape and enter keys and couldn't otherwise stop the RadDropDownButton from handling the event.)

    Thank you again Dess for all your help, it definitely put us on the right track.  Your assistance in this is greatly appreciated!
Back to Top
UI for WinForms is Visual Studio 2017 Ready