locked
Show shortcut key hint on a button just like ribbon when Alt key is pressed RRS feed

  • Question

  • I'm working on a WPF application, one of the requirement is to display the shortcut key hint on the button when Alt key is pressed. It needs to work same way as ribbon/menu does when you press Alt key, it shows hints.

    Is there a way to implement this? Any help would be appreciated.

    Thanks.

    Azeem

    Thursday, July 17, 2014 7:08 AM

Answers

  • Well you'll have to code something yourself....

    I looked at your pictures and they just confuse me.

    But anyhow.

    You need something that'll make all buttons "light up" and show the shortcut letter somehow when alt is pressed in a window.

    Then stop doing that when the user takes their finger off the button.

    I would do that by making a usercontrol which is a button with an adorner for the letter.

    The adorner allows you to make a letter or whatever appear above or below the button or just on top of the contents.

    Now you might be thinking yeah sure, how do I make em all do that easily.

    Well the way you do that is using MVVM Light messaging.

        private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
        {
          if(e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt) 
    
              {
    
    	    var msg = new AltPressed { HintVisible = Visibility.Visible};
                Messenger.Default.Send<AltPressed >(msg);
    
              }       
    
        }

    and

            private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
            {
                if (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt)
                {
                    var msg = new AltPressed {HintVisible = Visibility.Collapsed};
                    Messenger.Default.Send<AltPressed>(msg);
                }
            }

    As you can probably guess AltPressed is just a regular class with a public Visibility HintVisible . 

    In the usercontrol constructor

      Messenger.Default.Register<AltPressed >(this, (action) => ReceiveAltPressedMessage(action));

    In body of usercontrol

            private void ReceiveSetSiteMessage(AltPressed altPressed )
            {
                HintDecorator.Visibility = altPressed.HintVisible ;
            }

    MVVM light is available from NuGet and you can get a bunch of examples of it's use by googling, reading my many many recommendations... and you could start with Jesse Liberty:

    http://jesseliberty.com/2011/01/04/wpfs-mvvm-light-toolkit-soup-to-nuts-part-i/



    • Edited by Andy ONeill Thursday, July 17, 2014 6:52 PM
    • Marked as answer by Azeem Fraz Thursday, July 17, 2014 9:37 PM
    Thursday, July 17, 2014 6:46 PM
  • Now you may be thinking Adorner, what's an adorner.  Because it's not commonly used. 

    You know on the design surface when you get handles on top of a UIElement  to drag it around?

    They appear outside the actual control.

    Now if you think about it a bit that's pretty strange stuff.

    Surely everything about a control has to be inside it - because it's inside whatever Grid or whatever is the container for a control.

    Well an adorner goes in a special layer on top of EVERYTHNG - so it can be outside it's parent control.

    Where can you get an example textblock adorner then?

    using System;
    using System.Collections;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    namespace ADC.Controls
    {
        /// <summary>
        /// Renders a TextBlock in an element's adorner layer.
        /// </summary>
        public class TextBlockAdorner : Adorner
        {
            #region Data
    
            Point _location;
            private ArrayList _logicalChildren;
            private TextBlock _textBlock;
    
            #endregion // Data
    
            #region Constructor
    
            public TextBlockAdorner(UIElement adornedElement, TextBlock textBlock, Point location)
                : base(adornedElement)
            {
                _textBlock = textBlock;
                _location = location;
    
                // Register the TextBlock with the element tree so that
                // it will be rendered, and can inherit DP values.
                base.AddLogicalChild(_textBlock);
                base.AddVisualChild(_textBlock);
            }
    
            #endregion // Constructor
    
            #region UpdateTextLocation
    
            public void UpdateTextLocation(Point newLocation)
            {
                _location = newLocation;
                _textBlock.InvalidateArrange();
            }
    
            #endregion // UpdateTextLocation
    
            #region Measure/Arrange
    
            /// <summary>
            /// Allows the TextBlock to determine how big it wants to be.
            /// </summary>
            /// <param name="constraint">A limiting size for the TextBlock.</param>
            protected override Size MeasureOverride(Size constraint)
            {
                _textBlock.Measure(constraint);
                return _textBlock.DesiredSize;
            }
    
            /// <summary>
            /// Positions and sizes the TextBlock.
            /// </summary>
            /// <param name="finalSize">The actual size of the TextBlock.</param>		
            protected override Size ArrangeOverride(Size finalSize)
            {
                Rect rect = new Rect(_location, finalSize);
                _textBlock.Arrange(rect);
                return finalSize;
            }
    
            #endregion // Measure/Arrange
    
            #region Visual Children
    
            /// <summary>
            /// Required for the TextBlock to be rendered.
            /// </summary>
            protected override int VisualChildrenCount
            {
                get { return 1; }
            }
    
            /// <summary>
            /// Required for the TextBlock to be rendered.
            /// </summary>
            protected override Visual GetVisualChild(int index)
            {
                if (index != 0)
                    throw new ArgumentOutOfRangeException("index");
    
                return _textBlock;
            }
    
            #endregion // Visual Children
    
            #region Logical Children
    
            /// <summary>
            /// Required for the TextBlock to inherit property values
            /// from the logical tree, such as FontSize.
            /// </summary>
            protected override IEnumerator LogicalChildren
            {
                get
                {
                    if (_logicalChildren == null)
                    {
                        _logicalChildren = new ArrayList();
                        _logicalChildren.Add(_textBlock);
                    }
    
                    return _logicalChildren.GetEnumerator();
                }
            }
    
            #endregion // Logical Children
        }
    }

    And please don't forget to mark posts which help and or answer your question.

    • Marked as answer by Azeem Fraz Thursday, July 17, 2014 9:38 PM
    Thursday, July 17, 2014 6:58 PM

All replies

  • This would be any button, presumably.

    Handle keydown on the form and check for alt.

    xaml

    <Window ...
        PreviewKeyDown="Window_PreviewKeyDown">

    code

    private void Window_PreviewKeyDown(object sender, KeyEventArgs e) { if(e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt)

    {

    // Do something

    }

    }


    What you do with it then depends on how your form works.

    So how does your form work then?

    Is it MVVM, code behind, what?

    And what aren't you telling us

    Is this really only ever going to be the one button on one window or are there usercontrols or something involved?


    • Edited by Andy ONeill Thursday, July 17, 2014 8:48 AM
    Thursday, July 17, 2014 8:47 AM
  • Thanks for the response.

    Yes I am using MVVM and I want a generic solution (maybe behavior) to be applied on buttons at different places of the application. So it's not just one button.

    If i implement it myself, as you suggested, I'll have to code for all the scenarios like when user clicks somewhere else (mouse down event) or presses escape key, etc. to hide the hint. And if user presses the hint key, execute the event/command.

    Thanks.

    Azeem

    Update: I want something like the following


    • Edited by Azeem Fraz Thursday, July 17, 2014 4:44 PM
    Thursday, July 17, 2014 4:12 PM
  • Well you'll have to code something yourself....

    I looked at your pictures and they just confuse me.

    But anyhow.

    You need something that'll make all buttons "light up" and show the shortcut letter somehow when alt is pressed in a window.

    Then stop doing that when the user takes their finger off the button.

    I would do that by making a usercontrol which is a button with an adorner for the letter.

    The adorner allows you to make a letter or whatever appear above or below the button or just on top of the contents.

    Now you might be thinking yeah sure, how do I make em all do that easily.

    Well the way you do that is using MVVM Light messaging.

        private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
        {
          if(e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt) 
    
              {
    
    	    var msg = new AltPressed { HintVisible = Visibility.Visible};
                Messenger.Default.Send<AltPressed >(msg);
    
              }       
    
        }

    and

            private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
            {
                if (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt)
                {
                    var msg = new AltPressed {HintVisible = Visibility.Collapsed};
                    Messenger.Default.Send<AltPressed>(msg);
                }
            }

    As you can probably guess AltPressed is just a regular class with a public Visibility HintVisible . 

    In the usercontrol constructor

      Messenger.Default.Register<AltPressed >(this, (action) => ReceiveAltPressedMessage(action));

    In body of usercontrol

            private void ReceiveSetSiteMessage(AltPressed altPressed )
            {
                HintDecorator.Visibility = altPressed.HintVisible ;
            }

    MVVM light is available from NuGet and you can get a bunch of examples of it's use by googling, reading my many many recommendations... and you could start with Jesse Liberty:

    http://jesseliberty.com/2011/01/04/wpfs-mvvm-light-toolkit-soup-to-nuts-part-i/



    • Edited by Andy ONeill Thursday, July 17, 2014 6:52 PM
    • Marked as answer by Azeem Fraz Thursday, July 17, 2014 9:37 PM
    Thursday, July 17, 2014 6:46 PM
  • Now you may be thinking Adorner, what's an adorner.  Because it's not commonly used. 

    You know on the design surface when you get handles on top of a UIElement  to drag it around?

    They appear outside the actual control.

    Now if you think about it a bit that's pretty strange stuff.

    Surely everything about a control has to be inside it - because it's inside whatever Grid or whatever is the container for a control.

    Well an adorner goes in a special layer on top of EVERYTHNG - so it can be outside it's parent control.

    Where can you get an example textblock adorner then?

    using System;
    using System.Collections;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    namespace ADC.Controls
    {
        /// <summary>
        /// Renders a TextBlock in an element's adorner layer.
        /// </summary>
        public class TextBlockAdorner : Adorner
        {
            #region Data
    
            Point _location;
            private ArrayList _logicalChildren;
            private TextBlock _textBlock;
    
            #endregion // Data
    
            #region Constructor
    
            public TextBlockAdorner(UIElement adornedElement, TextBlock textBlock, Point location)
                : base(adornedElement)
            {
                _textBlock = textBlock;
                _location = location;
    
                // Register the TextBlock with the element tree so that
                // it will be rendered, and can inherit DP values.
                base.AddLogicalChild(_textBlock);
                base.AddVisualChild(_textBlock);
            }
    
            #endregion // Constructor
    
            #region UpdateTextLocation
    
            public void UpdateTextLocation(Point newLocation)
            {
                _location = newLocation;
                _textBlock.InvalidateArrange();
            }
    
            #endregion // UpdateTextLocation
    
            #region Measure/Arrange
    
            /// <summary>
            /// Allows the TextBlock to determine how big it wants to be.
            /// </summary>
            /// <param name="constraint">A limiting size for the TextBlock.</param>
            protected override Size MeasureOverride(Size constraint)
            {
                _textBlock.Measure(constraint);
                return _textBlock.DesiredSize;
            }
    
            /// <summary>
            /// Positions and sizes the TextBlock.
            /// </summary>
            /// <param name="finalSize">The actual size of the TextBlock.</param>		
            protected override Size ArrangeOverride(Size finalSize)
            {
                Rect rect = new Rect(_location, finalSize);
                _textBlock.Arrange(rect);
                return finalSize;
            }
    
            #endregion // Measure/Arrange
    
            #region Visual Children
    
            /// <summary>
            /// Required for the TextBlock to be rendered.
            /// </summary>
            protected override int VisualChildrenCount
            {
                get { return 1; }
            }
    
            /// <summary>
            /// Required for the TextBlock to be rendered.
            /// </summary>
            protected override Visual GetVisualChild(int index)
            {
                if (index != 0)
                    throw new ArgumentOutOfRangeException("index");
    
                return _textBlock;
            }
    
            #endregion // Visual Children
    
            #region Logical Children
    
            /// <summary>
            /// Required for the TextBlock to inherit property values
            /// from the logical tree, such as FontSize.
            /// </summary>
            protected override IEnumerator LogicalChildren
            {
                get
                {
                    if (_logicalChildren == null)
                    {
                        _logicalChildren = new ArrayList();
                        _logicalChildren.Add(_textBlock);
                    }
    
                    return _logicalChildren.GetEnumerator();
                }
            }
    
            #endregion // Logical Children
        }
    }

    And please don't forget to mark posts which help and or answer your question.

    • Marked as answer by Azeem Fraz Thursday, July 17, 2014 9:38 PM
    Thursday, July 17, 2014 6:58 PM
  • Thanks Andy, you have guided me to the right direction. I've marked your posts as answers. I'll give it a try and see if it works for me. Thanks again.
    Thursday, July 17, 2014 9:39 PM