I have to show a WebView
inside a ScrollViewer
in Windows Phone 8.1 application, with the following requirements:
WebView
height should be adjusted based on its content. WebView
vertical scroll should be handled by an outer ScrollViewer
.WebView
should handle horizontal scroll, scale (pinch-zoom), text selection (with the default copy button) and links navigation.On the picture below is my mocked layout (to the left) and the best example of similar functionality - that would be a built-in mail application (to the right)
Sample XAML layout:
<ScrollViewer>
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="My content" />
</Grid>
<WebView Grid.Row="1" x:Name="WebViewComponent"></WebView>
</Grid>
</ScrollViewer>
Measure HTML content and adjusting the WebView height - This part worked and with several adjustments I was able to set the correct height to the WebView
element.
Subscribing to a Border element inside a WebView - Did not work. The problem here is that in Windows Phone 8.1 it seems that a WebView
component does not have visual children (at least not DependencyObject
's)
As well I've tried playing around with ManupulationMode
and IsHitTestVisible
properties with no success.
UPDATE
Added text selection & copy button to required WebView
functionality. Somehow missed it in the original question content.
I've been facing this problem, and i got some workaround partially (excluding the text selection).
Basically to be able to use the scrollviewer containing the webview, you need something to be dragged, so I use a rectangle with the height and width same as the webview.
<ScrollViewer Name="ScrollViewer">
<Grid x:Name="LayoutRoot" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Name="GridHeader">
<Image Stretch="UniformToFill" Source="ms-appx:///Assets/img_default_header.jpg" />
</Grid>
<Grid Grid.Row="1" x:Name="GridNews" Margin="12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<WebView Margin="-6,12,0,-6" Grid.Row="0" x:Name="WebviewContent" VerticalAlignment="Top" DefaultBackgroundColor="Transparent" />
<Rectangle Margin="0,12,0,0" Height="{Binding Height, ElementName=WebviewContent}" Name="RecWeb" VerticalAlignment="Top" Fill ="#00000000" Tapped="RecWeb_Tapped" />
</Grid>
<Grid Name="GridRelatedNews" Grid.Row="2" Margin="12,0">
<ListView ItemsSource="{Binding NewsDetail.RelatedStory}" ScrollViewer.VerticalScrollBarVisibility="Hidden">
</ListView>
</Grid>
</ScrollViewer>
Now we need to inject some css and javascript to make the webview height adapting to the content (I found some code example on another stackoverflow thread, forget to track where it comes from)
string htmlFragment = @"
<html>
<head>
<meta name=""viewport"" content=""width="+width+@", initial-scale=1, user-scalable=no"" />
<script type=""text/javascript"">
function loadLink(x,y){
window.external.notify('x='+x+'y='+y);
var el = document.elementFromPoint(x, y);
el.click();};
</script>
<style>
" + CSS + @"
</style>
</head>
<body onLoad=""window.external.notify('rendered_height='+document.getElementById('content').offsetHeight);"">
<div id='content' style=""font-size:16px; color:#222222;"">" + original +
@"</div>
</body>
</html>";
Then added webview webscript notify event to determine the rendered height of the webview (and rectangle):
void WebviewContent_ScriptNotify(object sender, NotifyEventArgs e)
{
if (e.Value.Contains("rendered_height"))
{
if (valuePair != null && valuePair[0] == "rendered_height")
{
double renderedHeight = double.Parse(valuePair[1]);
WebviewContent.Height = renderedHeight;
}
}
}
I manage to allow this solution to be able to click some link in the content using this method (the loadLink javascript method):
private async void RecWeb_Tapped(object sender, TappedRoutedEventArgs e)
{
Point p = e.GetPosition(WebviewContent);
int x = Convert.ToInt32(p.X);
int y = Convert.ToInt32(p.Y);
string[] size = { x.ToString(), y.ToString() };
await WebviewContent.InvokeScriptAsync("loadLink",size);
}
Maybe you can take it from there to add an event handler for your selecting text problem. The point using this solution is also to find an equivalent event handler in javascript.
I checked it out and tried a few things, but couldn't make it work the way you want it. It might be easier to come up with a different design. Basically, something that does not include an outer ScrollViewer
.
One option (which might be a bit hacky, haven't tested it) is to inject your content into the page, through some JS, and use only a WebView
.
If your content is small, you could just leave it on the top of the page, remove the ScrollViewer
and let the WebView
handle everything. The WebView
will be smaller, but it will work just fine.
These are just some examples of different designs. If you tell me why they wouldn't work, or if you give me more info about your actual problem, I might be able to come up with something more appropriate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With