UWP: `webview` contents cut off if window is resized - windows

I want to resize webview with window.
But, when I reduce the size of the window, the webview is cut off.
(When I grow the size of the window it looks normal.)
<Grid>
<WebView x:Name="m_WebView" HorizontalAlignment="Left" Height="350" Margin="0,0,0,0" VerticalAlignment="Top" Width="568" ScriptNotify="m_WebView_ScriptNotify" NavigationCompleted="m_WebView_OnNavigationCompleted" x:FieldModifier="public"/>
<ProgressRing x:Name="workingProgressRing" HorizontalAlignment="Center" VerticalAlignment="Center" Width="50" Height="50"/>
</Grid>
private void Page_onSizeChanged2(object sender, SizeChangedEventArgs e)
{
double widthRatio = e.NewSize.Width / (double)568;
double heightRatio = e.NewSize.Height / (double)320;
double newRatio = widthRatio < heightRatio ? widthRatio : heightRatio;
double newWidth = 568 * newRatio;
double newHeight = 320 * newRatio;
m_WebView.RenderTransform = new CompositeTransform { ScaleX = newRatio, ScaleY = newRatio };
((CompositeTransform)m_WebView.RenderTransform).TranslateX = -(newWidth - 568 * widthRatio) / 2;
((CompositeTransform)m_WebView.RenderTransform).TranslateY = -(newHeight - 320 * heightRatio) / 2;
}
Normal window picture.
Cut off webview picture.

The proper way to size controls in UWP is scaling the automatically based on the layout instead of directly hardcoding Width and Height values.
In your case you could remove the Width and Height attributes, set HorizontalAlignment and VerticalAlignment to Stretch and let the WebView scale with the Grid instead.

Related

FFImageLoading, SVGCachedImage blurry when setting HeightRequest/WidthRequest from a binding

I'm using FFImageLoading to display svg icons in my Xamarin.Forms project. My understanding is that the Height and Width requests must be set explicitly in order for the SVG to be rendered properly. I'm getting a lot of pixilation whenever I try to bind the Height/Width requests to values on the ViewModel (I have this as a need because the desired size depends on data). If I set the size explicitly everything looks fine.
Does SvgCachedImage not redraw the SVG whenever the bindings for Height/Width request change?
If not, is there a way for me to explicitly force them to invalidate and redraw when the size changes?
The blur issue was resolved by setting Horizontal and Vertical options to fill instead of Center:
<Grid>
<ffimageloadingsvg:SvgCachedImage BackgroundColor="Transparent"
Margin="{Binding HarmonicIconMargin}"
HorizontalOptions="Fill"
VerticalOptions="Fill"
WidthRequest="{Binding HarmonicIconWidth}"
HeightRequest="{Binding HarmonicIconWidth}"
Source="{Binding CurrentTestItem, Converter={StaticResource TestItemToHarmonicIconConverter}}" />
</Grid>
At that point it seemed be ignoring the height/width requests. I could've experimented around with that more (perhaps the request was for too much space) but I found that binding the margin to a computed property effectively enabled me to control the size of the SVG Image while not causing it to become blurred.
In order to solve svg blur issue when scale size of view,
Change SvgImageSource.VectorWidth or SvgImageSource.VectorHeight
Reload Image
protected override void OnSizeAllocated(double width, double height)
{
if (0 < width && 0 < height && && Source is SvgImageSource imageSource)
{
imageSource.VectorWidth = (int)Math.Ceiling(width);
imageSource.VectorHeight = (int)Math.Ceiling(height);
svgImage.ReloadImage();
base.OnSizeAllocated(width, height);
}
}
According to FFImageLoading source code, SVG image size is determined by SvgImageSource.VectorWidth or SvgImageSource.VectorHeight.
double sizeX = VectorWidth;
double sizeY = VectorHeight;
if (UseDipUnits)
{
sizeX = VectorWidth.DpToPixels();
sizeY = VectorHeight.DpToPixels();
}
if (sizeX <= 0 && sizeY <= 0)
{
if (picture.CullRect.Width > 0)
sizeX = picture.CullRect.Width;
else
sizeX = 400;
if (picture.CullRect.Height > 0)
sizeY = picture.CullRect.Height;
else
sizeY = 400;
}
else if (sizeX > 0 && sizeY <= 0)
{
sizeY = (int)(sizeX / picture.CullRect.Width * picture.CullRect.Height);
}
else if (sizeX <= 0 && sizeY > 0)
{
sizeX = (int)(sizeY / picture.CullRect.Height * picture.CullRect.Width);
}
resolvedData.ImageInformation.SetType(ImageInformation.ImageType.SVG);
using (var bitmap = new SKBitmap(new SKImageInfo((int)sizeX, (int)sizeY)))
using (var canvas = new SKCanvas(bitmap))
using (var paint = new SKPaint())
{
canvas.Clear(SKColors.Transparent);
var scaleX = (float)sizeX / picture.CullRect.Width;
var scaleY = (float)sizeY / picture.CullRect.Height;
var matrix = SKMatrix.MakeScale(scaleX, scaleY);
canvas.DrawPicture(picture, ref matrix, paint);
canvas.Flush();
token.ThrowIfCancellationRequested();
return await Decode(picture, bitmap, resolvedData).ConfigureAwait(false);
}
binding cause a pixellation because view's initial width and height are used as VectorWidth and VectorHeight, which is -1 or somthing you set as default for binding property. Therefore, your svg image resolution set too low at first and then binding process scales up view without redrawing svg image.

xamarin rounded image that scales

I have a Xaml page with a grid with relative heights
<RowDefinition Height="1*">
<RowDefinition Height="2*">
<RowDefinition Height="3*">
Now on the middle row (And i don't know it's exact height since it scales with the display size) I want to have a circular image. Since I have not set a heightrequest / widthrequest for the image I think I need to bind it to actual height.
I tried a lot of things resulting in my 'latest effort which is the following code but still does not give the desired result
<!-- try 1 -->
<yummy:PancakeView BackgroundColor="Aqua" CornerRadius="{Binding Source={RelativeSource Self}, Path=ActualHeight, Converter={converters:PercentageConverter}, ConverterParameter='0,5'}" IsClippedToBounds="True" BorderColor="Black" BorderThickness="4">
<Image Source="{Binding NarrationImage}" ></Image>
</yummy:PancakeView>
<!-- try 2 -->
<Grid x:Name="RefGrid" WidthRequest="1"></Grid>
<Frame
HeightRequest="{Binding Path=ActualHeight, Source={x:Reference RefGrid}}"
WidthRequest="{Binding Path=ActualHeight, Source={x:Reference RefGrid}}"
CornerRadius="{Binding Path=ActualHeight, Source={x:Reference RefGrid}}"
IsClippedToBounds="True" Padding="0" VerticalOptions="CenterAndExpand">
<Image Source="{Binding NarrationImage}" Aspect="AspectFill"></Image>
</Frame>
Since you had set the Height of Row as * . The real size of Frame in runtime depend on the size of Grid . In your case , the Height of Frame equals 1/3 of the Grid and the Width equals the width of the Grid .
If you want to get the value of them .You could create a custom Frame .And rewrite the method OnSizeAllocated
using Xamarin.Forms;
namespace App10
{
public class MyFrame:Frame
{
protected override void OnSizeAllocated(double width, double height)
{
if(width>0&&height>0)
{
var size =width<height ? width: height ;
CornerRadius = (float)size / 2;
}
base.OnSizeAllocated(width, height);
}
public MyFrame()
{
SizeChanged += MyFrame_SizeChanged;
}
private void MyFrame_SizeChanged(object sender, EventArgs e)
{
var width = this.Width;
var height = this.Height;
if (width > 0 && height > 0)
{
var size = width < height ? width : height;
CornerRadius = (float)size / 2;
}
}
}
}
This method will been invoked multi times when it been first added to the parent view. The last time it will return the current size . You can do something you want .

Xamarin Forms Resize Image inside Listview

How can I set the size of the image inside of a list view. Currently I have several lists that have icons but I don't see any options for changing the aspect ratio or size of the image so it just gets blown up to the height of the list item.
all the images are from the Drawable folder
var cell = new DataTemplate(typeof(MenuTextCell));
cell.SetBinding(TextCell.TextProperty, "Title");
cell.SetBinding(ImageCell.ImageSourceProperty, "IconSource");
cell.SetValue(BackgroundColorProperty, Color.Transparent);
cell.SetValue(TextCell.TextColorProperty, Color.FromHex("262626"));
I am using a custom renderer
public class MenuTextCellRenderer : ImageCellRenderer
{
protected override View GetCellCore (Cell item, View convertView, ViewGroup parent, Context context)
{
var cell = (LinearLayout)base.GetCellCore (item, convertView, parent, context);
cell.SetPadding(20, 10, 0, 10);
cell.DividerPadding = 50;
var div = new ShapeDrawable();
div.SetIntrinsicHeight(1);
div.Paint.Set(new Paint { Color = Color.FromHex("b7b7b7").ToAndroid() });
if (parent is ListView)
{
((ListView)parent).Divider = div;
((ListView)parent).DividerHeight = 1;
}
var icon = (ImageView)cell.GetChildAt(0);
var label = (TextView)((LinearLayout)cell.GetChildAt(1)).GetChildAt(0);
label.SetTextColor(Color.FromHex("262626").ToAndroid());
label.TextSize = Font.SystemFontOfSize(NamedSize.Large).ToScaledPixel();
label.TextAlignment = TextAlignment.Center;
label.Text = label.Text.ToUpper();
var secondaryLabel = (TextView)((LinearLayout)cell.GetChildAt(1)).GetChildAt(1);
secondaryLabel.SetTextColor(Color.FromHex("262626").ToAndroid());
secondaryLabel.TextSize = Font.SystemFontOfSize(NamedSize.Large).ToScaledPixel();
label.TextAlignment = TextAlignment.Center;
return cell;
}
You are now dealing with an Android ImageView.
You have your reference via var icon = (ImageView)cell.GetChildAt(0).
You can now customize it via methods such as .setScaleType() for aspect ratio related, and use .layout() to change the position / size.
Try something like this on your ListView.ItemTemplate, instead of using the imagecell. You can also write your custom cell.
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<Image Aspect="AspectFill" HeightRequest ="20" WidthRequest="20" Source="{Binding IconSource}" />
<Label Text="{Binding Title}" YAlign="Center" Font="Medium" />
</StackLayout>
</ViewCell>

Crop Image in Canvas or scrollview?

I have put an image in the scrollview and canvas as xaml below:
<ScrollViewer x:Name="scroll" HorizontalAlignment="Center" VerticalAlignment="Center" Width="340" Height="480">
<Canvas x:Name="canvas" HorizontalAlignment="Center" VerticalAlignment="Center" Width="340" Height="480" Background="Blue">
<Image x:Name="photo" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" ManipulationMode="All" Width="340" Height="480">
<Image.RenderTransform>
<CompositeTransform/>
</Image.RenderTransform>
</Image>
</Canvas>
</ScrollViewer>
After that, I created a button to load and crop the image:
private async void btnCrop_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.FileTypeFilter.Add(".jpeg");
fileOpenPicker.FileTypeFilter.Add(".png");
fileOpenPicker.FileTypeFilter.Add(".bmp");
file = await fileOpenPicker.PickSingleFileAsync();
if (file != null)
{
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
BitmapTransform transform = new BitmapTransform();
BitmapBounds bounds = new BitmapBounds();
bounds.X = bounds.Y = 0;
bounds.Height = bounds.Width = 150;
transform.Bounds = bounds;
PixelDataProvider pix = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
byte[] pixels = pix.DetachPixelData();
WriteableBitmap cropBmp = new WriteableBitmap(340, 480);
Stream pixStream = cropBmp.PixelBuffer.AsStream();
pixStream.Write(pixels, 0, 150 * 150 * 4);
photo.Source = cropBmp;
}
}
The image has been crop and displayed successful. But when I zoom my image, I just want crop the image within the canvas instead of hard code. The code above is hard code the BitmapBounds width and height. How do I solve it? Thanks

Pushpin size at WP7 map control

while trying to implement logic that show current user location i encountered an issue.
<Maps:Pushpin Location="{Binding MyLocation}" Canvas.ZIndex="1000" PositionOrigin="Center" >
<Maps:Pushpin.Template>
<ControlTemplate>
<Grid>
<Ellipse Width="{Binding MyAccuracyViewSize}" Height="{Binding MyAccuracyViewSize}"
Fill="#60008000" Stroke="Green" StrokeThickness="3"/>
<Ellipse Width="18" Height="18" Fill="#A0FF4500" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</Maps:Pushpin.Template>
</Maps:Pushpin>
Bigger green circle shows accuracy area. Its size in pixels varies depending on zoom. If zoom level is big - it becomes rather big (> 480 pixels). At that point it gets cropped by screen resolution. AFAIK WP7 restriction is 2000x2000 px for control size.
Seems that this is a kind of a map control restriction.
Any ideas how to remove this restriction to show ellipse of size up to 2000x2000 px?
Thanks!
How about MapPolygon?
void OnPositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
SecondsCounter = 0; //reset counter
double accuracy = e.Position.Location.HorizontalAccuracy;
if (accuracy < e.Position.Location.VerticalAccuracy)
{
accuracy = e.Position.Location.VerticalAccuracy;
}
if (PolyCircle == null)
{
PolyCircle = new MapPolygon();
PolyCircle.Opacity = 0.7;
//Set the polygon properties
PolyCircle.Fill = new SolidColorBrush(Colors.Orange);
PolyCircle.Stroke = new SolidColorBrush(Colors.Purple);
PolyCircle.StrokeThickness = 4;
map1.Children.Add(PolyCircle);
}
PolyCircle.Locations = CreateCircle(e.Position.Location, accuracy);
map1.Center = e.Position.Location;
}
public static double ToRadian(double degrees)
{
return degrees * (Math.PI / 180);
}
public static double ToDegrees(double radians)
{
return radians * (180 / Math.PI);
}
public static LocationCollection CreateCircle(GeoCoordinate center, double radius)
{
var earthRadius = 6367000; // radius in meters
var lat = ToRadian(center.Latitude); //radians
var lng = ToRadian(center.Longitude); //radians
var d = radius / earthRadius; // d = angular distance covered on earth's surface
var locations = new LocationCollection();
for (var x = 0; x <= 360; x++)
{
var brng = ToRadian(x);
var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));
var lngRadians = lng + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));
locations.Add(new GeoCoordinate(ToDegrees(latRadians), ToDegrees(lngRadians)));
}
return locations;
}
More here, in My Map position example. Good luck!

Resources