Xamarin Forms Button Command doesn't fire after adding button effect - xamarin

I have a button like this:
<Button Margin="0,20,0,0" Command="{Binding OnSkip}" BackgroundColor="{StaticResource Primary}" CornerRadius="2"
Text="Terms and Conditions of Use" VerticalOptions="End" TextColor="White">
<Button.Effects>
<effects1:ButtonClickEffect></effects1:ButtonClickEffect>
</Button.Effects>
</Button>
Upon adding the button effect inside the button, the 'OnSkip' command no longer fires and I'm not sure why.
The button click effect code is implemented as follows:
public class AndroidButtonClickEffect : PlatformEffect
{
protected override void OnAttached()
{
this.Control.Touch += this.Control_Touch;
}
private void Control_Touch(object sender, Android.Views.View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Down)
{
this.SetColor(Android.Graphics.Color.Blue);
}
else if (e.Event.Action == MotionEventActions.Up)
{
this.SetColor(Android.Graphics.Color.LightBlue);
}
}
private void SetColor(Android.Graphics.Color color)
{
this.Control.SetBackgroundColor(color);
}
protected override void OnDetached()
{
this.Control.Touch -= this.Control_Touch;
}
}
Removing the button effect causes the command to fire again. Why does the button effect interfere with the command firing? Is there a way I can get the effect to invoke the desired command (generically so I can reuse the effect)?
Thanks.

In the main project, I added the following class, which binds the Command:
public class ButtonClickEffect : RoutingEffect
{
public ButtonClickEffect() : base("Framework.ButtonClickEffect") { }
public static readonly BindableProperty CommandProperty =
BindableProperty.Create("Command", typeof(ICommand), typeof(ButtonClickEffect));
public static ICommand GetCommand(BindableObject view)
{
return (ICommand)view.GetValue(CommandProperty);
}
public static void SetCommand(BindableObject view, ICommand value)
{
view.SetValue(CommandProperty, value);
}
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.CreateAttached("CommandParameter", typeof(object),
typeof(ButtonClickEffect), (object)null);
public static object GetCommandParameter(BindableObject view)
{
return view.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(BindableObject view, object value)
{
view.SetValue(CommandParameterProperty, value);
}
}
The Android implementation was implemented as follows:
[assembly:ResolutionGroupName("Framework")]
[assembly:ExportEffect(typeof(AndroidButtonClickEffect), "ButtonClickEffect")]
namespace Framework.Droid.Effects
{
public class AndroidButtonClickEffect : PlatformEffect
{
protected override void OnAttached()
{
Control.Touch += Control_Touch;
}
private void Control_Touch(object sender, Android.Views.View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Down)
{
SetColor(Color.LightBlue);
}
else if (e.Event.Action == MotionEventActions.Up)
{
SetColor(Color.Blue);
}
var command = ButtonClickEffect.GetCommand(Element);
command?.Execute(ButtonClickEffect.GetCommandParameter(Element));
}
private void SetColor(Color color)
{
Control.SetBackgroundColor(color);
}
protected override void OnDetached()
{
Control.Touch -= Control_Touch;
}
}
}
I then removed the 'Command' property from my button and replaced it as follows:
<Button Margin="0,20,0,0" BackgroundColor="{StaticResource Primary}" CornerRadius="2"
Text="Terms and Conditions of Use" VerticalOptions="End" TextColor="White"
effects1:ButtonClickEffect.Command="{Binding OnSkip}" effects1:ButtonClickEffect.CommandParameter="{Binding .}">
<Button.Effects>
<effects1:ButtonClickEffect></effects1:ButtonClickEffect>
</Button.Effects>
</Button>
In all honesty, the command binding is a lot more awkward now (all this code just to get a simple button effect), but the important thing is that it now works. Now I need to work out how implement for iOS.
Credit from here for the answer and whoever posted this URL as an answer (it got deleted).

Related

How can we assign Application.Current to BindingContext

Iam very much new to xamarin and wondering about how BindingContext is used.
I was going through a tutorial wherein they used BindingContext = Application.Current
according to the docs Application.Current should return Application.
so how can this above statement work?
Firstly, create one property in APp.cs, implement interface INotifyPropertyChanged.
public partial class App : Application, INotifyPropertyChanged
{
private string _str;
public string str
{
get { return _str; }
set
{
_str = value;
RaisePropertyChanged("str");
}
}
public App()
{
InitializeComponent();
str = "this is test";
MainPage = new NavigationPage(new simplecontrol.Page26());
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
Then binding Application.Current for ContentPage BindingContext.
<ContentPage.Content>
<StackLayout>
<!--<local:View2 Items="{Binding modelas}" />-->
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding str}"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
this.BindingContext = Application.Current;

Pass OnAppearing to a ViewModel in Xamarin Forms MVVM?

I have business logic that loops around and does waits and other things. Currently this is in the code behind.
From what I have been able to read this is the wrong place and I should be placing this in the viewModel (correct me if wrong). If that's the case then should I have an OnAppearing method in my VM and if so how should I pass the OnAppearing to the View Model?
Currently my page OnAppearing looks like this:
protected async override void OnAppearing()
{
base.OnAppearing();
Title = Settings.mode.Text() + " Deck";
vm.LearnViewVisible = Settings.mode.IsLearn();
vm.PracticeViewVisible = Settings.mode.IsPractice();
vm.QuizViewVisible = Settings.mode.IsQuiz();
vm.QuizStartViewVisible = false;
If I am to be moving most of the business logic to the ViewModel then would that mean that all of this would move to an OnAppearing() method I create in the ViewModel?
Other way is using Behaviors.Forms from David Britch
...
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
</behaviors:EventHandlerBehavior>
<behaviors:EventHandlerBehavior EventName="Disappearing">
<behaviors:InvokeCommandAction Command="{Binding PageDisappearingCommand}" />
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
...
Original
Or Xamarin Community Toolkit EventToCommandBehavior
<ContentPage.Behaviors>
<xct:EventToCommandBehavior
EventName="Appearing"
Command="{Binding PageAppearingCommand}" />
<xct:EventToCommandBehavior
EventName="Disappearing"
Command="{Binding PageDisappearingCommand}" />
</ContentPage.Behaviors>
Related Question: EventHandlerBehavior vs EventToCommandBehavior
This is how i link my Viewmodel. I would recommend setting up a ViewModelBase with : VModelActive and VModelInactive
Code Behind:
public partial class YourClass : ContentPage
{
ViewModelClass viewModelClass;
public YourClass()
{
InitializeComponent();
viewModelClass = new ViewModelClass();
this.BindingContext = viewModelClass;
}
protected override void OnAppearing()
{
base.OnAppearing();
viewModelClass.VModelActive(this, EventArgs.Empty);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
viewModelClass.VModelInactive(this, EventArgs.Empty);
}
}
View Model
public override void VModelActive(Page sender, EventArgs eventArgs)
{
base.VModelActive(sender, eventArgs);
//your code
}
public override void VModelInactive(Page sender, EventArgs eventArgs)
{
base.VModelInactive(sender, eventArgs);
//your code
}
I prefer a pattern I first encountered in some Realm sample code.
A ViewModel base provides empty overrideable OnAppearing/Disappearing
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
internal virtual void OnAppearing() { }
internal virtual void OnDisappearing() { }
}
User classes descend from a base that conditionally invokes the VM.
public class BasePage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
(BindingContext as BaseViewModel)?.OnAppearing();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
(BindingContext as BaseViewModel)?.OnDisappearing();
}
}
// used as
public class JournalEntryDetailsViewModel : BaseViewModel
Warning: if you change the base class like this you need to use it in the XAML - use a scoped version of BasePage instead of the <ContentPage top element.
Otherwise you will get an error [CS0263] Partial declarations of 'JournalEntriesPage' must not specify different base classes
<?xml version="1.0" encoding="UTF-8"?>
<v:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:v="clr-namespace:QuickJournal.Views"
x:Class="QuickJournal.Views.JournalEntriesPage"
Title="Journal"
x:Name="page">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Command="{Binding AddEntryCommand}" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
Here is example from my solution
public partial class TaskDetailsPage : MvvmContentPage
{
private readonly TaskDetailsViewModel _model;
public TaskDetailsPage()
{
InitializeComponent();
Shell.SetNavBarIsVisible(this, true);
Shell.SetTabBarIsVisible(this, false);
_model = BindingContext as TaskDetailsViewModel;
}
protected override string NavigationRoute => UniqeCodes.Routes.TaskDetailsPage;
protected override void OnAppearing()
{
_model.Init();
}
}

My UI did not response for orientation change

I am developing a native mobile app for all platforms. I have created my own theme content page. Then after deployment on android when I make phone landscape it did not respond. what's the reason here.
Here is my base content page.
public abstract class BaseContentPage : ContentPage
{
public readonly BaseViewModel BaseViewModel;
protected bool _isNavigated = false;
public BaseContentPage(BaseViewModel baseViewModel)
{
BaseViewModel = baseViewModel;
}
public abstract void Navigate(SelectedItemChangedEventArgs e);
protected abstract override void OnAppearing();
protected override void OnDisappearing()
{
_isNavigated = true;
}
public BaseContentPage()
{
}
}
here Xaml
<views:BaseContentPage
xmlns:views="clr-namespace:DipsDemoXaml.Views"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Resource="clr-namespace:DipsDemoXaml.Resources"
x:Class="DipsDemoXaml.Views.WardListPage" Title="{x:Static Resource:AppResources.WardListPageTitle}">
<StackLayout BackgroundColor="{StaticResource DefaultBackgroundColor}" Orientation="Vertical" x:Name="s1">
I even try this also in code behind constructor I call size changed and create a method called Wardpagesizechanged.
public WardListPage(WardListPageViewModel wardListViewModel) : base(wardListViewModel)
{
InitializeComponent();
this.SizeChanged += wardpagesizechanged;
}
Wardpagesizechanged method
private void wardpagesizechanged(object sender, EventArgs e)
{
if(this.Width> this.Height)
{
s1.Orientation = StackOrientation.Horizontal;
}
else
{
s1.Orientation = StackOrientation.Vertical;
}
}
what is the problem here, I am clueless

Xamarin.Forms How to add Behaviors in code

What I am trying to achieve is limiting the input of an Entry field to two character via code and not XAML
This can be achieved in XAML using the below:
<Entry.Behaviors>
<local:NumberValidatorBehavior x:Name="ageValidator" />
<local:MaxLengthValidator MaxLength="2"/>
I assume I will need to do something like this but I'm not quite sure how to add the required behaviour property
entry.Behaviors.Add(new MyBehavior())
Edit Answer
After adding the MaxLengthValidator class listed below and calling it using the proposed method by #Rui Marinho my code is working as expected.
public class MaxLengthValidator : Behavior<Entry>
{
public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create("MaxLength", typeof(int), typeof(MaxLengthValidator), 0);
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
protected override void OnAttachedTo(Entry bindable)
{
bindable.TextChanged += bindable_TextChanged;
}
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Length > 0 && e.NewTextValue.Length > MaxLength)
((Entry)sender).Text = e.NewTextValue.Substring(0, MaxLength);
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= bindable_TextChanged;
}
}
entry.Behaviors.Add(new MaxLengthValidator { MaxLength = 2 });

How to create command menu item with checkbox?

I'm writing a VSPackage and I need to have menu item with checkbox, just like on this sample image below:
I went through this msdn reference regarding .vsct files, bud didn't fine any information explaining how to do it. What I have now is standard menu item with icon and text (code sample from MyPackage.vsct file):
<Buttons>
<Button guid="guidMyPackageCmdSet" id="cmdidMyPackage" type="Button">
<Icon guid="guidImages" id="myPackageBitmap" />
<CommandFlag>TextChanges</CommandFlag>
<CommandFlag>DontCache</CommandFlag>
<CommandFlag>FixMenuController</CommandFlag>
<Strings>
<ButtonText>MyPackage</ButtonText>
</Strings>
</Button>
</Buttons>
I need this additional checkbox. How to do it?
The properties like Checked, Visible, Enabled or Supported can´t be defined via the VSCT file. You need a command handler that controls the command´s state. I´ve created a base class that wraps the creation of the OleMenuCommand instance and handles the command´s BeforeQueryStatus event. This is a slimmed version of my implementation, but it will give you an idea how to solve it...
internal abstract class CommandHandler : IDisposable
{
private readonly OleMenuCommand command;
protected CommandHandler(Guid group, int id)
{
var commandid = CommandID(group, id);
this.command = new OleMenuCommand(this.Invoke, commandId);
this.command.BeforeQueryStatus += this.OnBeforeQueryStatus;
}
protected virtual void OnExecute() { }
protected virtual void OnQueryStatus(QueryStatusEventArgs e) { }
private void Invoke(object sender, EventArgs e)
{
this.OnExecute();
}
private void OnBeforeQueryStatus(object sender, EventArgs e)
{
OleMenuCommand command;
if ((command = sender as OleMenuCommand) != null)
{
var e = new QueryCommandEventArgs
{
Checked = command.Checked,
}
this.OnQueryStatus(e);
command.Checked = e.Checked;
}
}
public void Dispose()
{
this.command.BeforeQueryStatus -= this.OnBeforeQueryStatus;
}
}
public class QueryCommandEventArgs : EventArgs
{
public bool Checked { get; set; }
}
The CommandHandler class allows to control the state of any menu command. Just derive new handler implementations from it and override the OnExecute and OnQueryStatus methods, like...
internal sealed class MyCommand : CommandHandler
{
private bool checked;
public MyCommand() : base(GuidCmdSet, MyCommandId) { }
protected override void OnExecute()
{
this.checked = !this.checked; // toggle checked state
}
protected override void OnQueryStatus(QueryStatusEventArgs e)
{
e.Checked = this.checked;
}
}

Resources