The Command enables the Button in Xamarin Prism - xamarin

I am working with Xamarin Prism, I'm trying to disable the button, but whenever I add the command it enables. What can this be ?
Code:
<Button
Command="{Binding save}"
IsEnabled="False"/>

From the document of Button.Command Property we can see:
This property is used to associate a command with an instance of a
button. This property is most often set in the MVVM pattern to bind
callbacks back into the ViewModel. IsEnabled is controlled by the
Command if set.
So, if you don't set the command property, it will work correctly.
If you set the command property, the isEnable is controlled by the Command itself.
Here I wrote an example to show you how to control the button's enable property in the command:
In the xaml:
<ContentPage.BindingContext>
<local:CommandDemoViewModel />
</ContentPage.BindingContext>
<StackLayout>
<Button Text="Divide by 2"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Command="{Binding DivideBy2Command}"
/>
</StackLayout>
And the ViewModel:
class CommandDemoViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Command DivideBy2Command { private set; get; }
public CommandDemoViewModel()
{
DivideBy2Command = new Command(()=> performCommandAction(), ()=>isButtonEnable());
}
private bool isButtonEnable()
{
//Handle the if the button isEnable here
//Reture ture or false with your own logic here to control the button's isEnabled
return false;
}
private void performCommandAction()
{
//Handle the button click logic here
}
}
Use the constructor public Command(Action execute, Func<bool> canExecute);.
You can control the isEnable by the command's CanExecute. Reture ture or false with your own logic in the method isButtonEnable to control the button's isEnabled.

Related

Xamarin Forms ListView cancel ItemSelected event

With the Xamarin Forms ListView, the ItemSelected event is fired each time an element is selected in the list.
Is there a way to cancel out of this event so that the new item isn't selected and the old item remains selected?
The use case is a master/detail type of view where selecting an item in the list changes the detail. But the detail view from the previous selection might have been altered and the user needs to decide to discard or save the previous changes before changing the List's SelectedItem.
#SushiHangover's suggestion to control the SelectionMode property and disable/enable the selection of the ListView is a good one. However, I have an alternate solution that will revert the ListView's selected item to the previous item for anyone who might have a similar need.
I will only post snippets of the solution, but they should be complete enough for someone else to learn and implement.
First, I am using FreshMVVM which provides (amongst many things), essentially, syntactic sugar over binding the View to the ViewModel. Also, the PropertyChanged nuget package creates the INotifyPropertyChanged boilerplate code at compile time. That is why you don't see the familiar XF patterns you normally see with that interface. AddINotifyPropertyChanged handles all that.
The solution to my problem is a dedicated, generic ListViewModel that can be bound to any ListView that needs the ability "roll back" a selection changed event. It binds to the Items collection. Additionally the SelectedItem property is bound to the control as well.
The constructor takes a Func which is called to determine if it's ok to move the selection or not.
[AddINotifyPropertyChangedInterface]
public class ListViewModel<T>
{
private Func<bool> _beforeChangeValidator;
private Action _afterChange;
public ListViewModel(Func<bool> beforeChangeValidator, Action afterChange)
{
_beforeChangeValidator = beforeChangeValidator;
_afterChange = afterChange;
_changing = false;
}
public int SelectedIndex { get; set; }
public T SelectedItem { get; set; }
public ObservableCollection<T> Items { get; set; }
private bool _changing;
public Command SelectedItemChanged
{
get
{
return new Command((args) =>
{
if (!_changing)
{
if (_beforeChangeValidator())
{
SelectedIndex = ((SelectedItemChangedEventArgs)args).SelectedItemIndex;
}
}
_changing = false;
});
}
}
public void RevertSelectedItemChanged()
{
_changing = true;
SelectedItem = Items[SelectedIndex];
}
}
And the code in the parent ViewModel has the Func (TagListBeforeChange) that determines if it's ok to move the selection or not. In this case I am checking if the last selected item has been changed, and if it has, prompt the user for what to do.
public override void Init()
{
TagListViewModel = new ListViewModel<Tag>(TagListBeforeChange, null);
}
private bool TagListBeforeChange()
{
if (ActiveTag.HasChanged)
{
var confirmConfig = new ConfirmConfig()
{
Message = "Current tag has changed. Discard changes and continue?",
OkText = "Discard Changes",
CancelText = "Cancel",
OnAction = (result) =>
{
if (result)
{
_mapper.Map(TagListViewModel.SelectedItem, ActiveTag);
}
else
{
TagListViewModel.RevertSelectedItemChanged();
}
}
};
_userDialogs.Confirm(confirmConfig);
return false;
}
_mapper.Map(TagListViewModel.SelectedItem, ActiveTag);
return true;
}
And finally, here is the ListView control declaration...
<ListView ItemsSource="{Binding TagListViewModel.Items}"
SelectedItem="{Binding TagListViewModel.SelectedItem, Mode=TwoWay}">
<ListView.Behaviors>
<behaviors:EventHandlerBehavior EventName="ItemSelected">
<behaviors:InvokeCommandAction Command="{Binding TagListViewModel.SelectedItemChanged}" />
</behaviors:EventHandlerBehavior>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView Padding="8">
<Label Text="{Binding DisplayValue}" />
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Complte event for Editor behaving like Unfocussed event

I am trying for saving some value in the editor after clicking on done on the keyboard for that I used the Completed event for the editor but this is calling while tapping on everywhere in the view like the Unfocused event. How to avoid that this?
<controls:CustomEditor Keyboard="Default"
ReturnKeyType="Next"
TextChanged="Comment_Changed"
Completed="OnDoneClicked"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
Text="{Binding QuestionComment}">
<controls:CustomEditor.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>
<OnPlatform x:TypeArguments="x:Double"
iOS="12"
Android="12"
WinPhone="30" />
</OnIdiom.Phone>
<OnIdiom.Tablet>
<OnPlatform x:TypeArguments="x:Double"
iOS="13"
Android="13"
WinPhone="40" />
</OnIdiom.Tablet>
</OnIdiom>
</controls:CustomEditor.FontSize>
</controls:CustomEditor>
CustomEditorClass is as following:
public const string ReturnKeyPropertyName = "ReturnKeyType";
public CustomEditor() { }
public static readonly BindableProperty ReturnKeyTypeProperty = BindableProperty.Create(
propertyName: ReturnKeyPropertyName,
returnType: typeof(ReturnKeyTypes),
declaringType: typeof(CustomEditor),
defaultValue: ReturnKeyTypes.Done );
public ReturnKeyTypes ReturnKeyType
{
get { return (ReturnKeyTypes)GetValue(ReturnKeyTypeProperty); }
set { SetValue(ReturnKeyTypeProperty, value); }
}
public enum ReturnKeyTypes : int
{
Default,
Go,
Google,
Join,
Next,
Route,
Search,
Send,
Yahoo,
Done,
EmergencyCall,
Continue
}
And the event is as following:
private async void OnDoneClicked(object sender, EventArgs e)
{
//some logic
}
Please see the documentation, this is the intended behavior
iOS (Unfocusing the editor or pressing "Done" triggers the event). Android / Windows Phone (Unfocusing the Editor triggers the event)
I've checked some messenger apps (iOS) and they don't have a done button on the keyboard at all, they provide a send button besides the entry control.
Depending on what you try to achieve, I'd ditch the done button on the keyboard altogether and rely on some sort of view on the UI, that is used for that purpose. This is platform dependent behavior anyway, Android does not support it at all, AFAIK. In order to get rid of the done button, you'll have to implement a custom renderer and set the InputAccessoryView of the native control to null (see here)
[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace ProjectName.iOS
{
public class CustomEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
this.Control.InputAccessoryView = null;
}
}
}
If you really want a button to send, you could create a derivative of Editor with a custom renderer, that creates a InputAccessoryView and raises an SendPressed event on your custom Editor when the InputAccessoryView is pressed. But remember, this is not possible for Android.

Telerik UWP DataForm changing bound data and Updating

I'm currently using telerik in UWP to create list of items, i want to be able to use a browse button and update a certain piece of data in the Telerik-RadDataForm. I have all the bindings setup using MVVM and it displays data fine if it isn't edited on the code side. My XAML is setup as so:
<Data:RadDataForm x:Name="dataform"
HorizontalAlignment="Left" Grid.Row="0"
Grid.RowSpan="2" Grid.Column="2"
VerticalAlignment="Center" Width="454"
Item="{Binding CurrentSceneViewModel, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" CommitMode="Immediate"
ValidationMode="Immediate" Height="664" Margin="0,28" />
The CurrentSceneViewModel is:
public SceneViewModel CurrentSceneViewModel
{
get => _currentSceneViewModel;
set=> _currentSceneViewModel= value;
}
And the data i wish to change is :
public string FileName
{
get => _fileName;
set
{
Scene.SceneFile = value;
_fileName = Path.GetFileName(value);
OnPropertyChanged(nameof(FileName));
}
}
The problem i have is pushing this information to the user interface the code-behind doesn't seem to update the UI, even using PropertyChanged. I'm not sure what else to try ? And if this is something the RadDataform simply doesn't support. It should be noted FileName is a property of CurrentScene ViewModel.
public abstract class BaseViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected IPageNavigationService navservice = new PageNavigationService();
}

ToolbarItem isEnabled property is available in xaml, not code

I have a form where a user can enter some data and save it to a database. I have been trying to add basic validation, making a user have to enter a value in every field. I want my save button to be unavailable until the user has entered some information in every form.
Here is my button:
<ToolbarItem Name="MenuItem1" Order="Primary" Text="Save" Priority="1" Command="{Binding SaveDataCommand}" IsEnabled="{Binding CanSaveData}"/>
I can access the isEnabled property in the XAML but where this gets infuriating is that I can't then reupdate this property. The button gets stuck in whatever state I tell it on load. I have checked my view model and it is returning a boolean which is correct (printing it to the console), it's just there is no way of updating the button state.
I even tried adding a new button that would force update the enabled state however this wouldn't work
<ToolbarItem x:Name="whyxamarinwhy" Name="MenuItem1" Order="Primary" Text="Save" Priority="1" Command="{Binding SaveEvent}" IsEnabled="{Binding CanCreateEvent}"/>
whyxamarinwhy.isenabled = true; //I cannot access this property because it doesn't exist.
Is the only way to implement this functionality going to be using a custom renderer?
MenuItem.IsEnabledProperty / For internal use by the Xamarin.Forms platform.
You should use the Command's CanExecute of the ToolbarItem to determine if the Execute method can be triggered.
Since you are already binding the command to SaveDataCommand, you can toggle the return of the CanExecute of that command to determine if the menu item button should be allowed to trigger the attached command.
You did not post your ViewModel, but in a inner-class ICommand implementation, something as simple as this works:
static bool SaveDataCommandCanExecute = true;
class SaveDataCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
// your code needs to toggle SaveDataCommandCanExecute to determine if the Execute method can be triggered
return SaveDataCommandCanExecute;
}
public void Execute(object parameter)
{
// Do something
}
}

how to use radio buttons in xamarin forms

Creating a Registration page, I need to get the following data from user.
First Name
Last Name
Username
Email
Password
Date of Birth
Gender
User Role
For the last two parameters, I am unable to find how to use radio buttons in Xamarin.Forms. Following is my code for the Registration Page.
<StackLayout BackgroundColor="#30af91" Padding="60">
<Entry Text="{Binding FirstName}" Placeholder="First Name"/>
<Entry Text="{Binding LastName}" Placeholder="Last Name"/>
<Entry Text="{Binding UserName}" Placeholder="Last Name"/>
<Entry Text="{Binding Email}" Placeholder="Email" />
<Entry Text="{Binding Password}" Placeholder="Password" IsPassword="True"/>
<Entry Text="{Binding ConfirmPassword}" Placeholder="Confirm Password" IsPassword="True"/>
<DatePicker MinimumDate="1/1/1948" MaximumDate="12/31/2007"/>
<!--Radio buttons for Gender
1. Male 2.Female-->
<!--Radio Buttons for UserRole
1. Admin 2.Participant-->
<Button Command="{Binding RegisterCommand}" Text="Register"/>
<Label Text="{Binding Message}" />
</StackLayout>
Xamarin forms does not provide Radio Button.
You can either use
1)Switch
2)Picker
or any other component to fulfill your requirement
UPDATE
The xamarin forms update version 4.6 has introduced the Radio button control, Here is the official documentation
I think there is a simpler solution that is fairly easy and requires no libraries. Really a a radio group is just a fancy ListView. You would just need to create a viewModel for each radio button that has a IsSelected flag and switch between 2 images. I had a need to allow a user to select how long a token persisted:
XAML
<ListView
HasUnevenRows="True"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Durations}"
ItemSelected="ListView_ItemSelected"
SelectedItem="{Binding SelectedDuration}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Orientation="Horizontal">
<Image
HeightRequest="18"
IsVisible="{Binding IsSelected}"
Source="radioButtonChecked.png"
WidthRequest="18"/>
<Image
HeightRequest="18"
IsVisible="{Binding IsUnselected}"
Source="radioButtonUnchecked.png"
WidthRequest="18"/>
<Label
Margin="8,0,0,0"
Text="{Binding Caption}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
We create a listview in our content page and listen for the ItemSelected event. Each list item is a horizontal stack panel where we flip between two images depending on the selected state
Code Behind
public partial class LoginPage : ContentPage
{
LoginPageViewModel LoginPageViewModel { get; }
public LoginTwoFactorFrequencyPage ()
{
BindingContext = LoginPageViewModel = new LoginPageViewModel();
InitializeComponent ();
}
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
LoginPageViewModel.UpdateSelected(e.SelectedItem as PersistenceDuration);
}
}
The page's code behind instantiates a view model and calls an UpdateSelected method with the newly selected item on the page's view model*
RadioButton ViewModel
The view model for each radio button:
public class PersistenceDuration : ViewModelBase
{
bool isSelected;
public string Caption { get; set; }
public TwoFactorTokenPersistenceDuration Duration { get; set; }
public bool IsSelected
{
get => isSelected;
set
{
isSelected = value;
OnPropertyChanged();
OnPropertyChanged("IsUnselected");
}
}
public bool IsUnselected => !IsSelected;
public PersistenceDuration(string caption, TwoFactorTokenPersistenceDuration duration)
{
Caption = caption;
Duration = duration;
IsSelected = false;
}
}
The radio button view model holds selection info and the caption. We make sure to fire OnPropertyChanged whenever the selected state changes
Page ViewModel
public class LoginPageViewModel : ViewModelBase
{
PersistenceDuration duration;
PersistenceDuration selectedDuration;
public ObservableCollection<PersistenceDuration> Durations { get; }
public PersistenceDuration SelectedDuration
{
get => selectedDuration;
set
{
if (value != null)
{
duration = value;
UpdateSelected(duration);
}
OnPropertyChanged();
}
}
public LoginTwoFactorFrequencyViewModel()
{
Durations = new ObservableCollection<PersistenceDuration>(
new List<PersistenceDuration>()
{
new PersistenceDuration(AppResources.Save_code__forever, TwoFactorTokenPersistenceDuration.Forever),
new PersistenceDuration(AppResources.ChatRequireEvery30Days, TwoFactorTokenPersistenceDuration.ThirtyDays),
new PersistenceDuration(AppResources.ChatRequireEveryLogin, TwoFactorTokenPersistenceDuration.None),
});
}
public void UpdateSelected(PersistenceDuration persistenceDuration)
{
foreach (var item in Durations)
item.IsSelected = persistenceDuration == item;
}
}
In the page view model we create a list of radio button view models that the XAML binds to. When we UpdateSelected() all the IsSelected states are updated which trigger binding updates which flip the image.
You will still need to do something about the highlight when someone selects an item, but that is easy enough to find on the internet :)
You can use XLabs plugin from manage NuGets package. After installing you can use like this:
In Xaml:
controls:BindableRadioGroup x:Name="Radiobtn"
In C#:
string[] gender = {"MAlE","FEMALE"}
Radiobtn.Add(gender)
Refer Link
https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/samples/XLabs.Samples/XLabs.Samples/Pages/Controls
You can get the radio button effect without a package. Use Labels with text unicode circle \u26AA or \u25CB. Attach a tab gesture recognizer to each label.
When tapped, change the text of the selected button to unicode circle bullet \u29BF and change the text of the other button(s) back to unicode circle \u26AA.
Test on your preferred platforms as each platform may display somewhat differently. You may need to adjust the font size as you change the text.
If you want real radiobuttons you can xlabs their package (https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/src/Forms/XLabs.Forms/Controls/RadioButton)
Personally I'd just use a picker, Xlabs package hasn't been updated in a while so their might be some bugs in the radiobutton
You can use image as a radio button. When tou you click on it, it can change. It is not a good way to do it though.
This is xaml code:
<Image Scale="0.7" HorizontalOptions="Start" x:Name="radioButton" Source="unRadioBtn.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="radioButton_Clicked"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
And this is .cs:
private void radioButton_Clicked(object sender, EventArgs e)
{
radioButton.Source = "radioBtn.png";
}
Xamarin.Forms 4.6 introduced a new RadioButton control. You can find the documentation here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/radiobutton
You can use the switch component. Also you can see the implementation for a checkbox component from the XLabs project which is now discontinued, get the code and modify it as you need.
Hint: You're gonna need the custom renderers per platform for it to work .
You need to use Picker
https://developer.xamarin.com/api/type/Xamarin.Forms.Picker/
Actually it is the best alternative to RadionButton On Xamarin.forms
XLabs RadioButton and BindableRadioGroup work well: XLabs RadioButton for Xamarin Forms
Here's a simple Yes/No radio using the BindableRadioGroup:
var answers = new List<string>();
answers.Add("Yes");
answers.Add("No");
var RadioGroup = new XLabs.Forms.Controls.BindableRadioGroup()
{
ItemsSource = answers,
Orientation = StackOrientation.Horizontal
};
Xamarin Forms now provides a Radio Button control.
See docs here:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/radiobutton
As of XF 4.8 this is still experimental and I've not yet used this feature so can't comment on its stability.

Resources