When I try to use PhoneDialer.Open, the application crashes.
I am trying to pull the phone number from firebase realtimedatabase.
<Image Source="phone.png"
HeightRequest="20"
WidthRequest="20" Margin="10,0,0,0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="CallTap_Tapped"
CommandParameter="{Binding Tel}"/>
</Image.GestureRecognizers>
</Image>
and class
public void CallTap_Tapped(object sender, EventArgs e)
{
string telTaxi = new TaxiSlovenskoModel().Tel;
PhoneDialer.Open(telTaxi);
}
UPDATE
Example:
TaxiSlovenskoModel:
public class TaxiSlovenskoModel
{
public string Id { get; set; }
public string Name { get; set; }
public string Kraj { get; set; }
public string Mesto { get; set; }
public string Tel { get; set; }
public string Description { get; set; }
public string Image { get; set; }
}
TaxiListPage.xaml:
<Image Source="phone.png"
HeightRequest="20"
WidthRequest="20" Margin="10,0,0,0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="CallTap_Tapped"
CommandParameter="{Binding Tel}"/>
</Image.GestureRecognizers>
</Image>
and TaxiListPage.xaml.cs:
TaxiRepository taxiRepository = new TaxiRepository();
public TaxiListPage()
{
InitializeComponent();
}
protected override async void OnAppearing()
{
var taxies = await taxiRepository.GetAll();
TaxiListView.ItemsSource = taxies;
}
private void AddToolBarItem_Clicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new TaxiEntry());
}
private void TaxiListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null)
{
return;
}
var taxi = e.Item as TaxiSlovenskoModel;
Navigation.PushModalAsync(new TaxiDetail(taxi));
((ListView)sender).SelectedItem = null;
}
public void CallTap_Tapped(object sender, EventArgs e)
{
string telTaxi = new TaxiSlovenskoModel().Tel;
PhoneDialer.Open(telTaxi);
}
Database:
enter image description here
In APP:
enter image description here
The reason this is happening is probably that you are missing your Manifest query that you need if you Target Android 11:
<queries>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel"/>
</intent>
</queries>
More information here: https://learn.microsoft.com/en-us/xamarin/essentials/phone-dialer?tabs=android
UPDATE:
So the issue is that you are creating a new object instead of actually using your existing object. your call method should look as follows:
public void CallTap_Tapped(object sender, EventArgs e)
{
var tappEvent = (TappedEventArgs)e;
var telTaxi = tappEvent?.Parameter.ToString();
if(!string.IsNullOrWhiteSpace(telTaxi))
PhoneDialer.Open(telTaxi);
}
Good luck
Related
Just wanted to confirm whether DatePicker's DateSelected is not Binding possible like:
DateSelected="{Binding TargetDateSelectedCommand}"/>
To me it looks like handler has to be created in .cs file.
It is possible, you could have a try with Turn Events into Commands with Behaviors.
Custom a Behavior<DatePicker> class as follows:
public class DatePickerSelectedItemBehavior: Behavior<DatePicker>
{
public static readonly BindableProperty CommandProperty =
BindableProperty.Create("Command", typeof(ICommand), typeof(DatePickerSelectedItemBehavior), null);
public static readonly BindableProperty InputConverterProperty =
BindableProperty.Create("Converter", typeof(IValueConverter), typeof(DatePickerSelectedItemBehavior), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
public DatePicker AssociatedObject { get; private set; }
protected override void OnAttachedTo(DatePicker bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
bindable.BindingContextChanged += OnBindingContextChanged;
bindable.DateSelected += DatePickerItemSelected;
}
protected override void OnDetachingFrom(DatePicker bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
bindable.DateSelected -= DatePickerItemSelected;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
void DatePickerItemSelected(object sender, DateChangedEventArgs e)
{
if (Command == null)
{
return;
}
object parameter = Converter.Convert(e, typeof(object), null, null);
if (Command.CanExecute(parameter))
{
Command.Execute(parameter);
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
Then used in Xaml:
<ContentPage.Resources>
<ResourceDictionary>
<local:SelectedItemEventArgsToSelectedItemConverter x:Key="SelectedItemConverter" />
</ResourceDictionary>
</ContentPage.Resources>
...
<DatePicker >
<DatePicker.Behaviors>
<local:DatePickerSelectedItemBehavior Command="{Binding OutputAgeCommand}"
Converter="{StaticResource SelectedItemConverter}" />
</DatePicker.Behaviors>
</DatePicker>
Here is the SelectedItemEventArgsToSelectedItemConverter.cs:
public class SelectedItemEventArgsToSelectedItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var eventArgs = value as DateChangedEventArgs;
return eventArgs.NewDate;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
according to the docs DatePicker does not have any command properties. DateSelected is a DateTime, not a command
I am using Media Plugin and everything worked fine until i have decided to move my logic to ViewModel.
This is my Xaml
<Frame BackgroundColor="LightGray" HasShadow="True">
<Image
x:Name="Photo"
Grid.Row="2"
HeightRequest="100"
Source="{Binding postViewModel.SelectedPhoto}"
VerticalOptions="Start"/>
</Frame>
My Binding to MasterViewModel
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
My Master
class MasterPostsViewModel : BaseViewModel
{
public PostViewModel postViewModel { get; set; }
public CategoriesViewModel categoriesViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
categoriesViewModel = new CategoriesViewModel();
postViewModel = new PostViewModel(navigation);
}
}
Taking Picture in View Model
private MediaFile _selectedPhoto;
public MediaFile SelectedPhoto { get => _selectedPhoto; set => SetValue(ref
_selectedPhoto, value); }
private async Task TakePicture()
{
await Permission();
var imageSource = await DependencyService.Get<IMessage>().ShowActionSheet(AppResources.AlertPhoto, AppResources.AlertNewPhoto, AppResources.AlertGallery);
if (imageSource == AppResources.AlertNewPhoto)
{
var imageFileName = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()
{
Name = $"{DateTime.UtcNow}.jpg",
DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Rear,
PhotoSize = PhotoSize.Medium,
SaveToAlbum = true
});
if (imageFileName == null) return;
else
{
SelectedPhoto = imageFileName;
}
}
}
I can see tthe adress of the picture however the picture doesnt display on my xaml. I have tried to follow this
Bind Plugin.Media fromViewModel
But still didnt work. Please some suggestion on what am i doing wrong
I use you code and write a demo with binding a string, it works well. You can have a look at the code and may get some idea from it.
Code in xaml:
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding postViewModel.SelectedPhoto}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="click me" Command ="{Binding postViewModel.NewCommand}"/>
</StackLayout>
Code behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
}
class MasterPostsViewModel
{
public PostViewModel postViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
}
}
class PostViewModel : INotifyPropertyChanged
{
string _selectedPhoto;
public ICommand NewCommand { private set; get; }
public event PropertyChangedEventHandler PropertyChanged;
public PostViewModel()
{
SelectedPhoto = "default text";
NewCommand = new Command(TakePicture);
}
private void TakePicture()
{
SelectedPhoto = "test text After click button";
}
public string SelectedPhoto
{
set
{
if (_selectedPhoto != value)
{
_selectedPhoto = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedPhoto"));
}
}
}
get
{
return _selectedPhoto;
}
}
}
Sample project has been uploaded here.
I have a very basic need, but it seems quite challenging to achieve such a simple thing in Xamarin Forms, especially when I compare it with the way the React Native let us do the same thing.
Anyways, so I am trying to highlight a frame's background color based on the selected Id. For that, I have created a value converter, and passing Id to check and convert to the desired background color.
Below is my XAML Code:
<Frame CornerRadius="6" Padding="10" Margin="5" WidthRequest="110" HeightRequest="80"
BackgroundColor="{Binding TitleId, Converter={StaticResource
selectedGuidelineToColorConverter},ConverterParameter={x:Reference Guidelines}}">
<StackLayout>
<Label Style="{StaticResource MaterialIcons}" Text="" FontSize="20"/>
<Label Text="{Binding Title}" FontSize="13" HorizontalTextAlignment="Start" TextColor="#333d47"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding Path=BindingContext.SelectedGuidelineCommand, Source={x:Reference Guidelines}}" CommandParameter="{Binding}">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</Frame>
Below is my Converter Code:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var page = parameter as ContentPage;
GuidelinesViewModel model = null;
if(page != null)
{
model = page.BindingContext as GuidelinesViewModel;
}
if(model != null && model.CurrentVisibleGuideline != null && model.CurrentVisibleGuideline.TitleId == (int)value)
{
return "#808080";
}
return "#fff";
}
My Model Code:
public class Guideline
{
public int TitleId { get; set; }
public string Title { get; set; }
public Section Section { get; set; }
public List<Content> Content { get; set; }
public List<QnA> QnA { get; set; }
}
Here is my View Model Code:
Guideline guideline = null;
public Guideline CurrentVisibleGuideline
{
get { return guideline; }
set { SetProperty(ref guideline, value); }
}
public ICommand SelectedGuidelineCommand
{
get
{
return new Command<Guideline>((guideline) => ExecuteSelectedGuidelineCommand(guideline));
}
}
void ExecuteSelectedGuidelineCommand(Guideline guideline)
{
CurrentVisibleGuideline = guideline;
}
async void GetGuidelines()
{
IsBusy = true;
Guidelines = new ObservableCollection<Guideline>();
try
{
var guidelines = await DataStore.GetGuidelinesAsync(CurrentVisibleSection);
foreach (var guideline in guidelines)
{
Guidelines.Add(guideline);
}
CurrentVisibleGuideline = Guidelines[0];
TotalGuidelines = Guidelines.Count;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
"CurrentVisibleGuideline" is a property in my View Model which contains the TitleId and other details of the selected guideline.
Problem is that, the converter code is executed before the CurrentVisibleGuideline = Guidelines[0] in the view model, and therefore, I get null in CurrentVisibleGuideline in the converter.
I believe that once the data is updated in the view model upon command execution, the XAML code should re-render the view and re-run the converter, but in my case it doesn't seem to happen.
Add an IsSelected boolean to you Guideline View Model.
Update the convertor to take in a bool and return a string that will return
your Hex color value.
When you change the CurrentVisibleGuideline Set the IsSelected flag on your Guideline View Model.
ViewModel
public class Guideline
{
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsSelected"));
}
}
public int TitleId { get; set; }
public string Title { get; set; }
public Section Section { get; set; }
public List<Content> Content { get; set; }
public List<QnA> QnA { get; set; }
}
ObservableCollection<Guideline> Guidelines { get; set; }
Guideline guideline = null;
public Guideline CurrentVisibleGuideline
{
get { return guideline; }
set { SetProperty(ref guideline, value); }
}
public ICommand SelectedGuidelineCommand
{
get
{
return new Command<Guideline>((guideline) => ExecuteSelectedGuidelineCommand(guideline));
}
}
void ExecuteSelectedGuidelineCommand(Guideline guideline)
{
CurrentVisibleGuideline = guideline;
}
async void GetGuidelines()
{
IsBusy = true;
Guidelines = new ObservableCollection<Guideline>();
try
{
var guidelines = await DataStore.GetGuidelinesAsync(CurrentVisibleSection);
foreach (var guideline in guidelines)
{
Guidelines.Add(guideline);
}
CurrentVisibleGuideline = Guidelines[0];
TotalGuidelines = Guidelines.Count;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
void SetCurrentGuideline(Guideline guideline)
{
CurrentVisibleGuideline = guideline;
foreach (var gl in Guidelines)
gl.IsSelected = gl.TitleId == CurrentVisibleGuideline.TitleId;
}
Converter
public class BooleanToStringConverter : IValueConverter
{
public string TrueValue { get; set; } = "#808080"
public string FalseValue { get; set; } = "#FFF"
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
if ((bool)value)
return TrueValue;
else
return FalseValue;
}
return "";
}
}
XAML
<Frame CornerRadius="6" Padding="10" Margin="5" WidthRequest="110" HeightRequest="80"
BackgroundColor="{Binding IsSelected, Converter={StaticResource BooleanToString}}">
<StackLayout>
<Label Style="{StaticResource MaterialIcons}" Text="" FontSize="20"/>
<Label Text="{Binding Title}" FontSize="13" HorizontalTextAlignment="Start" TextColor="#333d47"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding Path=BindingContext.SelectedGuidelineCommand, Source={x:Reference Guidelines}}" CommandParameter="{Binding}">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</Frame>
On a side note you will also need to turn the guideline(s) you get back from your DataStore into a ViewModel version.
I have a UWP Page, containing a form with textboxes and a ListView control. The ListView control is bound to a collection of Products. And I want that the bound textboxes should show the information regarding the product selected in the listview.
public class Product: INotifyPropertyChanged
{
public int ProductID { get; set; }
private string name;
public string Name {
get { return name; }
set
{
if (name==value)
return;
name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public Product(int pid, string name)
{
ProductID = pid;
Name = name;
}
}
}
The XAML is as below:
<TextBox x:Name="txtProductId" Grid.Row="1" Grid.Column="1"
Text="{x:Bind CurrentProduct.ProductID}"/>
<TextBox x:Name="txtProductName" Grid.Row="2" Grid.Column="1"
Text="{x:Bind CurrentProduct.Name}" />
<ListView x:Name="lstProducts" Grid.Row="3" Grid.ColumnSpan="2"
ItemsSource="{x:Bind ProductList}"
SelectedItem="{x:Bind CurrentProduct, Mode=TwoWay}"
ItemTemplate="{StaticResource lstDataTemplate}"
>
</ListView>
The following code executes on Page_Loaded:
CurrentProduct = Products[0];
DataContext = CurrentProduct;
The ListView is bound to ProductList (Type ObservableCollection). I've noticed by executing the app in single step mode, the value of CurrentProduct is changing, but I think since it is the reference and not the property of the DataContext that changes, the PropertyChanged event is never fired and the TextBox doesn't get updated to show the name of the CurrentProduct.
I don't know how to proceed, any help would be appreciated.
The X:Bind default mode is OneTime, in your case, you need to set the mode to OneWay.
I made a code sample for your reference:
<TextBox x:Name="txtProductId" Grid.Row="1" Grid.Column="1"
Text="{x:Bind CurrentProduct.ProductID,Mode=OneWay}"/>
<TextBox x:Name="txtProductName" Grid.Row="2" Grid.Column="1"
Text="{x:Bind CurrentProduct.Name,Mode=OneWay}" />
<ListView x:Name="lstProducts" Grid.Row="3" Grid.ColumnSpan="2"
ItemsSource="{x:Bind ProductList}"
SelectedItem="{x:Bind CurrentProduct, Mode=TwoWay}"
>
</ListView>
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
ProductList.Add(new Product(i, "name " + i));
}
}
public ObservableCollection<Product> ProductList { get; set; } = new ObservableCollection<Product>();
private Product _CurrentProduct = new Product(100,"test");
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropertyName));
}
}
public Product CurrentProduct
{
get { return _CurrentProduct; }
set
{
if (_CurrentProduct != value)
{
_CurrentProduct = value;
RaisePropertyChanged("CurrentProduct");
}
}
}
}
public class Product : INotifyPropertyChanged
{
public int ProductID { get; set; }
private string name;
public string Name
{
get { return name; }
set
{
if (name == value)
return;
name = value;
RaisePropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public Product(int pid, string name)
{
ProductID = pid;
Name = name;
}
public override string ToString()
{
return Name;
}
}
I have a problem with prism.forms and propertychanged.
I have settingsmodel,settingsviewmodel and settingspage that shown code below,
SettingsModel
public class SettingsModel: BindableBase
{
public string Url { get; set; }
public bool IsEnabled{get;set;}
public string ApiUrl { get; set; }
public SettingsModel()
{
this.Url = string.Empty;
this.IsEnabled = false;
this.ApiUrl = string.Empty;
}
}
SettingsViewModel
[ImplementPropertyChanged]
public class SettingsPageViewModel : ViewModelBase
{
readonly INavigationService _navigationService;
readonly IUserDialogs _userDialogs;
readonly IProductService _productService;
#region Constructor
public SettingsPageViewModel(INavigationService navigationService,
IUserDialogs userDialogs, IProductService productService)
{
_navigationService = navigationService;
_userDialogs = userDialogs;
_productService = productService;
this.Settings = new SettingsModel();
SaveCommand = new DelegateCommand(Save).ObservesCanExecute((vm) => Settings.IsEnabled);
}
#endregion
#region Model
SettingsModel _settingsModel;
public SettingsModel Settings
{
get { return _settingsModel; }
set {
if (_settingsModel != null)
_settingsModel.PropertyChanged -= MyPersonOnPropertyChanged;
SetProperty(ref _settingsModel, value);
if (_settingsModel != null)
_settingsModel.PropertyChanged += MyPersonOnPropertyChanged;
Validation();
}
}
public bool IsLoading { get; set; }
public DelegateCommand SaveCommand { get; set; }
#endregion
void MyPersonOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
Validation();
}
#region Methods
async void Save()
{
var result = await _productService.GetProducts(Priority.UserInitiated,"","","");
}
void Validation()
{
Settings.IsEnabled = !string.IsNullOrEmpty(Settings.ApiUrl) ? true : false;
}
#endregion
}
And SettingsPage XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XXX.Warehouse.SettingsPage">
<Entry Text="{Binding Settings.ApiUrl}" Margin="0,5,0,5"
Placeholder="www.example.com" HorizontalOptions="FillAndExpand" />
<Button Text="Save"
FontSize="16"
BorderRadius="5"
TextColor="White"
BackgroundColor ="#578A17"
IsEnabled="{Binding Settings.IsEnabled}"
Command="{Binding SaveCommand}" />
I want to do when user enter url than IsEnabled property will true, when Url is empty than IsEnabled property will false and save button if IsEnabled is false, button not enabled.
My main problem is, i write Entry url but propertychanged event not fired?
How can i solve this?
Thank you.