How do you allow users to copy and paste from an Xamarin.Forms label - xamarin

How do you allow users to copy and paste from an Xamarin.Forms Label?
Click on the text on any platform the default settings don't allow highlighting and therefore copying and pasting.
Any help would be appreciated.

What you could do is wrap your label in a gesture recogniser:
<Label Text="Test">
<Label.GestureRecognizers>
<TapGestureRecognizer
Tapped="YourFunctionToHandleMadTaps"
NumberOfTapsRequired="1"
/>
</Label.GestureRecognizers>
</Label>
This will trigger your function and in that function you can get to the clipboard and copy and paste. However I haven't been able to find an easy way to access the clipboard in Xamarin.Forms so you have to use the dependency service.
Xamarin.Forms Dependency service documentation
Here is how I did my clipboard data access. Please note that in my project I only needed to nab data from the clipboard so this code just shows you how to access the clipboard data:
Create an interface in you X.F project eg:
public interface IClipBoard
{
String GetTextFromClipBoard();
}
Implement the interface in your mobile projects:
Android:
public string GetTextFromClipBoard ()
{
var clipboardmanager = (ClipboardManager)Forms.Context.GetSystemService (Context.ClipboardService);
var item = clipboardmanager.PrimaryClip.GetItemAt(0);
var text = item.Text;
return text;
}
iOs:
public string GetTextFromClipBoard ()
{
var pb = UIPasteboard.General.GetValue ("public.utf8-plain-text");
return pb.ToString ();
}
Don't forget to add the Assembly bits at the top:
iOs: [assembly: Dependency (typeof (ClipBoard_iOs))]
Android: [assembly: Dependency (typeof (ClipBoard_Droid))]
Call the dependency service from you X.F function
public void YourFunctionToHandleMadTaps(Object sender, EventArgs ea)
{
var clipboardText = DependencyService.Get<IClipBoard> ().GetTextFromClipBoard ();
YourFunctionToHandleMadTaps.Text = clipboardText;
}

Since I_Khanage provided only a half solution, I will post the full solution.
IClipboardService should be implemented for all the targeting platforms, in my case it is Android and iOS:
public interface IClipboardService
{
string GetTextFromClipboard();
void SendTextToClipboard(string text);
}
// iOS
public class ClipboardService : IClipboardService
{
public string GetTextFromClipboard() => UIPasteboard.General.String;
public void SendTextToClipboard(string text) => UIPasteboard.General.String = text;
}
// Android
public class ClipboardService : IClipboardService
{
public string GetTextFromClipboard()
{
var clipboardmanager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
var item = clipboardmanager.PrimaryClip.GetItemAt(0);
var text = item.Text;
return text;
}
public void SendTextToClipboard(string text)
{
// Get the Clipboard Manager
var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
// Create a new Clip
var clip = ClipData.NewPlainText("YOUR_TITLE_HERE", text);
// Copy the text
clipboardManager.PrimaryClip = clip;
}
}
The code is available on github.
Now just add a GestureRecognizer in order to trigger the tap event.
P.S.: Clipboard service is now available as a part of Xamarin.Essentials package, so there is no need to write one yourself.

Related

FontAwesome icon with normal text in Xamarin

I want to make a button that has a small icon (from FontAwesome) and text on it in my Xamarin app. I know I can just make a button but the problem is that I will require two fonts (the standard font for text and FontAwesome for the icon). Would anyone happen to know how I can do this, or if there is another way to achieve what I want?
Thanks!
As the json mentioned, I just made a simple implementation.
Create a new class inherit from Label, set FormattedText to combine the string(standard and icon), and add tap gesture on it .
public class MyLabel : Label
{
public delegate void MyHandler(object sender, EventArgs e);
public event MyHandler myEvent;
public MyLabel(string _myIcon, string _myText)
{
//build the UI
FormattedString text = new FormattedString();
text.Spans.Add(new Span { Text = _myIcon ,FontFamily= "FontAwesome5Free-Regular" });
text.Spans.Add(new Span { Text = _myText, TextColor = Color.Red ,BackgroundColor = Color.Blue });
FormattedText = text;
//tap event
TapGestureRecognizer tap = new TapGestureRecognizer();
tap.Tapped += (sender,e) => {
myEvent(sender,e);
};
}
}
Usage
MyLabel label = new MyLabel("", "test");
label.myEvent += (sener,e) =>
{
//do something when tapping
};
Content = label;
For how to integrate FontAwesome in Xamarin.Forms ,refer to
https://montemagno.com/xamarin-forms-custom-fonts-everywhere/.

How to hide clear button Icon inside SearchBar control Xamarin forms

I am using xamarin forms SearchBar control. I want to remove clear button x icon without using custom renderer.
<controls:ExSearchBar
x:Name="entrySearch"
BackgroundColor="White"
CornerRadius="6"
BorderWidth="1"
HeightRequest="45"
Text="{Binding SearchText}"
HorizontalOptions="FillAndExpand"
Placeholder="search">
</controls:ExSearchBar>
This is ExSearchBar control in shared project
public class ExSearchBar : SearchBar
{
public static readonly BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(ExFrame), default(float));
public float Elevation
{
get { return (float)GetValue(ElevationProperty); }
set { SetValue(ElevationProperty, value); }
}
}
How can I do that?
The situation you are describing is the exact reason why Xamarin Forms ships with the ability to create custom renderers. The forms team define the UI elements in abstract (seperate from their native implementation) and when there is a specific feature that is not defined in their API, you must go down to the platform level to change it.
You can also use an Effect to achieve the same result, I have provided a custom renderer for iOS & Android to show you how you would go about achieving the UI you desire:
iOS:
public class SearchBarButtonRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
Control.SearchTextField.ClearButtonMode = UITextFieldViewMode.Never;
}
}
}
Really simple, just remove the clear button from the underlying UITextField
Android
public class SearchBarButtonRenderer : SearchBarRenderer
{
private readonly Context _context;
public SearchBarButtonRenderer(Context context)
: base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
// Get Search Close Button Drawable
var closeButtonId = Resources.GetIdentifier("android:id/search_close_btn", null, null);
var searchEditText = Control.FindViewById<ImageView>(closeButtonId);
// Get Close Button Drawable To Replace Existing Drawable
var closeDrawable = GetCloseButtonDrawable() as VectorDrawable;
if (closeDrawable is null) return;
// Apply Transparent Color To Drawable (To Make Invisible)
var buttonColor = Xamarin.Forms.Color.Transparent.ToAndroid();
closeDrawable.SetTint(buttonColor);
// Set Drawable On Control
searchEditText.SetImageDrawable(closeDrawable);
}
}
private Drawable GetCloseButtonDrawable()
{
return ContextCompat.GetDrawable(_context, Resource.Drawable.abc_ic_clear_material);
}
}
A little bit of a fiddle, find the close button drawable and replace it with a custom styled drawable

How to open file .doc use "Word" app on Xamarin Forms?

Use Xamarin Forms and on iOS, how to check and open Word app when Open file by UrL?
Reference: https://learn.microsoft.com/en-us/office/client-developer/integration/integrate-with-office-from-ios-applications
This is my code, it isn't work:
Device.OpenUri(new Uri("ms-word:ofe|u|https://calibre-ebook.com/downloads/demos/demo.docx"));
Please help me!
Thanks!
What you are asking here can be done using a simple WebView in iOS:
First, create a custom WebView class that allows you to pick file uri:
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(CustomWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
Then in ios make a renderer for the same and do something like this:
[assembly: ExportRenderer (typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace DisplayPDF.iOS
{
public class CustomWebViewRenderer : ViewRenderer<CustomWebView, UIWebView>
{
protected override void OnElementChanged (ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged (e);
if (Control == null) {
SetNativeControl (new UIWebView ());
}
if (e.OldElement != null) {
// Cleanup
}
if (e.NewElement != null) {
var customWebView = Element as CustomWebView;
string fileName = Path.Combine (NSBundle.MainBundle.BundlePath, string.Format ("Content/{0}", WebUtility.UrlEncode (customWebView.Uri)));
Control.LoadRequest (new NSUrlRequest (new NSUrl (fileName, false)));
Control.ScalesPageToFit = true;
}
}
}
}
Then use the custom control like this:
<local:CustomWebView Uri="FooPath" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
Where FooPath is the path for the doc file.
I have solution for my project, I try it:
If you only open file .docx on iOS, you could write code at Share Code of iOS:
Check device of user has setup app (Word, Excel, PP, etc...)
public static bool HasSetupAppDocument(string extension)
{
if (string.IsNullOrEmpty(extension))
return false;
// Device has setup app?
var result = UIApplication.SharedApplication.CanOpenUrl(NSUrl.FromString($"{extension}"));
return result;
}
(Ex: extension is ms-word: or ms-excel: or ms-excel:
Reference: https://learn.microsoft.com/en-us/office/client-developer/integration/integrate-with-office-from-ios-applications#office-protocols)
Notes: Add source to Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>ms-word</string>
<string>ms-excel</string>
<string>ms-powerpoint</string>
</array>
At Class Dependencies, open file with URL:
Device.OpenUri(new Uri($"{convertExtension}{url}"));
Note: url is link file share on Onedrive and Be sure that account Onedrive as same as account login Word app (if you had set security).
If file has mode Read-only, app Word will open file with mode Read-only.

Displaying an image with Xamarin Forms

Solved: The answer was to update all of the nuget packages and target a newer version of Android. Now images loads as expected. I'm not happy with this as I was using exactly the code that Xamarin provided and targeting newer versions has deprecated some of the items the code relys on. Initial version was Xamarin.Forms v23 and I updated to V25
I have a brand new Xamarin forms project with a simple view in which I'm trying to display an image. I've tried several ways of getting an image to display and I am not having any luck at all.
I'm using <image> and I have also tried FFImageLoader control as well.
<StackLayout Orientation="Vertical">
<ff:CachedImage Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" WidthRequest="100" HeightRequest="100" />
<Button x:Name="btn" Text="Image" Clicked="Button_Clicked" />
<Frame OutlineColor="Red">
<Image x:Name="StupidImage" Source="{Binding Thumbnail}" Aspect="Fill" HeightRequest="100" WidthRequest="100" />
</Frame>
</StackLayout>
This is the current view. I've also set the Source directly to a value with no result.
I'm able to get a stream for the image. I'm able to read all of the bytes from the stream. I built a debug visualizer to display the bytes as an image. Getting the image from a source is not a problem. Getting the image control(s) to display the image is a problem.
I tried binding with a view model. When that failed, I tried that directly setting the source
StupidImage.Source = ImageSource.FromStream(() => result.Stream);
I also made a copy of the bytes and tried
StupidImage.Source = ImageSource.FromStream(() => new MemoryStream(imageBytes));
I've tried ImageSource.FromFile() and .FromUri. I tried adding an image to the project as a resource. Each try was the same, the resource was read and the bytes were available, but the image control just doesn't display it.
I thought maybe it was a size problem, so I set the size of the control. Nothing. I thought maybe it was a resolution problem, so I used a smaller image. I tried several different images of varying quality.
Then I gave up on the image control and I got the FFImageLoading nuget package and gave it a direct url to an image. Same example that FFImageLoading examples used. Still no image.
I tried the emulator and I tried 2 different physical devices. Same result.
I also tried setting an image on a button using btn.Image = "whatever.jpg" with the same result.
This is the result every time. I'm lost. How do I get images to display?
EDIT:
I did get this to work, but only on the emulator
<Image x:Name="StupidImage" Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" />
and same for
StupidImage.Source = ImageSource.FromUri(new Uri("https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg"));
EDIT 2 - Clarification
My goal is to allow the user to select a photo from the device and then display a preview of it.
If you want to use images in you app you can load them into your Shared Project, like
Make sure you change the Build Action to Embedded resource
Then in your code
image.Source = ImageSource.FromResource("App5.Images.useravatar.png");
Note the Resource name.
And XAML
<ContentPage.Content>
<StackLayout>
<Image x:Name="image" WidthRequest="50"/>
</StackLayout>
</ContentPage.Content>
Just a few things you can take off the list:
[x] Adding a image from Visual studio :
Right click on the correct folder
select Add >> New File ...
NB: you have to add it with visual studio and not just throw it in the folder. Visual studio needs to know about it
[x] When Adding the image is it in the correct place :
For android: it has to be in
ProjectName.Driod.Resources.drawable folder
For ios: it has to be in
ProjectName.iOS.Resources folder
[x] Naming Convention
Its always best to use .png , all lowercase , no spaces or special char on both android and ios
with ios you normally get 3 images of the same image with the following namting convention
theman.png
theman#2x.png
theman#3x.png
They are all the same image just different sizes
[x] Showing it in xaml :
<StackLayout>
<Image Source="thedog.png" HeightRequest="100" WidthRequest="100" />
</StackLayout>
In your example you used a frame , how about a stacklayout ? a frame has more requirements.
for MVVM you can change Source with the following , dont forget that twoway :)
Source="{Binding Thumbnail, Mode=TwoWay}"
NB This is VERY basic explanations
You can try implementing the CrossMedia Plugin.
Then in your button clicked code section, put the following:
Button_Clicked.Clicked += async (sender, args) =>
{
if ( !CrossMedia.Current.IsPickPhotoSupported )
{
DisplayAlert("Error message here", "More message", "OK");
return;
}
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
});
if (file == null)
return;
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
};
Once the button is clicked, the gallery/directory will be displayed. You can choose the photo you want. Once you hit OK the image will be displayed in the Image control/tag. I'm not sure if this is the solution you are looking for. Hopes it gets you on the right direction.
This may or may not help I'll add some code, one of the surprising things about Xamarin forms and Android and using a memory stream.. is that the device density multiplier is still applied even if you aren't using a resource(If I am remembering correctly) so I would imagine if you are looking at the ADB interface you will see memory issues which is why you cant display an image... I solved this previously via sampling
The way I solved it was creating a new Image subclass -ResourceImage,
public class ResourceImage :Image
{
public enum SourceTypes{
Database,
File,
Function,
}
private bool _LoadAct = false;
public bool LoadAct { get{
return _LoadAct;
}
set{ _LoadAct = value; OnPropertyChanged ("LoadAct");
}
}
public Func<Stream> Func{ get; set; }
public SourceTypes SourceType{ get; set;}
public string ResName{ get; set;}
public ResourceImage ()
{
}
public ResourceImage (string name)
{
ResName = name;
}
public ResourceImage(Func<Stream> func){
SourceType = SourceTypes.Function;
Func = func;
}
}
then in the Android Renderer : I did the following
public class ResourceImageRenderer : ImageRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Image> e)
{
base.OnElementChanged (e);
if (e.OldElement == null)
{
var el = (ResourceImage)Element;
if (el.SourceType == ResourceImage.SourceTypes.Database) {
//Ignore for now
} else if (el.SourceType == ResourceImage.SourceTypes.File) {
using (global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ()) {
options.InJustDecodeBounds = false;
options.InSampleSize = 1;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);
var gd = Context.Resources.GetIdentifier (el.ResName.Split (new char[]{ '.' }) [0], "drawable", Context.PackageName);
using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {
var bitmap = global::Android.Graphics.BitmapFactory.DecodeResource (Context.Resources, gd, options);//DecodeStream (ms, rt, options);
bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;
Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));
}
}
} else if (el.SourceType == ResourceImage.SourceTypes.Function) {
new Task (() => {
var ms = el.Func();
if(ms == null)return;
global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ();
options.InJustDecodeBounds = false;
options.InSampleSize = 2;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);
ms.Position = 0;
Device.BeginInvokeOnMainThread(()=>{
using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {
try{
var bitmap = global::Android.Graphics.BitmapFactory.DecodeStream (ms, rt, options);
bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;
Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));
}catch(Exception eee){
}
}
});
}).Start();
}
}
}
Looking back at the code(haven't touched it in years.) there are plenty of places for improvement, I had to add the sampling to solve the same issue , users were selecting images to display in a messaging app and it worked perfectly on iOS just never displayed on Android
This is how I allow a user to select an image and then display it on a page.
I call my image service Select Image method passing in a callback method
await _imageService.SelectImage(ImageSelected);
This is my SelectImage method. There is some permission checking at the start. It uses the Media Plugin to display the gallery and allow the user to select an image.
public async Task SelectImage(Action<MediaFile> imageAction)
{
var allowed = await _permissionService.CheckOrRequestStoragePermission();
if (!allowed) return;
if (!_media.IsPickPhotoSupported)
{
throw new GalleryUnavailableException("Gallery unavailable");
}
var file = await _media.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Small,
CompressionQuality = 92
});
imageAction(file);
}
It returns a MediaFile
Here is the Image Selected callback method
private void ImageSelected(MediaFile image)
{
if (image == null)
{
return;
}
ChosenImage = new IncidentImage
{
ImageBytes = image.ToByteArray()
};
}
ChosenImage is a Property in my view model
public IncidentImage ChosenImage {get; set;}
I use PropertyChanged.Fody to trigger property changed notifications but you can also use INotifyPropertyChanged.
And IncidentImage is a class I use to both store and display images
public class IncidentImage
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int IncidentDetailsId { get; set; }
public byte[] ImageBytes { get; set; }
[Ignore]
public ImageSource ImageSource
{
get
{
ImageSource retval = null;
try
{
if (ImageBytes != null)
{
retval = ImageSource.FromStream(() => new MemoryStream(ImageBytes));
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return retval;
}
}
}
And here is the XAML
<Image Source="{Binding ChosenImage.ImageSource}"
Aspect="AspectFit"/>

Binding Image stored in the Isolated Storage to Image Control in Windows Phone

Is it possible to bind the image present in the Isolates storage to image control through xaml. I found some implementations like getting the image through the property and binding that into xaml control. But this is not the implementation what I am searching for. My question is like, writing an attach property and helper method to fetch the content from Isolated storage. I found a similar implementation in LowProfileImage class, used in windows phone 7. But I think it is deprecated now. If anyone tried similar implementations please help me to achieve the same. Also if implementation have any performance drains please mention that info too.
Yes, it is possible to use images from isolated storage in the app UI. It requires loading the image from the file into the BitmapImage and then binding ImageSource of your control to that BitmapImage. I'm using the following approach:
First, there's a method to load image asynchronously:
private Task<Stream> LoadImageAsync(string filename)
{
return Task.Factory.StartNew<Stream>(() =>
{
if (filename == null)
{
throw new ArgumentException("one of parameters is null");
}
Stream stream = null;
using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoStore.FileExists(filename))
{
stream = isoStore.OpenFile(filename, System.IO.FileMode.Open, FileAccess.Read);
}
}
return stream;
});
}
Then it can be used like this:
public async Task<BitmapSource> FetchImage()
{
BitmapImage image = null;
using (var imageStream = await LoadImageAsync(doc.ImagePath))
{
if (imageStream != null)
{
image = new BitmapImage();
image.SetSource(imageStream);
}
}
return image;
}
And finally you just assign return value of FetchImage() method to some of your view model's property, to which the UI element is bound. Of course, your view model should properly implement INotifyPropertyChanged interface for this approach to work reliably.
If you want to use attached properties approach, here's how you do it:
public class IsoStoreImageSource : DependencyObject
{
public static void SetIsoStoreFileName(UIElement element, string value)
{
element.SetValue(IsoStoreFileNameProperty, value);
}
public static string GetIsoStoreFileName(UIElement element)
{
return (string)element.GetValue(IsoStoreFileNameProperty);
}
// Using a DependencyProperty as the backing store for IsoStoreFileName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsoStoreFileNameProperty =
DependencyProperty.RegisterAttached("IsoStoreFileName", typeof(string), typeof(IsoStoreImageSource), new PropertyMetadata("", Changed));
private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Image img = d as Image;
if (img != null)
{
var path = e.NewValue as string;
SynchronizationContext uiThread = SynchronizationContext.Current;
Task.Factory.StartNew(() =>
{
using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoStore.FileExists(path))
{
var stream = isoStore.OpenFile(path, System.IO.FileMode.Open, FileAccess.Read);
uiThread.Post(_ =>
{
var _img = new BitmapImage();
_img.SetSource(stream);
img.Source = _img;
}, null);
}
}
});
}
}
}
And then in XAML:
<Image local:IsoStoreImageSource.IsoStoreFileName="{Binding Path}" />
Some limitations of this approach:
It only works on Image control, though you can change this to a whichever type you want. It's just not very generic.
Performance-wise, it will use a thread from the threadpool every time image source is changed. It's the only way to do asynchronous read from isolated storage on Windows Phone 8 right now. And you definitely don't want to do this synchronously.
But it has one one important advantage:
It works! :)
I like the above approach but there is a simpler more hacky way of doing it if you are interested.
You can go into your xaml and bind the image source to an string property then put the file path into the property dynamically.
<!-- XAML CODE -->
<Image Source="{Binding imagePath}"/>
//Behind property
public String imagePath { get; set; }
load your path into the image path then bind the image source to the image path string. You might have to do an INotifyPropertyChanged but this method should work with proper binding.

Resources