简体   繁体   中英

Change Xamarin forms button color

I'm new to xamarin, I want to change color of a button on click after it should return back to default color once the process is completed, See the code below How can re render the button at run time.The command was executing first so I have handled it in click.This feature should work for android and iOS.

public class RippleButton : Button
{
    private readonly Color _defaultBackgroundColor = Color.FromRgb(255, 87, 34);
    private readonly Color _clickedBackgroundColor = Color.FromRgb(76, 175, 80);

    public ICommand ClickCommand
    {
        get { return (ICommand)GetValue(ClickCommandProperty); }
        set
        {
            SetValue(ClickCommandProperty, value);
        }
    }


    public static readonly BindableProperty ClickCommandProperty = BindableProperty.Create(
                                                   propertyName: nameof(ClickCommand),
                                                   returnType: typeof(ICommand),
                                                   declaringType: typeof(RippleButton),
                                                   defaultValue: null,
                                                   defaultBindingMode: BindingMode.TwoWay,
                                                   propertyChanged: OnClickCommandChanged);

    private static void OnClickCommandChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
    }

    public RippleButton()
    {
        const int animationTime = 10;


        TextColor = Color.FromRgb(255, 255, 255);
        BackgroundColor = _defaultBackgroundColor;

        Clicked += async (sender, e) =>
        {
            var btn = (RippleButton)sender;

            BackgroundColor = Color.FromRgb(76, 175, 80);

            ClickCommand?.Execute(btn.CommandParameter);

            await btn.ScaleTo(1.2, animationTime);
            await btn.ScaleTo(1, animationTime);

            BackgroundColor = _defaultBackgroundColor;
        };
    }

    private void ChangeColorOfButton()
    {
        BackgroundColor = _clickedBackgroundColor;
        Device.StartTimer(TimeSpan.FromSeconds(0.25), () =>
        {
            BackgroundColor = _defaultBackgroundColor;
            return false;
        });
    }
}

In order to await command execution - one of the ways would be to define an asynchronous command. Stephen Cleary has shared some awesome patterns on how to implement asynchronous commands .

You can define an async command as:

public interface IAsyncCommand : ICommand
{
    Task ExecuteAsync(object parameter);
}

public abstract class AsyncCommandBase : IAsyncCommand
{
    public abstract bool CanExecute(object parameter);
    public abstract Task ExecuteAsync(object parameter);

    public async void Execute(object parameter)
    {
        await ExecuteAsync(parameter);
    }

    public event EventHandler CanExecuteChanged;

    protected void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

public class AsyncCommand<TResult> : AsyncCommandBase, INotifyPropertyChanged
{
    private readonly Func<Task<TResult>> _command;
    private NotifyTaskCompletion<TResult> _execution;

    public AsyncCommand(Func<Task<TResult>> command)
    {
        _command = command;
    }

    public override bool CanExecute(object parameter)
    {
        return Execution == null || Execution.IsCompleted;
    }

    public override async Task ExecuteAsync(object parameter)
    {
        Execution = new NotifyTaskCompletion<TResult>(_command());
        RaiseCanExecuteChanged();
        await Execution.TaskCompletion;
        RaiseCanExecuteChanged();
    }

    public NotifyTaskCompletion<TResult> Execution
    {
        get { return _execution; }
        private set
        {
            _execution = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

You can now refactor your control as:

public class RippleButton : Button
{
    public static readonly BindableProperty ClickedBackgroundColorProperty =
        BindableProperty.Create(
            "ClickedBackgroundColor", typeof(Color), typeof(RippleButton),
            defaultValue: Color.FromRgb(76, 175, 80));

    public Color ClickedBackgroundColor
    {
        get { return (Color)GetValue(ClickedBackgroundColorProperty); }
        set { SetValue(ClickedBackgroundColorProperty, value); }
    }

    public static readonly BindableProperty AsyncCommandProperty =
        BindableProperty.Create(
            "AsyncCommand", typeof(IAsyncCommand), typeof(RippleButton),
            defaultValue: default(IAsyncCommand));

    public IAsyncCommand AsyncCommand
    {
        get { return (IAsyncCommand)GetValue(AsyncCommandProperty); }
        set { SetValue(AsyncCommandProperty, value); }
    }

    public RippleButton()
    {
        const int animationTime = 150;

        TextColor = Color.FromRgb(255, 255, 255);
        BackgroundColor = Color.FromRgb(255, 87, 34);

        Clicked += async (sender, e) =>
        {
            //execute command only if button is enabled.
            if (!IsEnabled)
                return;

            //continue only if command is executable, and not allow multiple click(s)
            if (AsyncCommand == null || !AsyncCommand.CanExecute(CommandParameter))
                return;

            var defaultColor = BackgroundColor;
            BackgroundColor = ClickedBackgroundColor;
            IsEnabled = false;

            await AsyncCommand.ExecuteAsync(CommandParameter);

            await this.ScaleTo(1.2, animationTime);
            await this.ScaleTo(1, animationTime);

            IsEnabled = true;
            BackgroundColor = defaultColor;
        };
    }
}

Sample usage:

XAML

<local:RippleButton Text="Download" 
                    AsyncCommand="{Binding SimulateDownloadCommand}" />

View Model

public class YourViewModel : BaseViewModel
{
    public YourViewModel()
    {
        SimulateDownloadCommand = new AsyncCommand<bool>(() => SimulateDownloadAsync());
    }

    private IAsyncCommand _downloadCommand;
    public IAsyncCommand SimulateDownloadCommand
    {
        get { return _downloadCommand; }
        private set
        {
            if (_downloadCommand != value)
            {
                _downloadCommand = value;
                OnPropertyChanged("SimulateDownloadCommand");
            }
        }
    }

    async Task<bool> SimulateDownloadAsync()
    {
        await Task.Run(() => SimulateDownload());
        return true;
    }

    void SimulateDownload()
    {
        // Simulate a 1.5 second pause
        var endTime = DateTime.Now.AddSeconds(1.5);
        while (true)
        {
            if (DateTime.Now >= endTime)
            {
                break;
            }
        }
    }
}

在此处输入图片说明 在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM