Reputation: 689
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!
Upvotes: 2
Views: 824
Reputation: 3221
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
Upvotes: 2