locked
how to make a accelerator key for Xamarin.Forms button ? RRS feed

  • Question

  • User383552 posted

    I want make a accelerator key for my Xamarin.Forms button on UWP. My app is a chat app, on Android the user like to press the Send Button. But on UWP when it run on desktop, the user like to use the accelerator key to trigger the Send Button event. Can I do this for UWP in Xamarin.Forms ?

    Sunday, June 30, 2019 12:00 AM

Answers

  • User74518 posted

    Follow the docs to implement accelerator in UWP, something like (from the top of my head, not tested):

    ```` private KeyboardAccelerator _sendMessageAccelerator;

    protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e);

    if (e.OldElement != null)
    {
        if (_sendMessageAccelerator != null)
            _sendMessageAccelerator.Invoked -= SendMessageAcceleratorInvoked;
    
        this.KeyboardAccelerators.Clear();
    }
    
    if (e.NewElement != null)
    {
        _sendMessageAccelerator = new KeyboardAccelerator
        {
            Modifiers = VirtualKeyModifiers.Control,
            Key = VirtualKey.Enter,
        };
    
        _sendMessageAccelerator.Invoked += SendMessageAcceleratorInvoked;
    
        this.KeyboardAccelerators.Add(_sendMessageAccelerator);
    }
    

    }

    private void SendMessageAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) { args.Handled = true;

    // Send your message here...
    

    } ````

    You got the idea.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Monday, July 1, 2019 4:25 AM
  • User89714 posted

    @lihuipeng49 said: private void Control_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { if (e.Key == Windows.System.VirtualKey.Enter) { MainCtrl.SendChatMessage(Control.Text, false); Control.Text = ""; } }

    The code upon can finish the work. But there is another thing need to fix.

    The Enter press also do the line break. After the run of my code, the Editor will have 2 empty lines.

    I haven't tried it, but try setting e.Handled = true

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, July 2, 2019 10:26 AM
  • User383552 posted

    This is the final solution.

    MainPage.xaml

    <local:MyEditor Placeholder="??????" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand"
                        HeightRequest="50" MaxLength="2000"
                       x:Name="sendText"/>
    

    MyEditor.cs

    namespace ChannelChat
    {
        public class MyEditor : Editor
        {
        }
    }
    

    MyEditorRenderer.cs

    namespace ChannelChat.UWP
    {
        public class MyEditorRenderer : EditorRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
            {
                base.OnElementChanged(e);
    
                if (Control == null)
                {
                    return;
                }
    
                if (e.OldElement != null)
                {
                    Control.PreviewKeyDown -= Control_PreviewKeyDown;
                }
    
                if (e.NewElement != null)
                {
                    Control.PreviewKeyDown += Control_PreviewKeyDown;
                }
            }
    
            private void Control_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
            {
                if (e.Key == Windows.System.VirtualKey.Enter)
                {
                    e.Handled = true;
                    MainCtrl.SendChatMessage(Control.Text, false);
                    Control.Text = "";
                }
            }
        }
    }
    
    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, July 2, 2019 10:56 AM

All replies

  • User74518 posted

    Create a custom renderer for Button in the UWP project and implement the Accelerator functionality

    Sunday, June 30, 2019 12:27 AM
  • User89714 posted

    Or a custom renderer implementing https://docs.microsoft.com/en-us/windows/uwp/design/input/access-keys (this is how I did it - I just wanted Alt+key combinations)

    Sunday, June 30, 2019 8:48 AM
  • User383552 posted

    To take care of user habits, the accelerator key need be 'Ctrl + Enter'. I think use custom renderer for a ContentPage maybe is OK like Button. I am trying the method of @Amar_Bait . Because I am new to Xamarin, so I need to think how to write the code.

    My app named ChannelChat. I think I need write code to 2 project, ChannelChat and ChannelChat.UWP.

    Sunday, June 30, 2019 1:59 PM
  • User89714 posted

    @lihuipeng49 said: My app named ChannelChat. I think I need write code to 2 project, ChannelChat and ChannelChat.UWP.

    Yes, that's correct. The custom renderer will be in ChannelChat.UWP. You will also want to subclass Button in ChannelChat to add one or more properties that the custom renderer will access to get the key combination (key + modifier) to use,. You might want to add further properties for specifying the method to invoke, if not just triggering the Button's Command.

    Sunday, June 30, 2019 5:17 PM
  • User74518 posted

    If you're doing a custom renderer for your whole ContentPage instead of the send button, then no need to write code in the shared project, just UWP.

    Xamarin docs have exactly this use case, check it, try to implement it, and if you have any question don't hesitate.

    Sunday, June 30, 2019 5:20 PM
  • User383552 posted

    @Amar_Bait
    I successfully create a custom renderer. And the program running can hit my break point. But the next , I don't how to detect the 'Enter' or 'Ctrl+Enter' key press at the custom renderer.

    Monday, July 1, 2019 2:55 AM
  • User74518 posted

    Follow the docs to implement accelerator in UWP, something like (from the top of my head, not tested):

    ```` private KeyboardAccelerator _sendMessageAccelerator;

    protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e);

    if (e.OldElement != null)
    {
        if (_sendMessageAccelerator != null)
            _sendMessageAccelerator.Invoked -= SendMessageAcceleratorInvoked;
    
        this.KeyboardAccelerators.Clear();
    }
    
    if (e.NewElement != null)
    {
        _sendMessageAccelerator = new KeyboardAccelerator
        {
            Modifiers = VirtualKeyModifiers.Control,
            Key = VirtualKey.Enter,
        };
    
        _sendMessageAccelerator.Invoked += SendMessageAcceleratorInvoked;
    
        this.KeyboardAccelerators.Add(_sendMessageAccelerator);
    }
    

    }

    private void SendMessageAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) { args.Handled = true;

    // Send your message here...
    

    } ````

    You got the idea.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Monday, July 1, 2019 4:25 AM
  • User383552 posted

    This is worked:

                            Modifiers = VirtualKeyModifiers.Control,
                            Key = VirtualKey.R,
    

    This is not:

                            Modifiers = VirtualKeyModifiers.Control,
                            Key = VirtualKey.Enter,
    

    I think the Editor catch the Enter key first caught the problem.

    My final need is press 'Ctrl+Enter' in the Editor can do the send operate.

    Monday, July 1, 2019 6:15 PM
  • User74518 posted

    In that case you need to create a custom renderer for the Editor and implement the same code (this.Control. KeyboardAccelerators instead of this.KeyboardAccelerators), or maybe just use Control.KeyDown event instead of KeyboardAccelerators

    Monday, July 1, 2019 7:34 PM
  • User383552 posted

    I like to use Control.KeyDown with PageRenderer for ContentPage. I this this simple than create a custom renderer for Editor. So, can you give me a code sample for the Control.KeyDown? The final, when user press Enter key in Editor, the program do the send operate, is OK.

    Monday, July 1, 2019 7:45 PM
  • User74518 posted

    Sorry I can't give you any more code, I gave you more than enough so you can understand the basics of custom renderers and how to write platform specific code. I can help you, not write code for you. So you need to actually write code before asking questions.

    Just Google how to create a custom renderer for a control (Xamarin documentation is very good) and there are tons of articles and forum topics on this subject. Same thing for KeyDown implementation (it didn't change from the year I started programming for Windows on VB5) just Google for eg. "how to capture enter key in textbox uwp" I didn't check the results, but I'm sure you will find something.

    Monday, July 1, 2019 8:04 PM
  • User383552 posted

    Thanks! I know the google keyword is ok.

    Monday, July 1, 2019 8:06 PM
  • User383552 posted
    namespace ChannelChat
    {
        public class MyEditor : Editor
        {
        }
    }
    
    
    [assembly: ExportRenderer(typeof(MyEditor), typeof(MyEditorRenderer))]
    namespace ChannelChat.UWP
    {
        public class MyEditorRenderer : EditorRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
            {
                base.OnElementChanged(e);
    
                if (Control == null)
                {
                    return;
                }
    
                if (e.OldElement != null)
                {
                    Control.KeyDown -= Control_KeyDown;
                }
    
                if (e.NewElement != null)
                {
                    Control.KeyDown += Control_KeyDown;
                }
            }
    
            private void Control_KeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
            {
                System.Diagnostics.Debug.WriteLine(e.Key);
    
                if (e.Key == Windows.System.VirtualKey.Enter)
                {
                    // only the Enter key can't go here
                }
            }
        }
    }
    

    I find the way to create custom renderer for the Editor, and I success! But the Enter problem also. KeyDown event for any key can catch by the Control_KeyDown , only Enter key can't.

    Tuesday, July 2, 2019 7:24 AM
  • User383552 posted

    The accelerator key for the Editor I had try too. Result is also only Enter key event can't catch.

    Tuesday, July 2, 2019 7:28 AM
  • User74518 posted

    Try VirtualKey.Return instead of Enter if it exists? Also try to listening to Control.PreviewKeyDown instead of Control.KeyDown event

    Tuesday, July 2, 2019 8:03 AM
  • User383552 posted

    There is no VirtualKey.Return in Xamarin. I use Control.PreviewKeyDown, it can catch the Enter press event.

            private void Control_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
            {
                if (e.Key == Windows.System.VirtualKey.Enter)
                {
                    MainCtrl.SendChatMessage(Control.Text, false);
                    Control.Text = "";
                }
            }
    

    The code upon can finish the work. But there is another thing need to fix.

    The Enter press also do the line break. After the run of my code, the Editor will have 2 empty lines.

    I need find some way to block the Enter event post to the default process system. I had study Xamarin just 2 month, I am not use C# before. Even I don't know where to find the Xamarin official document for the EditorRenderer and PreviewKeyDown.

    I had use MFC and C++ to develop for years. The MFC use windows message pump to send event, so it can block the key press event at the PreKeyDown event process function. But in Xamarin can't do this. The PreviewKeyDown function have no return param, so I think it can't use the function return value to do some work to block the event post to next system.

    Tuesday, July 2, 2019 10:05 AM
  • User89714 posted

    @lihuipeng49 said: private void Control_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { if (e.Key == Windows.System.VirtualKey.Enter) { MainCtrl.SendChatMessage(Control.Text, false); Control.Text = ""; } }

    The code upon can finish the work. But there is another thing need to fix.

    The Enter press also do the line break. After the run of my code, the Editor will have 2 empty lines.

    I haven't tried it, but try setting e.Handled = true

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, July 2, 2019 10:26 AM
  • User383552 posted

    Yes, use e.Handled = true it worked. Thank your for help me. Thanks Amar_Bait help me step by step.

    Tuesday, July 2, 2019 10:50 AM
  • User383552 posted

    This is the final solution.

    MainPage.xaml

    <local:MyEditor Placeholder="??????" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand"
                        HeightRequest="50" MaxLength="2000"
                       x:Name="sendText"/>
    

    MyEditor.cs

    namespace ChannelChat
    {
        public class MyEditor : Editor
        {
        }
    }
    

    MyEditorRenderer.cs

    namespace ChannelChat.UWP
    {
        public class MyEditorRenderer : EditorRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
            {
                base.OnElementChanged(e);
    
                if (Control == null)
                {
                    return;
                }
    
                if (e.OldElement != null)
                {
                    Control.PreviewKeyDown -= Control_PreviewKeyDown;
                }
    
                if (e.NewElement != null)
                {
                    Control.PreviewKeyDown += Control_PreviewKeyDown;
                }
            }
    
            private void Control_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
            {
                if (e.Key == Windows.System.VirtualKey.Enter)
                {
                    e.Handled = true;
                    MainCtrl.SendChatMessage(Control.Text, false);
                    Control.Text = "";
                }
            }
        }
    }
    
    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, July 2, 2019 10:56 AM