Issues with child page's display when using Frames - xaml

Problem
This is the layout of my main pane:
<Page
x:Class="Communities.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Communities"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Loaded="Page_Loaded" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
...
<SplitView Grid.Row="1" Name="hamburgerMenu" OpenPaneLength="200" PaneBackground="#F02A2A2A">
<SplitView.Pane>
<ListView ItemsSource="{Binding}" IsItemClickEnabled="True" ItemClick="HamburgerItemClick">
... </ListView>
</SplitView.Pane>
<Frame Name="frame" />
</SplitView>
<Grid Grid.RowSpan="3" Name="popupArea" />
</Grid>
</Page>
the frame is where I load all my pages, so that the layout is always consistant.
Now, in most of my child pages I have defined AppBar control and attached it to the BottomAppBar property of that child page:
PostView.xaml
...
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Label="Back" Icon="Back" Click="TryGoBack" />
<AppBarButton Label="Refresh" Icon="Refresh" Click="TryRefreshComments" />
<AppBarButton Label="Go to Community" Icon="Go" Click="TryOpenCommunity" />
</CommandBar>
</Page.BottomAppBar>
...
Here's where the trouble starts. It works fine on PC, as the layout is mostly static on desktop. There are no software keyboards required most of the time etc. On mobile it's more problematic: Screenshots
My thoughts
It seems like the frame that is used to display the child page is causing all sorts of problems. When the AppBar is defined in the main page it positions correctly.
I'd like to avoid the keyboard covering the textbox as well as the AppBar but I don't want to get rid of the frame control. I'd also prefer it if the page got "squished" when the keyboard shows up, instead of getting pushed upwards, but I'm not sure how to display the keyboard on the frame level, instead of the entire MainPage, default level.
What would be the best way to solve this situation?
Cheers!

As you know, if we set the Page.BottomAppBar in the root of the Page, there is no issue with Touch keyboard. It seems it is the best way to add the Page.BottomAppBar.
If you want to add the Page.BottomAppBar in the other page in the Frame, you should be able to customize your UI. The UWP provides similar behavior on the appearance of the touch keyboard by handling the Showing and Hiding events exposed by the InputPane object.
We can use the InputPaneVisibilityEventArgs.OccludedRect to get the region of the application's window that the input pane is covering.
For example:
public PostView()
{
this.InitializeComponent();
InputPane.GetForCurrentView().Showing += PostView_Showing;
InputPane.GetForCurrentView().Hiding += PostView_Hiding;
}
private void PostView_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
MyTextBox.Margin = new Thickness(0, args.OccludedRect.Height, 0, 0);
}
private void PostView_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
MyTextBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
}

Related

Windows UWP ScrollViewer scrollbar overlaps contents

How do you stop a ScrollViewer's scrollbar from overlapping content like this?
I have a RichTextBlock containing text and no matter how wide I make the RichTextBlock, or how I change the Margin and Padding values, I cannot get the scrollbar to move further to the right to stop this overlap from happening. I'm running Windows 10 and it is configured to hide scrollbars until the mouse pointer hovers over them.
Below is the XAML for my app.
<Page
x:Class="PaulWinPOS1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PaulWinPOS1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Margin="0,26,0,0">
<Button x:Name="butConnect" Content="Connect" Margin="0,38,48,0" VerticalAlignment="Top" RenderTransformOrigin="-3.274,0.344" HorizontalAlignment="Right" Height="32" Click="ButConnect_Click" Width="92"/>
<Button x:Name="butLogin" Content="Login" Margin="0,92,48,0" VerticalAlignment="Top" RenderTransformOrigin="-3.274,0.344" HorizontalAlignment="Right" Height="32" Width="92" IsEnabled="False" Click="ButLogin_Click"/>
<Button x:Name="butAdd" Content="Add Item" Margin="0,143,48,0" VerticalAlignment="Top" RenderTransformOrigin="-3.274,0.344" HorizontalAlignment="Right" Width="92" IsEnabled="False" Click="ButAdd_Click"/>
<ScrollViewer x:Name="scrollViewerWeb"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
HorizontalAlignment="Left"
Width="350"
Padding="16,0"
Grid.RowSpan="10"
FontFamily="Segoe UI" RequestedTheme="Dark" ZoomMode="Enabled"
Margin="669,304,0,0" >
<WebView x:Name="webviewReceipt"
Margin="10,10,50,10"
HorizontalAlignment="Left"
Height="333" Width="300"
VerticalAlignment="Top"
ScrollViewer.VerticalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Visible" />
</ScrollViewer>
<Button x:Name="butDisconnect" Content="Disconnect" Margin="0,244,48,0" VerticalAlignment="Top" RenderTransformOrigin="-3.274,0.344" HorizontalAlignment="Right" Height="32" Width="92" Click="ButDisconnect_Click"/>
</Grid>
</Page>
The scroll bar of WebView is special and cannot be solved by the conventional ScrollViewer additional properties, but the scroll bar of the WebView can be disabled through the CSS of the web page.
body {
-ms-overflow-style: none;
}
If you cannot modify the source code of the webpage, you can perform the following operations after the WebView content is loaded:
private async void webviewReceipt_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
string js = "var style = document.createElement('style');" +
"style.type = 'text/css';" +
"style.innerHTML = \"body{ -ms-overflow-style: none !important; }\";" +
"document.getElementsByTagName('Head').item(0).appendChild(style); ";
await webviewReceipt.InvokeScriptAsync("eval", new string[] { js });
}
Update
If we need to display a scroll bar, we can add a padding-right to the body so that the scroll bar does not block the content.
private async void webviewReceipt_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
string js = "var style = document.createElement('style');" +
"style.type = 'text/css';" +
"style.innerHTML = \"body{ padding-right: 24px; }\";" +
"document.getElementsByTagName('Head').item(0).appendChild(style); ";
await webviewReceipt.InvokeScriptAsync("eval", new string[] { js });
}
You need to add a Padding to ScrollViewer.
<ScrollViewer Padding="18, 0">
<RichTextBlock />
</ScrollViewer>
Usually the ScrollBar Width is 18.
It looks like you have the scroll bars enabled on both the web view and scroll viewer. You can try disabling the scroll bars on one of them to see if it makes a difference.

Binding shared flyout to 2 controls in Listview's DataTemplate in UWP Windows 10

I've got a shared Flyout defined in my <Page.Resources> as follows:
<Flyout x:Name="InfoFlyout" Opened="{Binding IsOpen,
ElementName=MyListView, Mode=TwoWay}">
<Grid>
<Button Foreground="White" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Help"/>
</StackPanel>
</Button>
</Grid>
</Flyout>
But I get An object reference not set error when compiling, so I used the code from this article (Using Windows 8.1 Flyout control with MVVM) instead.
This seems to circumvent the problem I was having with the above code. Now my shared Flyout code looks like this:
<Flyout x:Name="InfoFlyout"
helpers:FlyoutHelpers.Parent="{Binding ElementName=MyListView}"
helpers:FlyoutHelpers.IsOpen="{Binding IsOpen, Mode=TwoWay}">
<Grid>
<Button Foreground="White" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Help"/>
</StackPanel>
</Button>
</Grid>
</Flyout>
My ListView control (i.e. x:Name="MyListView") is binded to the page's ViewModel i.e. MainPageViewModel. The IsOpen property is defined in the MainViewModel.
Now in my ListView DataTemplate, I want my Flyout to open when I press and hold the ListViewItem or when pressing a button within the ListViewItem:
<DataTemplate>
<Grid FlyoutBase.AttachedFlyout="{StaticResource InfoFlyout}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source={Binding MyImage} />
<Grid Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Width="30" Height="30"
Flyout="{StaticResource InfoFlyout}"
content="i">
</Button>
</Grid>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Holding">
<actions:OpenFlyoutAction />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Grid>
</DataTemplate>
As you can see, I've got the Flyout "attached" to the Grid via:
FlyoutBase.AttachedFlyout="{StaticResource InfoFlyout}"
and I've got the same Flyout attached to the button within the ListViewItem itself via:
Flyout="{StaticResource InfoFlyout}"
I've put breakpoints on both my setter and getter for the IsOpen property and when page gets loaded, it does go into the getter but whenever I open or close my Flyout either via Holding or by pressing the 'i' button, it doesn't trigger the method below and therefore it doesn't change the IsOpen property.
private static void OnIsOpenPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) as defined in the FlyoutHelper class.
The reason I've set my ElementName to MyListView is that I want all my ListViewItem to be binded to the one property i.e. IsOpen as I need to detect whenever a flyout menu is opened irrelevant of which ListViewItem it belongs to.
How can I achieve or resolve this?
UPDATE - 1
The problem of accessing the shared menu has been resolved by using the following:
<Flyout x:Name="InfoFlyout"
helpers:FlyoutHelpers.Parent="{Binding ElementName=MyListView}"
helpers:FlyoutHelpers.IsOpen="{Binding IsOpen, Mode=TwoWay}">
and setting the button to
<Button Width="30" Height="30"
Command="{Binding InformationCommand}"
CommandParameter="{Binding}"
Flyout="{StaticResource InfoFlyout}">
Which is fine and as #ElvisXia mentioned, you can comment out the code in the OnIsOpenPropertyChanged as the positioning is already determined by the button located inside my ListViewItem.
There is however one outstanding problem. A small one btw, but nice if it can be solved. The shared flyout which is attached to the grid itself in the DataTemplate i.e.
<DataTemplate>
<Grid FlyoutBase.AttachedFlyout="{StaticResource InfoFlyout}">
It is being positioning based on the ListViewItem which technically is correct as I'm calling a different piece of code for that one i.e.
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Holding">
<actions:OpenFlyoutAction />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
And the OpenFlyoutAction is defined as follows:
public class OpenFlyoutAction : DependencyObject, IAction
{
public object Execute(object sender, object parameter)
{
FrameworkElement senderElement = sender as FrameworkElement;
FlyoutBase flyoutBase = FlyoutBase.GetAttachedFlyout(senderElement);
flyoutBase.ShowAt(senderElement);
return null;
}
}
Can I somehow stop using the OpenFlyoutAction and use the same code as provided in the article to open my Flyout wherever the user is holding his/her finger on the relevant ListViewItem rather than on top or below the actual ListViewItem?
I understand it's a little bit side track from the original issue which was to share a Flyout by to controls but may as well finish it as it is somehow relevant to the issue.
Thanks.
Change the type of Parent from Button to ListView. To open flyout in particular X,Y position is not possible in WP. You can choose PopUp control instead. Here is a link which i got open the pop up in tapped position. You can use VisualTreeHelper to get PopUp control of tapped ListViewItem
By Using Windows 8.1 Flyout control with MVVM , the author use parent to control where the flyout shows up.
So the author have codes like below(FlyoutHelpers.cs):
private static void OnIsOpenPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var flyout = d as Flyout;
var parent = (ListView)d.GetValue(ParentProperty);
if (flyout != null && parent != null)
{
var newValue = (bool)e.NewValue;
if (newValue)
flyout.ShowAt(parent);
else
flyout.Hide();
}
}
He use flyout.ShowAt(parent) to let flyout show at parent element. But in your codes you have binded the flyout to the button using:
<Button Width="30" Height="30"
Flyout="{StaticResource InfoFlyout}" content="i">
</Button>
So it is not necessary to let it show at it's parent any more. To fix the problem, you can comment out the statements like below:
private static void OnIsOpenPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//var flyout = d as Flyout;
//var parent = (ListView)d.GetValue(ParentProperty);
//if (flyout != null && parent != null)
//{
// var newValue = (bool)e.NewValue;
// if (newValue)
// flyout.ShowAt(parent);
// else
// flyout.Hide();
//}
}
Then you will see the flyout shows at the right place.

UWP Page Preload ( Is it possible to preload the contents of a page )

I'm working on a Windows 10 app. and i created map in my second page.. but it is too slow.. Is it possible to preload the contents of a page before displaying it to the user so that the transition from page x to page y is smooth?
Is not possible, all pages in uwp apps will be rendered when you navigated to some page.
But you can prepare the data before to navigate to your second page maybe you can save this data in the local storage and load the data in the method OnNavigatedTo in your second page it would be better.
The easiest option is probably to not make it a page at all; instead, have the map on the page you are starting from, but have its Opacity be zero. Then when you want to show it, set the Opacity to one. This if course means you pay the cost of loading the map even if the user never wants to view it, but it makes viewing instant.
Sample XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="go to map" Click="GoToMap" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Grid x:Name="mapLayer" xmlns:maps="using:Windows.UI.Xaml.Controls.Maps" Opacity="0" IsHitTestVisible="False">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<maps:MapControl />
<Button Content="back" Click="HideMap" HorizontalAlignment="Center" Grid.Row="1"/>
</Grid>
</Grid>
And matching code:
private void GoToMap(object sender, RoutedEventArgs e)
{
mapLayer.Opacity = 1;
mapLayer.IsHitTestVisible = true;
}
private void HideMap(object sender, RoutedEventArgs e)
{
mapLayer.Opacity = 0;
mapLayer.IsHitTestVisible = false;
}

Pubcenter Ad is not clickable

I'm bashing my head with weird problem, the ads from microsoft pubcenter are not clickabkle when
application uses D3D/XAML interop.
BUT I see many games on windows store that have pubcenter ads and clearly uses d3d/xaml - how to make those two to work together ?
Simple steps to reproduce the problem:
(VS2013 - with all the latest updates, pibcenter ctrl also updated to latest version)
VS2013 -> New -> Project -> (Visual C++ / Store Apps) -> DirectX and XAML App (Windows phone)
(NOTE: This is Windows phone 8.1 app, NOT windows phone 8.1 silverlight app)
when the project is created, just go to DirectXPage.xaml
and edit it to look as follow:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="using:Microsoft.Advertising.Mobile.UI"
x:Class="AdTest.DirectXPage"
mc:Ignorable="d">
<SwapChainPanel x:Name="swapChainPanel">
<!-- Grid definition, to place ad on bottom (may remove if no ads) -->
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Hello from XAML!"
HorizontalAlignment="Right"
VerticalAlignment="Top"
FontSize="30" />
<UI:AdControl Grid.Column="0" Grid.Row="1" x:Name="adBanner" AutoRefreshIntervalInSeconds="60" ApplicationId="xxxxxxxxxxx" AdUnitId="xxxxx" HorizontalAlignment="Right" Height="50" IsAutoRefreshEnabled="True" Margin="0,0,0,0" VerticalAlignment="Bottom" Width="320"/>
</SwapChainPanel>
<!-- Uncomment this if using the app bar in your phone application.
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="Play" AutomationProperties.Name="Sample Button"
AutomationProperties.AutomationId="SampleAppBarButton" Click="AppBarButton_Click" />
</CommandBar>
</Page.BottomAppBar>-->
</Page>
replace xxxxx with real app / unit ids and run the sample - now when the banner shows up, try to click on it -- it does not work :/
Is there any way to make it clickable ? (it's not covered by any other controll, it gets it's input properly, so I do not know what the issue may be).
Have you solve your problem?
IT seems that this is related with it being a c++ app, I am also with a lot of problems with it.
One workaround that I found was to ad the method Tapped into the control:
adPubcenter->Tapped += ref new Windows::UI::Xaml::Input::TappedEventHandler(this, &OpenGLESPage::OnAdTapped);
void OpenGLESPage::OnAdTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e){
And then
void OpenGLESPage::OnAdTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e){
//Handle touch yourself (ie. opens a game that you have)
Windows::System::Launcher::LaunchUriAsync(ref new Uri("http://www.windowsphone.com/s?appid=bb8d4ce2-1853-47b6-9b0d-bb1af5a07436"));
}
At least this way the ad does something when clicked.

silverlight xaml layout issue: grid should be able to grow, but not off the screen

See the following XAML:
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightApplication1.MainPage"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" MinHeight="150">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<sdk:DataGrid x:Name="grid" VerticalScrollBarVisibility="Visible" />
<Button x:Name="button" Grid.Row="1" Content="hello" VerticalAlignment="Top" HorizontalAlignment="Center" Click="button_Click" />
</Grid>
</UserControl>
Corresponding code:
public partial class MainPage : UserControl
{
public class dataclass
{
public string data { get; set; }
}
ObservableCollection<dataclass> list;
public MainPage()
{
InitializeComponent();
grid.ItemsSource = list = new ObservableCollection<dataclass>();
}
private void button_Click(object sender, RoutedEventArgs e)
{
for (var i = 0; i < 5; i++)
list.Add(new dataclass
{
data = "hello" + i
});
}
}
How it works now: The grid takes up the entire screen height minus the height of the button. When too many new items are added, you scan start scrolling. The position of the button never changes, it's always at the bottom of the screen.
What I would like: The grid should take up as little space as possible, so when it's empty, only the header should be visible, and the button immediately below it. When too many items are added, and the button is already at the bottom of the screen, it shouldn't grow any more, but start scrolling instead.
If I swap the two RowDefinition's, then the grid is small at first, but grows indefinitely, pushes the button off the screen and never starts scrolling. How can I do this nicely?
In order to achieve this, nest another Grid in LayoutRoot and then use that nested grid as your main one. Then set both rows to Auto.
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="innerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="itemInTheFirstRow" Grid.Row="0" />
<Button x:Name="itemInTheSecondRow" Grid.Row="1" />
</Grid>
</Grid>
Finally, you need to track the size of the grid and change the sizing rules accordingly. This "pinning" code looks something like.
RowDefinition row = this.innerGrid.RowDefinitions[0];
if (row.Height.GridUnitType == GridUnitType.Auto)
{
if (this.innerGrid.ActualHeight > this.ActualHeight)
{
row.Height = new GridLength(1, GridUnitType.Star);
}
}
else
{
if (this.itemInTheFirstRow.DesiredSize.Height < row.ActualHeight)
{
row.Height = GridLength.Auto;
}
}
In my implementation, I wrap this code in an UpdateRowPinning method that actually uses the dispatcher to call this code. I then call UpdateRowPinning on resize events for the main grid and the inner grid as well as on adding and removing items from the grid and expand/collapse operations of grid groups. This ensures that the second row behaves properly by sitting at the base of the first row until the screen is full and then floating over it after that.
My answer here also covers this issue. I searched for a XAML only solution but it just doesn't seem possible (unless you write some XAML extensions, then you might be able to pull it off with XAML but that's kind of cheating).