scrollTo in WebView UWP may be achieved through:
private string ScrollToTopString = #"window.scrollTo(0,0);";
private async void ButtonClick(object sender, RoutedEventArgs e)
{
await WebViewTest.InvokeScriptAsync("eval", new string[] { ScrollToTopString });
}
But what about animated/Smooth scrolling in WebView UWP? In Android that is achieved either officially or through variations (for example, using android.animation.ObjectAnimator), whereas in UWP only ScrollViewer seems to support it as far as I know. Example 1 Example 2
Any ideas?
You can't animate the WebView because the WebView itself isn't actually scrolling, just like your browser doesn't actually scroll; it's the "window" element that's scrolling.
However, if I understand what you're wanting correctly, just replace your ScrollToTopString with this:
var ScrollToTopString = #"var int = setInterval(function() {
window.scrollBy(0, -5);
if( window.pageYOffset === 0 )
clearInterval(int);
}, 1);";
This will scroll that "window" element I mentioned. You can raise the speed of the interval (I have it set to 1) to slow down the animation, or lower the value of scrollBy to make it go faster.
Related
I am using a telerik SizeDrawer control for UWP. and in it I want the drawer content to cover full page when it opens. So I tried to bind to Actual-Width of the page but that didn't work and then I tried to register it with sizeChanged event but width of drawer content still remains 0. unless I explicitly define it to some value like 300 then it remains 300 but I want it to cover full screen.
CODE
<telerik:RadSideDrawer Name="MainDrawer" SizeChanged="MainDrawer_SizeChanged"
Loaded="MainDrawer_Loaded">
<telerik:RadSideDrawer.MainContent>
<Grid />
</telerik:RadSideDrawer.MainContent>
<telerik:RadSideDrawer.DrawerContent>
<Grid Name="DrawerGrid" Background="Yellow">
</Grid>
</telerik:RadSideDrawer.DrawerContent>
</telerik:RadSideDrawer>
C#
private void MainDrawer_SizeChanged(object sender, SizeChangedEventArgs e)
{
DrawerGrid.Width = e.NewSize.Width;
}
private void MainDrawer_Loaded(object sender, RoutedEventArgs e)
{
DrawerGrid.Width = MainDrawer.Width;
}
Note that height of drawer grid is alright and stretches to full screen but width remains 0 and if width is untouched totally then it is 240 fixed.
This is how you do it -
First, define a double property that gets the width of the current Window.
public double WindowWidth => Window.Current.Bounds.Width;
Then use it as a one-time binding source for the DrawerLength of your RadSideDrawer.
<primitives:RadSideDrawer x:Name="MainDrawer"
DrawerLength="{x:Bind WindowWidth}" ... />
Finally, update the DrawerLength whenever the size of the MainDrawer gets updated.
private void MainDrawer_SizeChanged(object sender, SizeChangedEventArgs e) =>
MainDrawer.DrawerLength = e.NewSize.Width;
Also note if you don't feel like doing bindings, you can replace the first two steps with one line of code, right after InitializeComponent();
MainDrawer.DrawerLength = Window.Current.Bounds.Width;
Update
Looks like when the drawer is collapsed, the control itself resets its Opacity back to 1, and then when you constantly update the DrawerLength, the edge of the drawer could come out and give a bad user experience.
You can try to throttle page size changes with Reactive Extensions, or you can simply set the Opacity of the drawer back to 0 when it's collapsed.
To do this, you just need to monitor the DrawerState property and when it's Closed, set the DrawerGrid.Opacity to 0.
But remember you will also need to set it back to 1 before it gets expanded. Unfortunately there isn't an Opening state and Opened fires too late, so you have to locate the menu button and do it inside its Click event.
public MainPage()
{
InitializeComponent();
MainDrawer.DrawerLength = Window.Current.Bounds.Width;
Loaded += (s, e) =>
{
// GetChildByName: https://github.com/JustinXinLiu/Continuity/blob/master/Continuity/Extensions/UtilExtensions.cs#L44
var menuButton = MainDrawer.GetChildByName<Button>("PART_SideDrawerButton");
menuButton.Click += (s1, e1) => DrawerGrid.Opacity = 1;
};
MainDrawer.RegisterPropertyChangedCallback(RadSideDrawer.DrawerStateProperty, (s, e) =>
{
var state = MainDrawer.DrawerState;
if (state == DrawerState.Closed)
{
DrawerGrid.Opacity = 0;
}
});
}
I am trying to convert my WebView to a WebViewBrush in order to print it, in my UWP (C#/XAML) app.
I've set up my WebView, and the Brush such that when I click a button the WebView is hidden and the WebViewBrush gets displayed.
This is the XAML:
<WebView ext:Extension.HtmlString="{Binding Html}"
x:Name="saveWebView"
Grid.Row="1"
Grid.Column="0" />
<Rectangle Height="400" x:Name="saveWebViewBrush" />
When I click the button to show the Brush, basically it's only taking a snapshot of what was visible in the WebView. What I want is to take a snapshot of the entire WebView (and not the scrollbars either!).
The only other person I've seen attempt this was https://stackoverflow.com/a/17222629/2884981 - but unfortunately that's a few years old, and when I try that solution I get a million errors stemming from InvokeScript being obsolete and InvokeScriptAsync causes breaking changes.
The C# code when I press the button is this:
private async void OnButtonClick(object sender, RoutedEventArgs e)
{
//make the rectangle visible when you want something over the top of the web content
saveWebViewBrush.Visibility = Windows.UI.Xaml.Visibility.Visible;
//if the rectangle is visible, then hit the webview and put the content in the webviewbrush
if (saveWebViewBrush.Visibility == Windows.UI.Xaml.Visibility.Visible)
{
WebViewBrush b = new WebViewBrush();
b.SourceName = "saveWebView";
b.Redraw();
saveWebViewBrush.Fill = b;
saveWebView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
If anyone knows how to convert this whole WebView to a WebView brush I'd be most grateful.
EDIT
To explain the "why", I am trying to print the contents of a WebView. It seems from what I have read that this is not possible, unless I convert it to a WebViewBrush. But if anyone has any alternative ideas I am all ears!
If anyone knows how to convert this whole WebView to a WebView brush I'd be most grateful.
To achieve this, you can try with following method:
private async void OnButtonClick(object sender, RoutedEventArgs e)
{
int width;
int height;
// get the total width and height
var widthString = await saveWebView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
var heightString = await saveWebView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
if (!int.TryParse(widthString, out width))
{
throw new Exception("Unable to get page width");
}
if (!int.TryParse(heightString, out height))
{
throw new Exception("Unable to get page height");
}
// resize the webview to the content
saveWebView.Width = width;
saveWebView.Height = height;
WebViewBrush b = new WebViewBrush();
b.SourceName = "saveWebView";
b.Redraw();
saveWebViewBrush.Fill = b;
}
Please note that WebViewBrush.Redraw method happens asynchronously. So to make sure we can get the complete snapshot, we'd better not hide the WebView or we can add some delay before hide the WebView like:
await Task.Delay(500);
saveWebView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
Once we have the "saveWebViewBrush" Rectangle, we can refer to the Printing sample or this answer to print it.
I'm currently capturing the PointerMoved event on the page to use with a horizontal menu. So the user can swipe left/right and the page will animate accordingly.
This works when the user touches a static element (TextBlock etc.) but if they touch a ListView it captures the touch events.
How can I implement the ListView so when the user scrolls vertically it works as normal, but when the user scrolls horizontally it passes the events to my code?
It is possible, but you will need a small trick. As a refference I put here Rob Caplan's article.
Let's start:
First - where are your events? - answer is simple - while you have ScrollViewer enabled, all events are intercepted by it and handeled. You ListView will get only PointerEntered event and just after it PointerExited, all further proccesing is handeled by ScrollViewer. That is the problem. But as I've said there is a method to do what you want.
For this purpose lets assume that you have defined your ListView only with VerticalScroll:
<ListView Name="myList" ScrollViewer.HorizontalScrollMode="Disabled">
Of course it is possible to do for both directions, but it's a simple example.
Now let's have a look at constructor of a Page:
PointerPoint firstPoint = null;
ScrollViewer listScrollviewer = null;
public MainPage()
{
this.InitializeComponent();
myList.ItemsSource = yourItemSource;
myList.PointerEntered += myList_PointerEntered;
myList.PointerMoved += myList_PointerMoved;
}
Nothing weird here - I just subscribe to events, and declare two variables firstPoint and listScrollviewer, which I'll need later.
We will need also to get our ScrollViewer of our ListView - the following method will do the job:
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}
Now - to enable our events we will need to disable the ScrollViewer:
private ScrollViewer DisableScrolling(DependencyObject depObj)
{
ScrollViewer foundOne = GetScrollViewer(depObj);
if (foundOne != null) foundOne.VerticalScrollMode = ScrollMode.Disabled;
return foundOne;
}
We will disable the ScrollViewer upon PointerEntered event which is fired. In this step we will also remember the pressed PointerPoint - as we have disable Scrollviewer, we will have to scroll it manually - that is what we need this PointerPoint for.
private void myList_PointerEntered(object sender, PointerRoutedEventArgs e)
{
firstPoint = e.GetCurrentPoint(myList);
if (listScrollviewer == null) listScrollviewer = DisableScrolling(myList);
}
Finally our PointerMoved event, which now wil be fired as we had disabled ScrollViewer - moving ScrollViewer + other code you need to put there:
private void myList_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (listScrollviewer != null)
{
PointerPoint secondPoint = e.GetCurrentPoint(myList);
double verticalDifference = secondPoint.Position.Y - firstPoint.Position.Y;
listScrollviewer.ChangeView(null, listScrollviewer.VerticalOffset - verticalDifference, null);
}
// some other code you need
}
Few remarks:
this method still needs much tuning, but hopefuly will show you how to achieve your goal,
you may need also to separate some small horizontal movements from vertical ones,
if your ListView or other Control has horizontal scroll, then you will also need to disable and handle it,
this method won't probably work so smooth like original ScrollViewer.
I've also put a simple working example here at OneDrive.
I'm trying to make a custom scroll bar for Web browser Control.
I used a Scroll Bar Control for this,so i attached Scroll Bar Control to Web browser Control
use following code:
Doc = (mshtml.HTMLDocument)browser.Document;
Doc.parentWindow.document.body.style.overflow = "hidden";
mshtml.IHTMLElement2 ScrolablePlace= (mshtml.IHTMLElement2)Doc.getElementById("ScrolablePlace");
ScrollBar.ViewportSize = browser.ActualHeight;
ScrollBar.Maximum = ScrolablePlace.scrollHeight;
and while scroling scrolbar:
private void ScrollBar_Scroll(object sender, ScrollEventArgs e)
{
if (Doc != null)
{
Doc.parentWindow.scroll(0, (int)e.NewValue);
}
}
this is work,but ScrollBar.Maximum value is always larger than scroll bar place.dose ScrollBar.ViewportSize and ScrollBar.Maximum
set correctly?
I Hope i could explain my problem correctly with this image:
After some research, I've found that a scroll bar maximum property must calculate from this formula :
scrolbar.maximum=(maxsize-scrolbar.ViewportSize)+scrolbar.smallchanges
so i simply do this and it work fine:
ScrollBar.Maximum = ScrolablePlace.scrollHeight- browser.ActualHeight+ScrollBar.SmallChange;
I'm making an app for Windows Phone, I've been trying for ages to get the InputScope of the main text box to change when the orientation is changed to landscape (so that the keyboard takes up less space in landscape without the autocorrect bar), and back again.
I experimented with a second text box and hiding the others upon an orientation change, but this did not work neatly. :P
Try as I might I can't get this to work and can find no way to change the InputScope value after the OrientationChangedEvent argument, which has worked nicely in changing the position of the elements of the page around orientations.
I'm still fairly new to developing apps with C# and XAML, and I hope there's a nice easy way to set the InputScope of my text box that one of you awesome people could show me!
-EDIT : Here's the event handler, everything inside there works absolutely fine, but any way I try to add anything to do with InputScope does not work :(
private void MainPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
if ((e.Orientation & PageOrientation.Portrait) == (PageOrientation.Portrait))
{
//Portrait
PlaceholderText.FontSize = 29.333;
PlaceholderText.Padding = new Thickness (0,0,0,0);
MainTweet.FontSize = 29.333;
MainTweet.Padding = new Thickness (12,8,12,8);
Counter.Margin = new Thickness (0,212,28,0);
}
else
{
//Landscape
PlaceholderText.FontSize = 23;
PlaceholderText.Padding = new Thickness (8,0,0,0);
MainTweet.FontSize = 22;
MainTweet.Padding = new Thickness (16,8,180,0);
Counter.Margin = new Thickness (0,-18,28,0);
}
}
MainTweet.Text is the textbox that the keyboard is focusing on by default, when the page is changed to landscape I'd love to be able to change this from the "Search" InputScope to another, probably "URL". The stuff currently in there rearranges elements on the page nicely when the orientation is changed, I appreciate it might not look that neat...
There are multiple "orientation" states in the enumeration - not just Portrait and Landscape. The following worked for me to change the scope (on Windows Phone 7.5 emulator):
if (e.Orientation == PageOrientation.Landscape
|| e.Orientation == PageOrientation.LandscapeRight
|| e.Orientation == PageOrientation.LandscapeLeft)
{
InputScope inputScope = new InputScope();
InputScopeName inputScopeName = new InputScopeName();
inputScopeName.NameValue= InputScopeNameValue.Url;
inputScope.Names.Add(inputScopeName);
textBox1.InputScope = inputScope;
}
So you'd drop that into your MainPage_OrientationChanged event handler.