I basically want to create a hyperlink in Xamarin.Forms using the label class. Basically, I want to following link to take the user to google.com in a web browser:
<Label Text="http://www.google.com/" />
I can't find anything in the Xamarin Forms API about this and the internet has vague and limited information on this topic in Xamarin.Forms.
Is this possible? If so, could someone please point me in the right direction? Thanks in advance to anyone who answers.
You can't really do this because Labels by default don't respond to user input, but you can achieve something similar with gestures
using Xamarin.Forms;
using Xamarin.Essentials;
Label label = new Label();
label.Text = "http://www.google.com/";
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += async (s, e) => {
// Depreciated - Device.OpenUri( new Uri((Label)s).Text);
await Launcher.OpenAsync(new Uri(((Label)s).Text));
};
label.GestureRecognizers.Add(tapGestureRecognizer);
I made this little class to handle it:
public class SimpleLinkLabel : Label
{
public SimpleLinkLabel(Uri uri, string labelText = null)
{
Text = labelText ?? uri.ToString();
TextColor = Color.Blue;
GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(() => Device.OpenUri(uri)) });
}
}
And a bit more involved if you want to underline it too:
public class LinkLabel : StackLayout
{
private SimpleLinkLabel label;
public LinkLabel(Uri uri, string labelText = null, bool underlined = true)
{
// Remove bottom padding
Padding = new Thickness(Padding.Left, Padding.Top, Padding.Right, 0);
VerticalOptions = LayoutOptions.Center;
Children.Add(label = new SimpleLinkLabel(uri, labelText));
if (underlined)
Children.Add(new BoxView { BackgroundColor = Color.Blue, HeightRequest = 1, Margin = new Thickness(0, -8, 0, 0) });
}
public TextAlignment HorizontalTextAlignment { get { return label.HorizontalTextAlignment; } set { label.HorizontalTextAlignment = value; } }
}
The latter class inspired by this post: how to underline in xamarin forms
Edit: XLabs have a HyperLinkLabel too.
<Label LineBreakMode="WordWrap">
<Label.FormattedText>
<FormattedString>
<Span Text="Google">
<Span.GestureRecognizers>
<TapGestureRecognizer Tapped="Handle_Tapped" />
</Span.GestureRecognizers>
</Span>
</FormattedString>
</Label.FormattedText>
</Label>
public async void Handle_Tapped(object sender, EventArgs e)
{
await Browser.OpenAsync(new Uri(url), BrowserLaunchMode.SystemPreferred);
}
use a button and a xamarin.forms.theme nuget
<Button StyleClass = "Link"/>
https://developer.xamarin.com/guides/xamarin-forms/user-interface/themes/light/
If you want to do this with MVVM, you can create a Label with a blue text colour and a GestureRecognizers to hook it up to a Command:
<Label TextColor="Blue" Text="{Binding Model.LinkDescription}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ClickCommand}" CommandParameter="{Binding Model.LinkURL}"/>
</Label.GestureRecognizers>
</Label>
In your ViewModel you can launch the default Browser using the Xamarin Essentials Nuget Package:
private Boolean IsValidUri(String uri)
{
try
{
new Uri(uri);
return true;
}
catch
{
return false;
}
}
public ICommand ClickCommand => new Command<string>(async (url) =>
{
try
{
if (!string.IsNullOrEmpty(url))
{
if (!url.Trim().StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
url = "http://" + url;
}
if (IsValidUri(url))
{
await Browser.OpenAsync(new Uri(url), BrowserLaunchMode.SystemPreferred);
}
}
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
Related
I am looking for a clean way to create a (custom) overlay with a flashlight button for the barcode scanner using the Zxing Net Mobile NuGet package.
I already looked at some samples, but unfortunately these samples use clean Xamarin and no MvvmCross.
The code below is placed inside a custom 'ScannerService', which gets called within a MvxViewModel (ScanViewModel).
What I've got so far:
IScannerService.cs
public class ScannerService : IScannerService
{
public void Scan(Action<Result> onComplete)
{
MobileBarcodeScanner.Initialize(Xamarin.Essentials.Platform.CurrentActivity.Application);
MobileBarcodeScanner scanner = new MobileBarcodeScanner
{
TopText = "Houd uw telefoon voor de barcode.",
FlashButtonText = "Flitser",
CancelButtonText = "Stoppen",
BottomText = "Het scannen gebeurt automatisch."
};
var overlay = new ZxingOverlayView(Xamarin.Essentials.Platform.CurrentActivity.Application);
var button = new Button();
button.Clicked += (sender, e) => scanner.ToggleTorch();
overlay.AddChildrenForAccessibility(button);
scanner.UseCustomOverlay = true;
scanner.CustomOverlay = overlay;
bool result = false;
scanner.ScanContinuously((scanResult) =>
{
if (!result)
{
result = true;
scanner.Cancel();
Device.BeginInvokeOnMainThread(() => onComplete(scanResult));
}
});
}
}
The problem is that I created a Xamarin.Forms Button and that this button can't be added to the custom overlay of the 'ZxingOverlayView'.
ScanViewModel.cs
private async Task Scan()
{
if (await _mediaService.CheckPermissions().ConfigureAwait(false))
{
_scanner.Scan((result) => MvxNotifyTask.Create(() => MainThread.InvokeOnMainThreadAsync(() => HandleScan(result)), OnException));
}
}
Is there an alternative way to add the flashlight button to the custom overlay?
Edit:
As Leo suggested I used the default example of ZXing.Net.Mobile. I added the 'ZXingDefaultOverlay' to my page, but unfortunately everytime I use a ZXing component inside my page it throws the following exception:
Java.Lang.NullPointerException: 'Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference'
Here is my code so far:
ScanningViewModel.cs (BaseViewModel inherits from 'MvxViewModel')
public class ScanningViewModel : BaseViewModel
{
private readonly IMvxNavigationService _navigationService;
private readonly IUserDialogs _userDialogs;
ZXingScannerView ZXingScannerView;
ZXingDefaultOverlay ZXingDefaultOverlay;
private Grid _grid;
public Grid Grid
{
get => _grid;
set => SetProperty(ref _grid, value);
}
public ScanningViewModel(IMvxNavigationService navigationService, IUserDialogs userDialogs)
{
_navigationService = navigationService;
_userDialogs = userDialogs;
ZXingScannerView = new ZXingScannerView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
AutomationId = "zxingScannerView"
};
ZXingScannerView.OnScanResult += (result) =>
Device.BeginInvokeOnMainThread(async () =>
{
// Stop analysis until we navigate away so we don't keep reading barcodes
ZXingScannerView.IsAnalyzing = false;
// Show an alert
await _userDialogs.AlertAsync("Scanned Barcode", result.Text, "OK");
});
ZXingDefaultOverlay = new ZXingDefaultOverlay
{
TopText = "",
BottomText = "",
ShowFlashButton = true,
AutomationId = "zxingDefaultOverlay"
};
ZXingDefaultOverlay.FlashButtonClicked += (sender, e) =>
{
ZXingScannerView.IsTorchOn = !ZXingScannerView.IsTorchOn;
};
var grid = new Grid
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
var stopButton = new Button
{
WidthRequest = 100,
HeightRequest = 50,
HorizontalOptions = LayoutOptions.Start,
VerticalOptions = LayoutOptions.End,
Text = "Disable",
Command = new Command(() => ZXingScannerView.IsScanning = false)
};
var cancelButton = new Button
{
WidthRequest = 100,
HeightRequest = 50,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.End,
Text = "Cancel",
Command = new Command(async () => await _navigationService.Close(this))
};
var startButton = new Button
{
WidthRequest = 100,
HeightRequest = 50,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.End,
Text = "Enable",
Command = new Command(() => ZXingScannerView.IsScanning = true)
};
grid.Children.Add(ZXingScannerView);
grid.Children.Add(ZXingDefaultOverlay);
grid.Children.Add(startButton);
grid.Children.Add(cancelButton);
grid.Children.Add(stopButton);
// The root page of your application
Grid = grid;
}
//protected override void OnAppearing()
//{
// base.OnAppearing();
// ZXingScannerView.IsScanning = true;
//}
//protected override void OnDisappearing()
//{
// ZXingScannerView.IsScanning = false;
// base.OnDisappearing();
//}
}
ScanningPage.xaml (This is a 'MvxContentPage')
<ContentPage.Content>
<StackLayout>
<ContentView Content="{Binding Grid}"></ContentView>
</StackLayout>
</ContentPage.Content>
ScanningPage.xaml.cs
[MvxContentPagePresentation(WrapInNavigationPage = true)]
public partial class ScanningPage : MvxContentPage<ScanningViewModel>
{
public ScanningPage()
{
InitializeComponent();
}
}
Any idea why it throws this specific error?
I use version 2.4.1 of the 'ZXing.Net.Mobile' and 'ZXing.Net.Mobile.Forms' NuGet package.
I use SearchBar to search from listView and I have other controls
but when page load just SearchBar appear and other controls disappear
like this image
and when search then select from listView the disappeared controls appears
like this image
my xaml code :
<StackLayout>
<SearchBar x:Name="search_trade"
TextChanged="search_trade_TextChanged"
/>
<ListView x:Name="list" ItemSelected="list_ItemSelected" >
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" ></TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Editor x:Name="edit_trade"
FontSize="14"/>
</StackLayout>
my c# code :
private void search_trade_TextChanged(object sender, TextChangedEventArgs e)
{
if (search_trade.Text.Trim()==string.Empty)
{
list.IsVisible = false;
}
list.IsVisible = true;
if (string.IsNullOrEmpty(e.NewTextValue))
{
list.ItemsSource = tempdata;
}
else
{
list.ItemsSource = tempdata.Where(x => x.Name.ToLower().StartsWith(e.NewTextValue));
}
}
private void list_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = (Contacts)e.SelectedItem;
edit_trade.Text = item.Name.ToString();
search_trade.Text = "";
list.IsVisible = false;
edit_trade.IsReadOnly = true;
}
code c# that I initially set the ItemsSource :
public Anti_biotic()
{
InitializeComponent();
active();
years_months();
frame_view.IsVisible = false;
trade();
}
private void trade()
{
tempdata = new List<Contacts>
{
new Contacts() { Name = "Amoclawin228.5mg/5mlsusp60ml"},
new Contacts() { Name = "Amoclawin 457mg / 5ml susp 60 ml"},
new Contacts() { Name = "Amoflux 250 mg susp 100 ml"},
};
}
how can I solve this issue?
you are not assigning the ListView's ItemsSource when the page loads. If the ListView doesn't have any data then it won't display anything
public Anti_biotic()
{
InitializeComponent();
active();
years_months();
frame_view.IsVisible = false;
// this method creates tempdata but doesn't do anything with it
trade();
// add this
list.ItemsSource = tempdata;
}
I have installed Xamarin.Forms.Googlemaps 2.3.0.But I can see blank control in Android with text "Xamarin.Forms.GoogleMaps" and in iOS, nothing.
Where am i going wrong ?
In Androidmanifest, added:-
<meta-data android:name="com.google.android.geo.API_KEY" android:value="API_KEY"/>
In xaml,namespace is:-
xmlns:gmap="clr-namespace:Xamarin.Forms.GoogleMaps;assembly=Xamarin.Forms.GoogleMaps"
Control is:-
<gmap:Map x:Name="map"
Grid.Row="1"
HasZoomEnabled="True"
HeightRequest="{x:Static local:AppConstants.ActivityLogGfxContainerSize}"
InitialCameraUpdate="-23.68, -46.87, 13, 30, 60"
IsShowingUser="True"
MapLongClicked="map_MapLongClicked"
VerticalOptions="FillAndExpand"
WidthRequest="{x:Static local:AppConstants.PadThird}" />
in xaml.cs:-
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
this.locationViewModel = viewModel as LocationControlViewModel;
Check.IsNotNull(locationViewModel);
map.MyLocationEnabled = true;
map.MoveToRegion(Xamarin.Forms.GoogleMaps.MapSpan.FromCenterAndRadius(new Xamarin.Forms.GoogleMaps.Position(this.locationViewModel.Latitude, this.locationViewModel.Longitude), Xamarin.Forms.GoogleMaps.Distance.FromMiles(1)));
}
private async void map_MapLongClicked(object sender, Xamarin.Forms.GoogleMaps.MapLongClickedEventArgs e)
{
this.locationViewModel.Latitude = e.Point.Latitude;
this.locationViewModel.Longitude = e.Point.Longitude;
Xamarin.Forms.GoogleMaps.Pin pin = new Xamarin.Forms.GoogleMaps.Pin
{
Type = Xamarin.Forms.GoogleMaps.PinType.Place,
Position = new Xamarin.Forms.GoogleMaps.Position(this.locationViewModel.Latitude, this.locationViewModel.Longitude),
Label = "test"
};
map.Pins.Clear();
map.Pins.Add(pin);
map.MoveToRegion(Xamarin.Forms.GoogleMaps.MapSpan.FromCenterAndRadius(new Xamarin.Forms.GoogleMaps.Position(this.locationViewModel.Latitude, this.locationViewModel.Longitude), Xamarin.Forms.GoogleMaps.Distance.FromMiles(1)));
var stream = await map.TakeSnapshot();
}
I would like to take one photo at Xamarin.Forms. But when I build it, when I click on the "Take Photo" button I get the above error.
I put a breakpoint on all lines, but I could not find my fault.
Click Take Photo
ResimYukle.axml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using hackathon.CustomControls;
using hackathon.Views;
using Plugin.Media;
namespace hackathon.TabbedPages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ResimYukle : ContentPage
{
private Image img;
public ResimYukle()
{
InitializeComponent();
RelativeLayout layout = new RelativeLayout();
CustomButton btnTakePhoto = new CustomButton
{
Text = "Take Photo"
};
btnTakePhoto.Clicked += BtnTakePhoto_Clicked;
CustomButton btnPickPhoto = new CustomButton
{
Text = "Pick Photo"
};
btnPickPhoto.Clicked += BtnPickPhoto_Clicked;
CustomButton btnTakeVideo = new CustomButton
{
Text = "Take Video"
};
btnTakeVideo.Clicked += BtnTakeVideo_Clicked;
CustomButton btnPickVideo = new CustomButton
{
Text = "Pick Vİdeo"
};
btnPickVideo.Clicked += BtnPickVideo_Clicked;
StackLayout stkImage = new StackLayout
{
BackgroundColor = Color.White
};
img = new Image
{
Source = "defaultimg.png"
};
stkImage.Children.Add(img);
layout.Children.Add(stkImage, Constraint.Constant(0),
Constraint.Constant(0), Constraint.RelativeToParent(
(parent) =>
{
return parent.Width;
}));
StackLayout stkPictureButtons = new StackLayout
{
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Padding = 20,
Children =
{
btnTakePhoto,
btnPickPhoto
}
};
StackLayout stkVideoButtons = new StackLayout
{
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Padding = 20,
Children =
{
btnTakeVideo,
btnPickVideo,
}
};
layout.Children.Add(stkPictureButtons, Constraint.Constant(0),
Constraint.Constant(0), Constraint.RelativeToParent((parent) =>
{
return parent.Width;
}));
layout.Children.Add(stkVideoButtons, Constraint.Constant(0),
Constraint.RelativeToView(stkPictureButtons,
(parent, sibling) =>
{
return sibling.Height + 10;
}), Constraint.RelativeToParent((parent) =>
{
return parent.Width;
}));
Content = layout;
}
private async void BtnPickVideo_Clicked(object sender, EventArgs e)
{
if (!CrossMedia.Current.IsPickVideoSupported)
{
DisplayAlert("UYARI", "Galeriye erişme yetkiniz yok!", "OK");
return;
}
var file = await CrossMedia.Current.PickVideoAsync();
if (file == null)
return;
DisplayAlert("UYARI", "Seçilen video: " + file.Path, "OK");
file.Dispose();
}
private async void BtnTakeVideo_Clicked(object sender, EventArgs e)
{
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakeVideoSupported)
{
DisplayAlert("UYARI", "Cihazınızın kamerası aktif değil!", "OK");
return;
}
var file = await CrossMedia.Current.TakeVideoAsync(
new Plugin.Media.Abstractions.StoreVideoOptions
{
Name = DateTime.Now + ".mp4",
Directory = "MediaPluginPhotoVideo",
Quality = Plugin.Media.Abstractions.VideoQuality.High,
DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Front
});
if (file == null)
return;
DisplayAlert("UYARI",
"Video başarılı bir şekilde kayıt edildi: " + file.Path, "OK");
file.Dispose();
}
private async void BtnPickPhoto_Clicked(object sender, System.EventArgs e)
{
if (!CrossMedia.Current.IsPickPhotoSupported)
{
DisplayAlert("UYARI", "Galeriye erişme yetkiniz yok!", "OK");
return;
}
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
return;
img.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
}
private async void BtnTakePhoto_Clicked(object sender, System.EventArgs e)
{
int a;
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
a = 0;
int b;
DisplayAlert("UYARI", "Cihazınızın kamerası aktif değil!", "OK");
b = 0;
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(
new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "MediaPluginPhoto",
Name = DateTime.Now + ".jpg",
DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Front
});
if (file == null)
return;
img.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
}
}
}
ResimYukle.axml
<?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="hackathon.TabbedPages.ResimYukle"
Title="Yükle">
</ContentPage>
I do this by looking at my example from here :
https://github.com/ozaksuty/Xamarin-Ogreniyorum/tree/master/MediaPlugin
For future reference I will take the answer of apineda in the comments and elaborate a bit.
The error here is that you have installed the NuGet package only on your shared PCL project. What you need to do is install it on your PCL project as well as your platform projects.
This is because of the way these plugins and Xamarin.Forms work. What actually happens with plugins like these is it offers you an abstract method to work with. Xamarin.Forms is targeting multi-platform, but at the end of the day, it will just transform into a native app. Because of that, it needs an implementation on the actual platform. For this example, code for showing the camera differs greatly between Android and iOS (and all other platforms for that matter).
So, effectively, you are installing the plugin on your shared library to get the method you call upon, but it is not implemented. By then installing the same plugin (but it takes another binary) to your platform project(s), the method will get it's implementation.
It is kind of hard to determine whether a plugin needs to be installed on all projects, or just the shared. Try to decide for yourself if it uses any platform specific stuff.
I have a situation with my app whereby the ProgressBar displays correctly when OnNavigatedTo is executed. Then during the process of searching for and collating the data resuested, the ProgressBar hangs/freezes. Finally, when all data has been collected and displayed correctly in the list, the ProgressBar collapses correctly.
My XAML looks like this:
<ProgressBar
x:Name="progressbar"
IsIndeterminate="True"
Minimum="0"
Maximum="100"
Value="0"
Width="400"
Height="30"
Foreground="#FF117B0F"
/>
My C# looks like this:
List<GameLive> gameLive;
public MainPage()
{
InitializeComponent();
gameLive = new List<GameLive>();
ProgressBar progressbar = new ProgressBar();
}
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string htmlPageLive = "";
bool isError = false;
HttpClient client = new HttpClient();
try
{
htmlPageLive = await client.GetStringAsync("webpage");
}
catch (Exception)
{
isError = true;
}
if (isError)
{
MessageBox.Show("Unable to retrieve data");
return;
}
HtmlDocument htmlDocumentLive = new HtmlDocument();
htmlDocumentLive.LoadHtml(htmlPageLive);
foreach (var div in htmlDocumentLive.DocumentNode.SelectNodes("..nav.."))
{
bool isErrorTwo = false;
GameLive newGame = new GameLive();
try
{
newGame.Title = removed;
newGame.Summary = removed;
gameLive.Add(newGame);
}
catch (Exception)
{
isErrorTwo = true;
}
if (isErrorTwo)
{
NavigationService.Navigate(new Uri("/Temp.xaml", UriKind.Relative));
return;
}
}
lstGameLive.ItemsSource = gameLive;
progressbar.Visibility = Visibility.Collapsed;
progressbar.IsIndeterminate = false;
}
I've tried multiple solutions but have run out of options. Can somebody please point me in the right direction here?
Thank you.
It's a problem related to the async methods as they freeze the UI (as I think) till the execution finishes.
You might want to look at this and also this
For a much more cleaner code, try binding the progress bar visibility like this one and this