views:

30

answers:

2

I'm trying to build a behavior that disables the button and changes the text to follow the common pattern of disabling a button to stop a user from double clicking. I need the behavior changes to take effect before the commmand. Can I use a behavior this way?

view: (set the DataContext in the loaded event of the code behind)

<Grid x:Name="LayoutRoot" Background="White">
    <Button Height="50" Width="150" Content="Save" Command="{Binding ButtonClickedCommand}" IsEnabled="{Binding IsNextButtonEnabled}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Click">
                <behaviors:SubmitButtonBehavior LoadingButtonText="WAIT!!!..." IsEnabled="True" IsFinished="{Binding IsFinishedSubmitButtonBehavior}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Button>
</Grid>

ViewModel

using System.Windows; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; namespace SubmitButtonBehaviorTest { public class MainPageViewModel : ViewModelBase { /// /// Private isFinishedSubmitButtonBehavior property. ///
private bool isFinishedSubmitButtonBehavior;

    public bool IsFinishedSubmitButtonBehavior
    {
        get
        {
            return this.isFinishedSubmitButtonBehavior;
        }

        set
        {
            this.isFinishedSubmitButtonBehavior = value;
            this.RaisePropertyChanged("IsFinishedSubmitButtonBehavior");
        }
    }

    /// <summary>
    /// Private ButtonClickedCommand property.
    /// </summary>            
    private RelayCommand buttonClickedCommand;

    public RelayCommand ButtonClickedCommand
    {
        get
        {
            return this.buttonClickedCommand;
        }

        private set
        {
            this.buttonClickedCommand = value;
        }
    }

    public MainPageViewModel()
    {
        this.ButtonClickedCommand = new RelayCommand(
            () =>
            {
                MessageBox.Show("Clicked");
                this.IsFinishedSubmitButtonBehavior = true;
            });
    }
}

}

Behavior:

namespace SubmitButtonBehaviorTest.Behaviors

{ using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity;

/// <summary>
/// Attach this behavior to disable the button on click and change the text
/// </summary>
public class SubmitButtonBehavior : TriggerAction<UIElement>
{
    /// <summary>
    /// The original button text, reset when the IsEnabled is set back.
    /// </summary>
    public string OriginalButtonText { get; set; }

    /// <summary>
    /// Gets or sets the loading button text.
    /// </summary>
    /// <value>The loading button text.</value>
    public string LoadingButtonText { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether [change button text].
    /// </summary>
    /// <value><c>true</c> if [change button text]; otherwise, <c>false</c>.</value>
    public bool ChangeButtonText { get; set; }

    /// <summary>
    /// Set this to true when the operation is finished.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this instance is finished; otherwise, <c>false</c>.
    /// </value>
    public bool IsFinished
    {
        get { return (bool)GetValue(IsFinishedProperty); }
        set
        {
            SetValue(IsFinishedProperty, value);
            this.OnIsFinishedChanged();
        }
    }

    /// <summary>
    /// Called when [is finished change].
    /// </summary>
    /// <param name="value">if set to <c>true</c> [value].</param>
    private void OnIsFinishedChanged()
    {
        if (this.AssociatedObject != null && !((Button)this.AssociatedObject).IsEnabled)
        {
            ((Button)this.AssociatedObject).IsEnabled = true;
            ((Button)this.AssociatedObject).Content = this.OriginalButtonText;
        }
    }

    // Using a DependencyProperty as the backing store for IsFinished.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsFinishedProperty =
         DependencyProperty.Register("IsFinished", typeof(bool), typeof(SubmitButtonBehavior), new PropertyMetadata(false));

    /// <summary>
    /// Initializes a new instance of the <see cref="SubmitButtonBehavior"/> class.
    /// </summary>
    public SubmitButtonBehavior()
    {
        // defaults
        this.ChangeButtonText = true;
        this.LoadingButtonText = "Please Wait...";
        // AssociatedObject is empty at initialization this.originalButtonText = this.AssociatedObject.Content.ToString();
    }

    protected override void Invoke(object parameter)
    {
        if (this.IsEnabled)
        {
            Button clickedButton = ((Button)this.AssociatedObject);
            clickedButton.IsEnabled = false;
            this.OriginalButtonText = clickedButton.Content.ToString();
            if (this.ChangeButtonText)
            {
                clickedButton.Content = this.LoadingButtonText;
            }
        }
    }
}

}

EDIT: Suggestion with disable through CanExecute. The button text still does not change. Also I would still like to make this a reusable behavior, but this could be a good approach as well.

this.ButtonClickedCommand = new RelayCommand<Button>(
            (clickedButton) =>
            {
                string originalText = clickedButton.Content.ToString();
                this.IsSubmitting = true;
                clickedButton.Content = "Please Wait...";
                MessageBox.Show("Clicked");
                this.IsFinishedSubmitButtonBehavior = true;
                this.IsSubmitting = false;
                clickedButton.Content = originalText;
            },
            (clickedButton) =>
            {
                return !this.IsSubmitting;
            }
            );

Edit: I've found a good solution using messaging and added that as an answer to my own question.

A: 

As Dan commented you are probably better off using CanExecute to implement your disable logic. As for changing button text I would recommend using a binding to this.IsSubmitting through a ValueConverter that chooses the correct text. This minimizes the interaction between your ViewModel and the View in that all the ViewModel does is say I'm submitting and the View will read that and display the correct text.

Stephan