For a Windows Store App a requirement says : "Keep the focus on a TextBox when the page is loaded and after user interact with others components".
I solved the Issue when the page is loaded and when the user interact with other components in the same Grid (i.e. Buttons).
MyTextBox.LostFocus += (s,e)=> {
Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () => SearchBox.Focus(FocusState.Programmatic));
}
The problem is that when when the user interact with components in different view , like the AppBar.
Maybe i can solve the issue comparing the parent views and run the Focus(FocusState.Programmatic) if they are coming from the same view.
But... how ?
Based on your description, you want to keep focus on TextBox. I do a simple demo and you can refer to this.
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom">
<TextBox x:Name="textbox1" Width="300" Height="50" HorizontalAlignment="Left" VerticalAlignment="Bottom"></TextBox>
</StackPanel>
<CommandBar>
<AppBarToggleButton Icon="Shuffle" Label="Shuffle" Click="AppBarButton_Click" />
<AppBarToggleButton Icon="RepeatAll" Label="Repeat" Click="AppBarButton_Click"/>
<AppBarSeparator/>
<AppBarButton Icon="Back" Label="Back" Click="AppBarButton_Click"/>
<AppBarButton Icon="Stop" Label="Stop" Click="AppBarButton_Click"/>
<AppBarButton Icon="Play" Label="Play" Click="AppBarButton_Click"/>
<AppBarButton Icon="Forward" Label="Forward" Click="AppBarButton_Click"/>
<CommandBar.SecondaryCommands>
<AppBarButton Icon="Like" Label="Like" Click="AppBarButton_Click"/>
<AppBarButton Icon="Dislike" Label="Dislike" Click="AppBarButton_Click"/>
</CommandBar.SecondaryCommands>
<CommandBar.Content>
<TextBlock Text="Now playing..." Margin="12,14"/>
</CommandBar.Content>
</CommandBar>
</Grid>
Code Behind:
public MainPage()
{
this.InitializeComponent();
this.LayoutUpdated += MainPage_LayoutUpdated;
}
private void MainPage_LayoutUpdated(object sender, object e)
{
textbox1.Focus(Windows.UI.Xaml.FocusState.Programmatic);
}
I also find one threads you can refer to.
Related
XAML code:
<Page.TopAppBar>
<CommandBar x:Name="bottomAppBar" Padding="10,0,10,0">
<AppBarButton AutomationProperties.Name="Sample Button"
AutomationProperties.AutomationId="SampleAppBarButton"
Click="AppBarButton_Click">
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Name="MuteMenu" Icon="Mute" Text="Mute" Click="MuteMenu_Click">
<FlyoutBase.AttachedFlyout>
<Flyout>
<TextBlock Text="Some text..."/>
</Flyout>
</FlyoutBase.AttachedFlyout>
</MenuFlyoutItem>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
</CommandBar>
</Page.TopAppBar>
C++/CX:
void App2::DirectXPage::MuteMenu_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
FlyoutBase::ShowAttachedFlyout((FrameworkElement^)sender);
}
But ShowAttachedFlyout not working - flyout is not appearing when I click menu item. No errors reported.
Creating and attaching flyout programmatically doesn't work as well.
Target version is 10.0.18362.0.
Visual Studio 2019 (v142).
flyout is not appearing when I click menu item
By testing your code, the flyout doesn't appear is because after the MenuFlyoutItem is clicked, the entire MenuFlyout will be hidden, the Flyout inside it can't appear.
You can try to use MenuFlyoutSubItem which contains a cascading list of menu items.
<Page.TopAppBar>
<CommandBar x:Name="bottomAppBar" Padding="10,0,10,0">
<AppBarButton AutomationProperties.Name="Sample Button"
AutomationProperties.AutomationId="SampleAppBarButton"
x:Name="MyAppButton" >
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutSubItem x:Name="MuteMenu" Icon="Mute" Text="Mute">
<MenuFlyoutItem Text="Some Text..."></MenuFlyoutItem>
<MenuFlyoutItem Text="123"></MenuFlyoutItem>
</MenuFlyoutSubItem>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
</CommandBar>
</Page.TopAppBar>
Or you can add a Button.Flyout and include the menuFlyout in the Button.Flyout.
<Page.TopAppBar>
<CommandBar x:Name="bottomAppBar" Padding="10,0,10,0">
<AppBarButton x:Name="button" >
<AppBarButton.Flyout>
<Flyout>
<StackPanel>
<Button>
<StackPanel Orientation="Horizontal">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph=""></FontIcon>
<TextBlock>Mute</TextBlock>
</StackPanel>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="Some text..."></MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</Flyout>
</AppBarButton.Flyout>
</CommandBar>
</Page.TopAppBar>
Is there a way to create master / template pages in XAML (for UWP applications)?
The problem I'm trying to solve:
I have an application with a lot of similar sites, where only the content changes slightly but not the Buttons and the Layout. Example:
<Page
DataContext="{Binding WebpageViewModel, Source={StaticResource Locator}}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Edit Webpage" Style="{StaticResource BigTexBlock}" />
<ScrollViewer Style="{StaticResource ContentScrollViewer}" Grid.Row="1" VerticalScrollMode="Enabled">
<StackPanel Margin="10,0">
<webpage:EditWebpage DataContext="{Binding }" />
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
<Page.BottomAppBar>
<CommandBar>
<CommandBar.PrimaryCommands>
<!-- more buttons -->
<AppBarButton IsCompact="True" Command="{Binding SaveEntryCommand}" Icon="Save" Label="Save" />
</CommandBar.PrimaryCommands>
</CommandBar>
</Page.BottomAppBar>
</Page>
Only three parts of this template will change; the ViewModel in the DataContext, the Text of the TextBlock, and the UserControl which contains the editable fields.
As this is an application with a lot of CRUD happening with simple Entities the amount of code to be repeated over and over again is a lot if I keep "solving" the problem like this. In the separated business logic I could avoid this problem with inheritance, but I'm struggling to find a elegant solution in XAML.
Is there a way to refactor this so I may have a "Template Page"?
I like how for example twig has solved this problem: http://twig.sensiolabs.org/. You define a master/template page and override parts of it in the children templates.
Important to me is that
I don't break the MVVM pattern.
I don't want to hide/show UserControls in one "Main" XAML as the amount of different entities may become quite large
I want navigation happening between the pages that the user sees the expected animations, and it does not break the separated view code I already have
There isn't a master page or template mechanism that other technologies, like MVC, have. But you can use frames and navigation to do what you're looking for.
You could keep the page defined the way you currently have it. All of the fixed elements on the page are in the layout. Now instead of using a UserControl for your specific edit UI, replace that with a frame.
<StackPanel Margin="10,0">
<Frame Name="EditFrame" DataContext="{Binding }" />
</StackPanel>
Now when you navigate to the Main Edit page, also pass the type for the view you want in the frame. Then on your OnNavigatedTo override for the main page, you can navigate the frame to the view type as the parameter.
You can also use the EditFrame to page through multiple editing pages, like if you had a wizard UI with Next and Previous buttons, without leaving the main page.
You can either do this in your OnNavigatedTo method or modify your NavigationService to be able to handle this behavior.
I have solved this problem with the approach suggested by https://stackoverflow.com/a/43170663
my NavigationService
//get the current frame
var frame = (Frame)Window.Current.Content;
//navigate to the generic AddEntry page
frame.Navigate(typeof(AddEntryPage), new NavigationParameter() { /* set props needed */ });
my xaml (my "master page") looks now like this (using a Frame now):
<Page
x:Class="Famoser.Bookmarked.Presentation.Universal.Pages.Entry.Webpage.AddEntryPage"
mc:Ignorable="d"
d:DataContext="{Binding WebpageViewModel, Source={StaticResource Locator}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="Title" Grid.Row="0" Text="Add "/>
<ScrollViewer Grid.Row="1">
<StackPanel Margin="10,0">
<Frame x:Name="EntryFrame" />
</StackPanel>
</ScrollViewer>
</Grid>
<Page.BottomAppBar>
<CommandBar>
<CommandBar.PrimaryCommands>
<!-- more app buttons -->
<AppBarButton IsCompact="True" Command="{Binding SaveEntryCommand}" Icon="Save" Label="Save" />
</CommandBar.PrimaryCommands>
</CommandBar>
</Page.BottomAppBar>
</Page>
and in the code behind in the navigation event I set the properties passed by my NavigationService
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is NavigationParameter pm)
{
DataContext = SimpleIoc.Default.GetInstance(pm.ViewModelType);
Title.Text = "Add " + pm.Name;
EntryFrame.Navigate(pm.EditFrameType);
}
}
the full project in on github: https://github.com/famoser/Bookmarked
I have a commandbar and on one of the appbar buttons I attached a flyout which shows up properly. This flyout again has a command bar with appbar buttons. The problem is that I wanted to open a flyout again on one of these buttons but that does not work. Please help!
<Page.Resources>
<Flyout x:Key="SetBookmarkFlyout">
<Grid Width="250" Height="250">
<TextBlock Grid.Row="0">Name</TextBlock>
<TextBox Grid.Row="1" x:Name="BookmarkName"></TextBox>
<TextBlock Grid.Row="2">Create in</TextBlock>
<ComboBox Grid.Row="3"></ComboBox>
</Grid>
</Flyout>
<Flyout x:Key="SubMenuFlyout">
<CommandBar Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Height="250" >
<AppBarButton Icon="Bookmarks" ToolTipService.ToolTip="Open Bookmarks"></AppBarButton>
<AppBarButton x:Name="SetBookmark" Icon="Favorite" ToolTipService.ToolTip="Set Bookmark" Click="SetBookmark_Click" FlyoutBase.AttachedFlyout="{StaticResource SetBookmarkFlyout}" ></AppBarButton>
<AppBarButton Icon="Page" ToolTipService.ToolTip="Recent Files"></AppBarButton>
</CommandBar>
</Flyout>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="0,0,0,-10" >
<CommandBar x:Name="Commandbar" Height="70" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<AppBarButton Icon="Back" Label="" ToolTipService.ToolTip="Back" ></AppBarButton>
<AppBarButton Icon="Forward" Label="" ToolTipService.ToolTip="Forward" ></AppBarButton>
<AppBarButton x:Name="SpaceAppBtn"></AppBarButton>
<AppBarButton Icon="Add" Label="" ToolTipService.ToolTip="Add Tab"></AppBarButton>
<AppBarButton Icon="Favorite" Label="" ToolTipService.ToolTip="Favorites (Set Bookmark,open Bookmarks, and see recent files)" Click="AppBarButton_Click" FlyoutBase.AttachedFlyout="{StaticResource SubMenuFlyout}">
</AppBarButton>
<AppBarButton Icon="Find" Label="" ToolTipService.ToolTip="Search"></AppBarButton>
</CommandBar>
</Grid>
I have added your Flyout directly to the Button in my example to test.
<AppBarButton x:Name="FavoriteButton" Icon="Favorite" Label="" ToolTipService.ToolTip="Favorites (Set Bookmark,open Bookmarks, and see recent files)"
Click="AppBarButton_Click">
<AppBarButton.Flyout>
<Flyout>
<CommandBar Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Height="250" >
<AppBarButton Icon="Bookmarks" ToolTipService.ToolTip="Open Bookmarks"></AppBarButton>
<AppBarButton x:Name="SetBookmark" Icon="Favorite" ToolTipService.ToolTip="Set Bookmark"
Click="SetBookmark_Cick">
</AppBarButton>
<AppBarButton Icon="Page" ToolTipService.ToolTip="Recent Files"></AppBarButton>
</CommandBar>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
Use a Popup rather than the second Flyout.
After CommandBar add
<CommandBar>...
</CommandBar>
<Popup x:Name="SetBookmarkPopup" HorizontalOffset="200" VerticalOffset="200">
<Grid Width="250" Height="250">
<TextBlock Grid.Row="0">Name</TextBlock>
<TextBox Grid.Row="1" x:Name="BookmarkName"></TextBox>
<TextBlock Grid.Row="2">Create in</TextBlock>
<ComboBox Grid.Row="3"></ComboBox>
</Grid>
</Popup>
and call from your AppBarButton_Click event
private void AppBarButton_Click(object sender, RoutedEventArgs e)
{
FavoriteButton.Flyout.ShowAt(FavoriteButton);
}
finally open the Popup
private void SetBookmark_Cick(object sender, RoutedEventArgs e)
{
SetBookmarkPopup.IsOpen = true;
}
Hope that helps
I have XAML page that looks like:
<Page
xmlns:vm="using:domain.viewmodels">
<Page.Resources>
<vm:MainPageViewModel x:Name="MainPageVm"/>
</Page.Resources>
<Grid DataContext="{Binding Source={StaticResource MainPageVm}}">
<Button Command={Binding QuitCommand, Mode=TwoWay}"/>
</Grid>
<Page.BottomAppBar>
<CommandBar IsOpen="True">
<AppBarButton Label="Quit" Command={Binding QuitCommand, Mode=TwoWay}"/>
</CommandBar>
</Page.BottomAppBar>
</Page>
If I press the Button – the Command was executed.
If I press the AppBarButton – the Command was not executed.
I get that the reason is that I have set DataContext for Grid only, but not for BottomAppBar. But I don't know how to set DataContext for all Page.
I have tried this code, but it doesn't work because I have declared MainPageVm below:
<Page
DataContext="{Binding Source={StaticResource MainPageVm}}"
xmlns:vm="using:domain.viewmodels">
<Page.Resources>
<vm:MainPageViewModel x:Name="MainPageVm"/>
</Page.Resources>
I thought I can set DataContext for CommandBar. I don't like this solution, as I need to set DataContext twice for the Page, but even this solution doesn't work:
<Page.BottomAppBar>
<CommandBar IsOpen="True" DataContext="{Binding Source={StaticResource MainPageVm}}">
<AppBarButton Label="Quit" Command={Binding QuitCommand, Mode=TwoWay}"/>
</CommandBar>
</Page.BottomAppBar>
Why?
Instead of using Resources, set the DataContext directly:
<Page
xmlns:vm="using:domain.viewmodels">
<Page.DataContext>
<vm:MainPageViewModel x:Name="MainPageVm"/>
</Page.DataContext>
Your bindings become then:
<Grid>
<Button Command={Binding QuitCommand}"/>
</Grid>
<Page.BottomAppBar>
<CommandBar IsOpen="True">
<AppBarButton Label="Quit" Command={Binding QuitCommand}"/>
</CommandBar>
</Page.BottomAppBar>
It's often simpler to declare your DataContext in code-behind:
public YourPageClass()
{
InitializeComponent();
this.DataContext = new MainPageVM();
}
With this XAML:
<Page.BottomAppBar>
<AppBar x:Name="bottomAppBar" Padding="10,0,10,0">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Button Style="{StaticResource BrowsePhotosAppBarButtonStyle}" Click="btnOpenImgFiles_Click"/>
<Button Style="{StaticResource OpenAppBarButtonStyle}" Click="btnOpenMap_Click"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource SaveAppBarButtonStyle}" Click="btnSaveMap_Click"/>
</StackPanel>
</Grid>
</AppBar>
</Page.BottomAppBar>
(which I adapted from markup I found online) I got a "Windows.UI.Xaml.Markup.XamlParseException"
Looking at this, I figured it should be AppBarButton instead of Button, so I changed them to that...but I'm still getting the same err msg. Is it because there's no such thing as "BrowsePhotosAppBarButtonStyle" (I can't find a list of valid values for that) or...???
Yes. It's probably the button styles which are based on legacy Windows 8 code. If you're targeting Windows 8.1 then you should use AppBarButtons rather than Buttons. I'd also put them in a CommandBar rather than layout out your own Grid in an AppBar.
If BrowsePhotosAppBarButtonStyle isn't specific to the sample you got that from it is probably available in the StandardStyles.xaml file included with Windows 8 templates. That file included a large number of commented out button styles for you to uncomment as needed.
Here's how you'd set this up in a Windows 8.1 app. For simplicity I didn't hook up the Click handlers, and you may want to update the Label and Automation names:
<Page.BottomAppBar>
<AppBar x:Name="bottomAppBar" Padding="10,0,10,0">
<CommandBar>
<CommandBar.SecondaryCommands>
<AppBarButton Icon="BrowsePhotos" Label="Browse" AutomationProperties.Name="Browse Photos" />
</CommandBar.SecondaryCommands>
<CommandBar.PrimaryCommands>
<AppBarButton Icon="OpenFile" Label="Open" AutomationProperties.Name="Open File"/>
<AppBarButton Icon="Save" Label="Save" AutomationProperties.Name="Save File"/>
</CommandBar.PrimaryCommands>
</CommandBar>
</AppBar>
</Page.BottomAppBar>
See Adding app bars (XAML) for more details.