1
votes

Is it possible to add a menu flyout to a RichEditBox in UWP in addition to already available flyout items. But i see that there is no flyout property in richeditbox. So is it possible to add one? If Yes please provide the steps to add a menuFlyout. Thanks in advance!

1

1 Answers

2
votes

Ofcourse its possible.

 <RichEditBox GotFocus="RichEditBox_GotFocus">
                    <FlyoutBase.AttachedFlyout>
                        <Flyout>
                            <Button Content="test"/>
                        </Flyout>
                    </FlyoutBase.AttachedFlyout>
                </RichEditBox>

 private void RichEditBox_GotFocus(object sender, RoutedEventArgs e)
        {
            FlyoutBase.ShowAttachedFlyout((sender as RichEditBox));
        }

Update

I tried implementing your requirement without custom flyout.

Observations

1 RightTapped event doesnt fire for Textbox. Not sure why. There is ScrollViewer in ControlTemplate of TextBox(May be that is the reason why RightTapped event not firing in Textbox)So I added RightTapped event for Scrollviewer.

2.

   private async void ContentElement_RightTapped(object sender, RightTappedRoutedEventArgs e)
            {
                FlyoutBase.ShowAttachedFlyout(textbox);
                 await  Task.Delay(1000);
                FlyoutPresenter canvas = testbutton.FindParent<FlyoutPresenter>();
                var popup = canvas.Parent as Popup;

                double x = e.GetPosition(e.OriginalSource as UIElement).X;
                Debug.WriteLine(x);
                popup.IsOpen = false;
                popup.SetValue(Canvas.LeftProperty, e.GetPosition(e.OriginalSource as  UIElement).X);
                popup.IsOpen = true;
            }


 <Style x:Key="RichEditBoxStyle1" TargetType="RichEditBox">
    ...
     <ScrollViewer x:Name="ContentElement" IsRightTapEnabled="True" RightTapped="ContentElement_RightTapped"  AutomationProperties.AccessibilityView="Raw" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled"/>
...
    </Style>
    <RichEditBox x:Name="textbox" Width="400" HorizontalAlignment="Left"  Grid.Row="3" RightTapped="RichEditBox_RightTapped" IsRightTapEnabled="True" GotFocus="RichEditBox_GotFocus" Style="{StaticResource RichEditBoxStyle1}">
                        <FlyoutBase.AttachedFlyout>
                               <Flyout >
                                <Button Content="test" x:Name="testbutton"            Click="Button_Click"/>   
                            </Flyout>
                        </FlyoutBase.AttachedFlyout>                        
                    </RichEditBox>

In above code ContentElement_RightTapped is RightTapped Event of ScrollViewer. To add this You have to edit Style of TextBox. Basically flyout conatins popup that i'm getting using VisualTreeHelper. And setting position(which i get from the event) of PopUp. But somehow PopUp is not getting set to the exact position.

So second option is to go for custom flyout. Refer this Link for how to implement one . You can take code from there Here is the modified code code

     public class TemplatedFlyout:DependencyObject
        {
            public TemplatedFlyout()
            {

            }
            Popup popUp;
            FrameworkElement senderElement;
            FrameworkElement frameworkContent;
            bool keyboardOpen = false;
            InputPane keyboard;
            public void Initialization(UIElement sender)
            {
                senderElement = sender as FrameworkElement;
                senderElement.DataContextChanged += (s, e) =>frameworkContent.DataContext = senderElement.DataContext;
                popUp = new Popup()
                {
                    ChildTransitions = new Windows.UI.Xaml.Media.Animation.TransitionCollection(),
                    IsLightDismissEnabled = true
                };
                popUp.ChildTransitions.Add(new PaneThemeTransition() { Edge = EdgeTransitionLocation.Bottom });
                frameworkContent = Template as FrameworkElement;
                frameworkContent.DataContext = senderElement.DataContext;
                popUp.Child = frameworkContent;
                FocusKeeper();
             if(sender is RichEditBox || sender is TextBox)
                {
                    (sender as FrameworkElement).Loaded += TemplatedFlyout_Loaded;
                }
             //else
             //       sender.Tapped += (s, e) => Show(e);
            }

            private void TemplatedFlyout_Loaded(object sender, RoutedEventArgs e)
            {
                (sender as FrameworkElement).FindElementInVisualTree<ScrollViewer>().RightTapped += (s, e1) => Show(e1);
            }

           public static readonly DependencyProperty TemplateProperty = DependencyProperty.Register(nameof(Template), typeof(object), typeof(TemplatedFlyout), new PropertyMetadata(null));
            public object Template
            {
                get { return (object) GetValue(TemplateProperty); }
                set { SetValue(TemplateProperty, value); }
            }       
             void FocusKeeper()
            {
                keyboard = InputPane.GetForCurrentView();
                popUp.Closed += (s, e) =>
                {
                    if(keyboardOpen)
                    {
                        popUp.IsOpen = true;
                    }
                };
                if(keyboard!=null)
                {
                    keyboard.Showing += (s, e) => keyboardOpen = true;
                    keyboard.Hiding += (s, e) => keyboardOpen = false;
                }
            }
            public async void Show(RightTappedRoutedEventArgs args)
            {
                try
                {
                   popUp.RequestedTheme = ((Window.Current.Content as Frame).Content as Page).RequestedTheme;
                    popUp.IsOpen = true;
                    frameworkContent.UpdateLayout();
                    var top = Math.Abs(senderElement.ActualHeight+frameworkContent.ActualHeight+10-Window.Current.Bounds.Height);
                    var left = args.GetPosition(args.OriginalSource as UIElement).X;                
                    if (frameworkContent is Panel)
                    {
                        var panel = frameworkContent as Panel;
                        if (panel.Children.Any())
                        {
                            if (panel.Children.First() is Control)
                            {
                                (panel.Children.First() as Control).Focus(FocusState.Keyboard);
                            }
                        }
                    }                
                    popUp.SetValue(Canvas.TopProperty, top);
                    popUp.SetValue(Canvas.LeftProperty, left);

                }
                catch(Exception e)
                {

                }
            }       
        }

  public class Extensions:DependencyObject
    {

        public static void SetFlyout(UIElement element, TemplatedFlyout value)
        {
            element.SetValue(FlyoutProperty, value);
        }
        public static TemplatedFlyout GetFlyout(UIElement element)
        {
            return (TemplatedFlyout)element.GetValue(FlyoutProperty);
        }
        public static readonly DependencyProperty FlyoutProperty = DependencyProperty.Register(nameof(FlyoutProperty), typeof(TemplatedFlyout), typeof(Extensions), new PropertyMetadata(null, TemplateFlyoutChanged));

        private static void TemplateFlyoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var uiSender = d as UIElement;
            var flyout = e.NewValue as TemplatedFlyout;
            flyout.Initialization(uiSender);
        }
    }
    public static class VisualExtensions
    {       
        public static T FindElementInVisualTree<T>(this DependencyObject parentElement) where T : DependencyObject
        {
            var count = VisualTreeHelper.GetChildrenCount(parentElement);
            if (count == 0) return null;

            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(parentElement, i);
                if (child != null && child is T)
                    return (T)child;
                else
                {
                    var result = FindElementInVisualTree<T>(child);
                    if (result != null)
                        return result;
                }
            }
            return null;
        }
    }


<RichEditBox  IsRightTapEnabled="True"  >
                <local:Extensions.Flyout>
                    <local:TemplatedFlyout >
                        <local:TemplatedFlyout.Template>
                            <StackPanel>
                                <TextBlock Text="test1"/>
                                <TextBlock Text="test1"/>
                            </StackPanel>
                        </local:TemplatedFlyout.Template>
                    </local:TemplatedFlyout>
                </local:Extensions.Flyout>
            </RichEditBox>

Update 2

You cant add to existing Contextmenu of TextBox. To modify the context menu here is the sample