Ignore horizontal mouse scrolling with nested ScrollViewer - xaml

I have a ScrollViewer around a Grid to vertically scroll it's content.
<ScrollViewer>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
...
</Grid>
</ScrollViewer>
Inside of this Grid i have another Scrollviewer with horizontal content.
<Grid>
<ScrollViewer Name="ScrollViewer" VerticalScrollMode="Disabled" VerticalScrollBarVisibility="Disabled" HorizontalScrollMode="Disabled" HorizontalScrollBarVisibility="Hidden">
...
</ScrollViewer>
<Button Name="ButtonRight" HorizontalAlignment="Right" VerticalAlignment="Stretch" Tapped="ButtonRight_Tapped" />
<Button Name="ButtonLeft" HorizontalAlignment="Left" VerticalAlignment="Stretch" Tapped="ButtonLeft_Tapped" />
<Grid>
The HorizontalScrollMode is disabled and HorizontalScrollBarVisibility is hidden because with mouse input, i want to scroll the content with the left/right buttons. The HorizontalScrollMode is disabled to prevent horizontal scrolling when the mouse is inside the ScrollViewer.
private void ButtonRight_Tapped(object sender, TappedRoutedEventArgs e)
{
ScrollViewer.ChangeView(ScrollViewer.HorizontalOffset + ScrollViewer.ActualWidth, 0, 1);
}
With this configuration i can scroll the page vertically even when the mouse is inside the horizontal ScrollViewer. Here is an image to give you an idea:
The problem is with touch. How can i still scroll horizontally when the user is using a touch device? When i enable HorizontalScrollMode touch works as desired, but with a mouse it scrolls horizontally which i try to prevent.
Is there a way to ignore the horizontal scrolling with nested ScrollViewers?

I found a pretty simple solution to solve my problem.
I've added a PointerEntered to my nested horizontal ScrollViewer
<ScrollViewer Name="ScrollViewer" PointerEntered="ScrollViewerImages_PointerEntered" VerticalScrollMode="Disabled" VerticalScrollBarVisibility="Disabled" HorizontalScrollMode="Disabled" HorizontalScrollBarVisibility="Hidden">
...
</ScrollViewer>
and enabled the HorizontalScrollMode when the PointerDeviceType is Touch.
private void ScrollViewerImages_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch)
ScrollViewerImages.HorizontalScrollMode = ScrollMode.Enabled;
else
ScrollViewerImages.HorizontalScrollMode = ScrollMode.Disabled;
}

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.

How to bind InkCanvas height and width to an image

I have an Image control in a Grid that is displayed when the user clicks on an image in a list. I want to add an InkCanvas control directly on top of the Image control so the user can draw on it.
However, it seems like the height and width of the InkCanvas is not being bound correctly to the image and I am able to draw outside the image. What else do I need to do?
My XAML code:
<Grid>
<Image x:Name="result_img" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<InkCanvas x:Name="inkCanvas" x:Load="False" Height="{x:Bind result_img.ActualHeight}" Width="{x:Bind result_img.ActualWidth}"/>
</Grid>
Code-behind (C++/CX):
void MyGui::test::ListView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e)
{
this->FindName("inkCanvas");
inkCanvas->InkPresenter->InputDeviceTypes = CoreInputDeviceTypes::Mouse;
}
Any help would be much appreciated!
If you want to use element-to-element binding,you should use the ElementName property.The ElementName is the name of the control you want to bind and the Path is the property of the control you want to bind.
<Image x:Name="result_img" VerticalAlignment="Center" HorizontalAlignment="Center" Width="300" Height="400" />
<InkCanvas x:Name="inkCanvas" x:Load="False" Height="{Binding ElementName=result_img, Path=ActualHeight}" Width="{Binding ElementName=result_img, Path=ActualWidth}"/>

Issues with child page's display when using Frames

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);
}

Windows Phone items scroll issue while SIP keyboard appear

I'm facing two issues with the code following while I'm making a simple chat app.
The code shows a textblock at the top of the page and two textbox stack at the bottom. Plus a listbox which will be auto height to fill the remaining gap.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle Height="1" Fill="White" VerticalAlignment="Top"/>
<TextBlock Text="Hello World!" Grid.Row="0" FontSize="36"/>
<Listbox Grid.Row="1"/>
<TextBox Grid.Row="2"/>
<TextBox Grid.Row="3"/>
</Grid>
When I click on one of the top textboxes, the SIP keyboard becomes visible and all content in the page is pushed up. The main issue here is that the top textblock disappears and hides over the top. How can I keep it on top and not moving while the SIP keyboard is viewed?
When the most bottom textbox has focus, the SIP keyboard appears and pushes all content up. In this case, the keyboard will just fit and be sticky to that textbox. However, when the other textbox has focus, the keyboard will make a gap between them. How can I make the keyboard behave as it does when the most bottom one is focused?
When the keyboard pops and unpops, a TranslateTransform runs on the PhoneApplicationFrame, translating the whole screen up and down.
Based on this article, you should be able to get the value of the translation. As you can observe an animation moves the Y property from zero to a specific negative value (based on the control you took focus on).
Option 1: I have not been able to write a descent way of handling this value but you should be able to resize your controls to fit in what's left of the screen.
Option 2.0 (bad): You can cancel or remove this animation. The keyboard will be on top of the screen without any movement. Your turn now to move/resize your controls to fit the remaining space.
public MainPage()
{
this.InitializeComponent();
PhoneApplicationFrame frame = (App.Current as App).RootFrame;
var group = (frame.RenderTransform as TransformGroup);
group.Children.RemoveAt(0); // remove translate transform
}
Option 2.1: There's an issue with 2.0: removing the transform will prevent you from being notified about the keyboard. Setting up a reverse animation on your page's child when the Y property changes will "kind of cancel" the original translation.
<Grid x:Name="LayoutRoot" Background="Transparent" VerticalAlignment="Stretch">
<Grid.RenderTransform>
<TransformGroup>
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
static void OnRootFrameTransformChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
// ... edit from the blog article ...
MainPage page = source as MainPage;
page.lb.Items.Add(newvalue);
var oppositeTransform = (TranslateTransform)((TransformGroup)page.RenderTransform).Children[0];
if (newvalue < 0.0)
{
page.IsSipVisibleGuess.IsChecked = true;
oppositeTransform.Y = -newvalue;
}
else if (newvalue == 0.0)
{
page.IsSipVisibleGuess.IsChecked = false;
oppositeTransform.Y = 0;
}
I'm sorry none of these options will magically solve the problem but it may help you code what fits best for your app.
If you find a better solution out of this, please post it as an answer.

ScrollViewer still visible when Height is 0

It is easy to reproduce. Just create a new project and paste the following code:
<StackPanel Grid.Row="0" Name="Header" Height="0">
<TextBlock Text="This text is hidden" />
<ScrollViewer>
<TextBlock Margin="2" TextWrapping="Wrap" FontSize="32" Text="This text isn't."/>
</ScrollViewer>
</StackPanel>
The question is the stact panel height is 0 thus the content is supposed to be invisible.
How do I fix it?
Just messing around with the settings.
Found that by adding the CacheMode="BitmapCache" to Stackpanel worked.
Googled it and it seems it is not recommend to do this way considering the performance.
Still looking for the proper answer;)
If you want to make the StackPanel invisible, set Visibility to Collapsed instead. Or you wish to make the ScrollViewer invisible in certain cases?
It sounds like you are trying to make the ScrollViewer and its content disappear when the height of the StackPanel goes to zero. If that is the case, then just trigger off the SizeChanged event of the StackPanel to handle when you should show or hide the Scrollviewer.
private void Header_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (Header.Height.Equals(0))
{
scroller.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
scroller.Visibility = System.Windows.Visibility.Visible;
}
}