I using mapsui to show an IGN map on my application and I want to make a screenshot of the map when I click on a specific button.
So I use dependency injection and It works perfectly on UWP.
But with Android, I can't screen the map and I have a white screen.
There is my code for Android :
public Task<byte[]> TakeShot()
{
var _activity = CrossCurrentActivity.Current;
if (_activity == null)
{
throw new Exception("You have to set ScreenshotManager.Activity in your Android project");
}
var view = _activity.Activity.Window.DecorView;
view.DrawingCacheEnabled = true;
Bitmap bitmap = view.GetDrawingCache(true);
view.BuildDrawingCache();
byte[] bitmapData;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return Task.FromResult(bitmapData);
}
Only the map is blank, because I have my button on the image.
Thank's for your help
I used the Mapsui, then I create a screenshot function with dependence service, Here is my GIF when take a screemshot.
Here is my dependence service about achieveing screenshot.
[assembly: Dependency(typeof(ScreenshotService))]
namespace MVVMDataBinding.Droid
{
public class ScreenshotService : IScreenshotService
{
public byte[] Capture()
{
var _activity = CrossCurrentActivity.Current;
var rootView = _activity.Activity.Window.DecorView;
using (var screenshot = Bitmap.CreateBitmap(
rootView.Width,
rootView.Height,
Bitmap.Config.Argb8888))
{
var canvas = new Canvas(screenshot);
rootView.Draw(canvas);
using (var stream = new MemoryStream())
{
screenshot.Compress(Bitmap.CompressFormat.Png, 90, stream);
return stream.ToArray();
}
}
}
}
}
And create an interface IScreenshotService in the xamarin forms.
public interface IScreenshotService
{
byte[] Capture();
}
Use it in the xamarin forms.
private void Button_Clicked(object sender, EventArgs e)
{
var screenshotData = DependencyService.Get<IScreenshotService>().Capture();
myImage.Source=ImageSource.FromStream(() => new
MemoryStream((byte[])screenshotData));
}
forground code.
<ContentPage.Content>
<StackLayout>
<Grid x:Name="ContentGrid" WidthRequest="100" HeightRequest="200" />
<Button Text="Take a screen" Clicked="Button_Clicked"/>
<Image x:Name="myImage" WidthRequest="200" HeightRequest="300"/>
</StackLayout>
</ContentPage.Content>
Here is my demo.
https://github.com/851265601/MapsuiWithScreenshotDemo
Related
I'm trying to use the MediaElement to play a video selected from the Gallery. The path to the Video is saved within the app then bound to the MediaElement Source.
<xct:MediaElement Source="{Binding VideoUri, Converter={StaticResource VideoSourceConverter}}"
AutoPlay="False"
ShowsPlaybackControls="True"
Aspect="AspectFit"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
I'm using a converter as described in the documentation:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return null;
if (string.IsNullOrWhiteSpace(value.ToString()))
return null;
if (value.ToString().StartsWith("http"))
return value;
return new Uri($"ms-appdata:///{value}");
}
https://learn.microsoft.com/en-gb/xamarin/community-toolkit/views/mediaelement#play-local-media
But am getting the error: Invalid UriParameter name: Source
The saved path on Android is:
"/storage/emulated/0/Android/data/[app identifier]/files/Movies/temp/[filename].mp4"
Haven't tested in iOS yet.
Any guidance will be much appreciated.
Thanks.
The Converter you used is used for the UWP. UWP could play media files that are located in the app's xxxx folder by prefixing the media file with ms-appdata:///xxxx/.
For mobile devices, when you use the CrossMedia to select the video, you could get the stream directly from the file.
I use a button to do the select operation and then use the INotifyPropertyChanged to update the binding. Here is the code for your reference.
Xaml:
<Button Clicked="Button_Clicked" Text="Select"/>
<xct:MediaElement Source="{Binding VideoUri}"
AutoPlay="False"
ShowsPlaybackControls="True"
Aspect="AspectFit"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
Code:
public partial class Page29 : ContentPage
{
Page29ViewModel viewModel = new Page29ViewModel();
public Page29()
{
InitializeComponent();
this.BindingContext = viewModel;
}
private async void Button_Clicked(object sender, EventArgs e)
{
string path = string.Empty;
MediaFile video = null;
if (CrossMedia.Current.IsPickVideoSupported)
{
video = await CrossMedia.Current.PickVideoAsync();
}
var fileName = "sample";
var newFile = Path.Combine(FileSystem.AppDataDirectory, fileName + ".mp4");
if (!File.Exists(newFile))
{
using (var inputStream = video.GetStream())
{
using (FileStream outputStream = File.Create(newFile))
{
await inputStream.CopyToAsync(outputStream);
}
}
}
viewModel.VideoUri = newFile;
}
}
public class Page29ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _videoUri;
public string VideoUri
{
get { return _videoUri; }
set { _videoUri = value; NotifyPropertyChanged(nameof(VideoUri)); }
}
public Page29ViewModel()
{
}
}
You dont need the converter just return the path to the mediaplayer control .
mediaFile = await this._mediaPicker.PickVideoAsync();
VideoUri = mediaFile.Path;
Hey I'm new to Xamarin and I'm hoping you guys could help me. Since there isn't a default folder-picker in xamarin, I want to implement it myself. The problem is that UWP as well as Android throw me this exception:
System.UnauthorizedAccessException
HResult=0x80070005
Nachricht = Access to the path 'C:\Users\imtt\AppData\Local\Packages\3ef1aa30-7ffe-4ece-84c7-d2eaf1f8634b_wvdsmkc2tee92\LocalState\Test\199.jpg' is denied.
Quelle = System.IO.FileSystem
Stapelüberwachung:
bei System.IO.FileSystem.DeleteFile(String fullPath)
bei System.IO.File.Delete(String path)
bei MinimalReproducibleExample.ViewModel.DeleteFiles() in C:\Users\imtt\source\repos\MinimalReproducibleExample\MinimalReproducibleExample\MinimalReproducibleExample\ViewModel.cs: Zeile107
bei Xamarin.Forms.Command.<>c__DisplayClass4_0.<.ctor>b__0(Object o)
bei Xamarin.Forms.Command.Execute(Object parameter)
bei Xamarin.Forms.ButtonElement.ElementClicked(VisualElement visualElement, IButtonElement ButtonElementManager)
bei Xamarin.Forms.Button.SendClicked()
bei Xamarin.Forms.Platform.UWP.ButtonRenderer.OnButtonClick(Object sender, RoutedEventArgs e)
Here's the xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MinimalReproducibleExample.MainPage">
<StackLayout>
<Button Text="Add Image" Command="{Binding AddImage}"/>
<Button Text="Delete Images" Command="{Binding DeleteImages}"/>
<Image Source="{Binding CreatedImage}"/>
</StackLayout>
Here's the code-behind:
using Xamarin.Forms;
namespace MinimalReproducibleExample
{
public partial class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new ViewModel();
InitializeComponent();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace MinimalReproducibleExample
{
public class ViewModel : INotifyPropertyChanged
{
private ImageSource image;
private string fileFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Test");
public ICommand AddImage { get; }
public ICommand DeleteImages { get; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public ViewModel()
{
AddImage = new Command(ShowFilePicker);
DeleteImages = new Command(DeleteFiles);
}
public ImageSource CreatedImage
{
get => image;
set
{
image = value;
OnPropertyChanged();
}
}
public async void ShowFilePicker()
{
FilePickerFileType filePickerFileType = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>> {
{ DevicePlatform.iOS, new [] { "jpeg", "png", "mp3", "mpeg4Movie", "plaintext", "utf8PlainText", "html" } },
{ DevicePlatform.Android, new [] { "image/jpeg", "image/png", "audio/mp3", "audio/mpeg", "video/mp4", "text/*", "text/html" } },
{ DevicePlatform.UWP, new []{ "*.jpg", "*.jpeg", "*.png", "*.mp3", "*.mp4", "*.txt", "*.html" } }
});
PickOptions pickOptions = new PickOptions
{
PickerTitle = "Wählen Sie eine oder mehrere Dateien aus",
FileTypes = filePickerFileType,
};
IEnumerable<FileResult> pickedFiles = await FilePicker.PickMultipleAsync(pickOptions);
List<FileResult> results = pickedFiles.ToList();
if (results != null && results.Count > 0)
{
foreach (FileResult fileResult in results)
{
using (Stream stream = await fileResult.OpenReadAsync())
{
DirectoryInfo directoryInfo = Directory.CreateDirectory(fileFolder);
string directoryPath = directoryInfo.FullName;
string filepath = Path.Combine(directoryPath, fileResult.FileName);
try
{
byte[] bArray = new byte[stream.Length];
using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate))
{
stream.Read(bArray, 0, (int)stream.Length);
int length = bArray.Length;
fs.Write(bArray, 0, length);
}
CreatedImage = ImageSource.FromFile(filepath);
}
catch (Exception exc)
{
}
}
}
}
}
public void DeleteFiles()
{
string[] filePaths = Directory.GetFiles(fileFolder);
foreach(string filePath in filePaths)
{
File.Delete(filePath);
}
}
}
}
I already gave my app the access to the filesytem via windows settings, also I gave the Android-part read and write access. I even gave the UWP-part "broadFileAccess" and even that didn't make the cut.
This intersects with another problem, where the UWP part can write files into a folder in "Environment.SpecialFolder.LocalApplicationData", but it isn't allowed to delete the files in this folder.
Does this have something to do with the sandboxes of UWP and Android?
App cannot further process disks / folder in Xamarin
I tested with you code, the problem is the file was using by image control when you delete it, if we disable CreatedImage = ImageSource.FromFile(filepath); this line. it will work as expect.
I need that image control to display the image
We suggest you render image with stream but not create source from file directly.
For example
CreatedImage = ImageSource.FromStream(() => stream);
<ListView x:Name="PictureListView" HasUnevenRows="True" WidthRequest="320" HeightRequest="400" SeparatorColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Image Source="{Binding Path}" HeightRequest="80" WidthRequest="80" Aspect="AspectFill" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ObservableCollection<Image> Pictures = new ObservableCollection<Image>(new Image { Source = "file.png" });
PictureListView.ItemSource = Pictures;
private async void PictureListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var photo = (Image)e.Item;
photo.Source = "file2.png";
}
i am trying to update an image in a listview using xamarin forms....i have the code listed above....but when the click fires, the new image ("file2.png") doesn't get repainted to the screen. Instead, i just an empty space for where the image would have been. How do i change an image out in a listview?
You are using ObservableCollection which will only update the list view when the collection is updated (add, move, remove). In your case, you are only updating the content of the item, so it will not reflect in the screen.
You should implement your code with proper data binding or MVVM. Read more about MVVM HERE.
If you do not want to implement the MVVM, you can use the following tricks.
private async void PictureListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var photo = (Image)e.Item;
var newImage = new Image { Source = "file2.png" };
var index = Pictures.IndexOf(photo);
Pictures.Remove(photo);
Pictures.Insert(index, newImage);
}
You should implement INotifyPropertyChanged
Here a sample code
using System;
using System.ComponentModel;
using Xamarin.Forms;
namespace XamlSamples
{
class ClockViewModel : INotifyPropertyChanged
{
DateTime dateTime;
public event PropertyChangedEventHandler PropertyChanged;
public ClockViewModel()
{
this.DateTime = DateTime.Now;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
this.DateTime = DateTime.Now;
return true;
});
}
public DateTime DateTime
{
set
{
if (dateTime != value)
{
dateTime = value;
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs("DateTime"));
}
}
}
get
{
return dateTime;
}
}
}
}
Here some docs
data_bindings_to_mvvm
I want to know how I can display video from jpegs in Xamarin (all platforms).
My jpegs are being streamed from a http client stream sent by a popular video surveillance management software.
My jpegs are in the form of byte[] and I get about 10 jpegs/second. This format is imposed.
I tried rapidly changing the Source on a Image but it results in severe fliquering on Android. This seems to work on Windows phone but not so good performance.
How can I create a videoplayer for each one? Unless I am wrond, the existing components cannot do this.
Best,
Thank you Jason! Works great, very fluid rendering!!
Simply add the SkiaSharp.Views.Forms with NuGet to the project and voila!
Here is what that would look like in code (shared project):
// Content page initialization
private void InitUI() {
Title = "Xamavideo";
var button = new Button
{
Text = "Connect!"
};
Label label = new Label
{
Text = ""
};
var scroll = new ScrollView();
scroll.BackgroundColor = Color.Black;
Content = scroll;
var stack = new StackLayout
{
Padding = 40,
Spacing = 10
};
//Add a SKCanvasView item to the stack
var videoCanvas = new SKCanvasView
{
HeightRequest = 400,
WidthRequest = 600,
};
videoCanvas.PaintSurface += OnCanvasViewPaintSurface;
stack.Children.Add(videoCanvas);
}
//Create the event handler
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
// using (var stream = new SKManagedStream(fileStream))
if (lastFrame == null) return;
using (var canvas = surface.Canvas)
// use KBitmap.Decode to decode the byte[] in jpeg format
using (var bitmap = SKBitmap.Decode(lastFrame))
using (var paint = new SKPaint())
{
// clear the canvas / fill with black
canvas.DrawColor(SKColors.Black);
canvas.DrawBitmap(bitmap, SKRect.Create(640, 480), paint);
}
}
void UpdateFrame(VideoClient client){
//Use this to update the canvas:
byte[] lastFrame = client.imageBytes;
videoCanvas.InvalidateSurface();
}
I am trying to play the GIF images with the Xamarin.Forms (Portable) project. I have implemented with the following code but unable to Play GIF, I don't event see the static image.
There is no error or crash in this code, but I am not able to see the
GIF image.
Is there anything wrong in the code?
Interface:
public interface IGif
{
string Get();
}
iOS Implementation:
[assembly: Dependency(typeof(GifView_iOS))]
namespace Project.iOS
{
public class GifView_iOS : IGif
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Android Implementation:
[assembly: Dependency(typeof(GifView_Droid))]
namespace Project.Droid
{
public class GifView_Droid : IGif
{
public string Get()
{
return "file:///android_asset/";
}
}
}
GIF Image class:
public class Gif: WebView
{
public string GifSource
{
set
{
string imgSource = DependencyService.Get<IGif>.Get() + value;
var html = new HtmlWebViewSource();
html.Html = String.Format
(#"<html><body style='background: #000000;'><img src='{0}' /></body></html>",
imgSource);
SetValue(SourceProperty, html);
}
}
}
And Finally, I have added to the StackLayout.
Gif gifimage = new Gif();
gifimage.GifSource = "loading.gif";
StackGif.Children.Add(gifimage);
I have not tested this code in iPhone, maybe it is working in iPhone.
Thank You in advance.
This was the silly mistake, I have to give the size of Image inside web view and to GIF class also.
Here is the updated code.
My GIF Class:
public class Gif: WebView
{
public string GifSource
{
set
{
string imgSource = DependencyService.Get<Dependency.IGif>().Get() + value;
var html = new HtmlWebViewSource();
html.Html = String.Format(#"<html><body><img src='{0}' style='width: 100px; height: 100px;' /></body></html>", imgSource);
SetValue(SourceProperty, html);
}
}
}
and Initialization in ContentPage:
Helpers.Controls.Gif gifImage = new Helpers.Controls.Gif();
gifImage.GifSource = "loading.gif";
gifImage.HorizontalOptions = LayoutOptions.Center;
gifImage.VerticalOptions = LayoutOptions.FillAndExpand;
gifImage.HeightRequest = 120;
gifImage.BackgroundColor = Color.Transparent;
GridLoad.Children.Add(gifImage);
Note: I am still working on the Background Part, as it is looking like
attached image, I want the transparent background in web view.
Image:
By default StackLayout occupies the whole available space but WebView doesn't.
Instead just
Gif gifimage = new Gif();
use
public WebViewProgrammaticallyPage()
{
var StackGif = new StackLayout()
{
BackgroundColor=Color.Red,
};
Gif gifimage = new Gif()
{
WidthRequest = 120,
HorizontalOptions = LayoutOptions.CenterAndExpand,
HeightRequest = 80,
VerticalOptions = LayoutOptions.CenterAndExpand,
};
gifimage.GifSource = "icon1.png";
StackGif.Children.Add(gifimage);
Content = StackGif;
}
Transparency of WebView is different problem. You should use custom renderers
https://forums.xamarin.com/discussion/34957/creating-a-webview-with-a-transparent-background