I would like to create a control that can be expanded and is able to be shown even though the size is bigger than its parent control. I need a TextBox
to input something and expand the control when text entered to show some buttons. Just like when the AutoSuggestBox
or web browser's address bar received suggestions, the area is expanded and float on the surface.
I have tried to put anything I want to show in a Flyout
and then make the Flyout
floats over the original controls.
<Flyout x:Name="myFlyout">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="0,-15,0,0"/>
<!-- -15 margin makes it float over the attached control-->
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid x:Name="ContentArea" Width="800">
<StackPanel x:Name="FlyoutStack">
</StackPanel>
</Grid>
</Flyout>
public void ShowFlyout()
{
/**
BaseGrid is the grid contains textbox
TextBoxPart is a TextBox
And the Flyout is attached to BaseGrid.
*/
BaseGrid.Children.Remove(TextBoxPart);
// move the textbox from BaseGrid to Flyout
try { FlyoutStack.Children.Insert(0, SearchBoxPart); }
catch (Exception e){ }
SearchBoxFlyout.ShowAt(BaseGrid); //show the flyout
}
But when the text is in complex language. Moving the textbox will cause the received character disappeared. Is there any alternative way to expend the control?
Here is my new code, SearchBox
is my class.
public static readonly DependencyProperty TextBoxText = DependencyProperty.Register(
nameof(myText),
typeof(string),
typeof(SearchBox),
new PropertyMetadata(default, (sender, args) =>
{
((SearchBox)sender).TextChanged();
}));
public string myText
{
get => (string)GetValue(TextBoxText);
set => SetValue(TextBoxText, value);
}
public void TextChanged()
{
Debug.WriteLine($"{TextBoxControl.Text}////{TextBoxOnFlyout.Text}////{myText}");
// When I enter text in TextBoxControl myText Changed but TexBoxOnFlyout doesn't
}
<Grid x:Name="BaseGrid">
<TextBox
x:Name="TextBoxControl"
Text="{x:Bind myText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<FlyoutBase.AttachedFlyout>
<Flyout x:Name="SearchBoxFlyout">
<Grid x:Name="ContentArea">
<StackPanel x:Name="FlyoutStack">
<TextBox x:Name="TextBoxOnFlyout" Text="{x:Bind myText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</Flyout>
</FlyoutBase.AttachedFlyout>
</Grid>
Instead of moving TextBox
es, you can just create a DependencyProperty and bind it to each TextBox
.
For example:
FlyoutTextBox.xaml
<UserControl
x:Class="WinUIApp.FlyoutTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:WinUIApp10"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<TextBox
x:Name="TextBoxControl"
Text="{x:Bind Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Grid.ContextFlyout>
<Flyout
x:Name="Flyout"
Placement="Bottom">
<TextBox
Loaded="FlytoutTextBox_Loaded"
Text="{x:Bind Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Flyout>
</Grid.ContextFlyout>
</Grid>
</UserControl>
FlyoutTextBox.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace WinUIApp;
public sealed partial class FlyoutTextBox : UserControl
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text),
typeof(string),
typeof(FlyoutTextBox),
new PropertyMetadata(default, (d, e) => (d as FlyoutTextBox)?.OnTextChanged()));
public FlyoutTextBox()
{
InitializeComponent();
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
private void OnTextChanged()
{
if (string.IsNullOrEmpty(Text) is false)
{
Flyout.ShowAt(TextBoxControl);
}
}
private void FlytoutTextBox_Loaded(object sender, RoutedEventArgs e)
{
if (sender is not TextBox textBox)
{
return;
}
textBox.SelectionStart = textBox.Text.Length;
}
}