bananeeek
bananeeek

Reputation: 193

How to allow pressing only the cancel button when the value is invalid?

I have a simple dialog with a SpinEdit and two buttons: OK_Button and Cancel_Button. I've set a mask for the value in the SpinEdit and the dialog won't let me press the cancel button when the value is invalid. I've tried changing the SpinEdit's property to InvalidValueBehavior="AllowLeaveEditor" but then I can click both, OK and cancel button. Is there a way to ONLY allow pressing cancel when the value is incorrect?

XAML:

 <dxe:SpinEdit x:Name="dxSpinEdit" 
               Height="23" MinWidth="200" Width="Auto"
               HorizontalAlignment="Right"
               Text="{Binding Value, Mode=TwoWay}"
               MaskType="Numeric"
               IsFloatValue="{Binding FloatValue}"
               MinValue="{Binding MinValue}"
               MaxValue="{Binding MaxValue}"
               Mask="{Binding Mask, Mode=OneWay}" 
               MaxLength="{Binding Path=InputLength}"
               MaskShowPlaceHolders="{Binding ShowPlaceHolder}"
               InvalidValueBehavior="WaitForValidValue"
     />

  <StackPanel Grid.Row="1" x:Uid="OKCancel_Buttons" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
     <Button Height="23" x:Name="OK_Button" Click="OK_Click" Content="OK" IsDefault="True" HorizontalAlignment="Right" MinWidth="95" />
     <Button Height="23" x:Name="Cancel_Button" Click="Cancel_Click" Content="Cancel" HorizontalAlignment="Right" MinWidth="95" PreviewMouseDown="win_PreviewMouseDown" />
  </StackPanel>

I looked up this issue on the devexpress forum but their solution didn't work for me. I've implemented the MouseDownPreview event like so:

C# (code behind)

  private void OK_Click(object sender, RoutedEventArgs e)
  {
     DialogResult = true;
     Close();
  }

  private void Cancel_Click(object sender, RoutedEventArgs e)
  {
     DialogResult = false;
     Close();
  }

  private void win_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
  {
     if(e.Source == Cancel_Button)
     {
        DialogResult = false;
        Close();
     }
  }

But the event wasn't handled at all. I'd like to keep the property InvalidValueBehavior at the value "WaitForValidValue" but at the same time I'd like to allow pressing the Cancel button.

Upvotes: 0

Views: 404

Answers (1)

Peregrine
Peregrine

Reputation: 4546

Even if you're not going to go the full MVVM route, you should switch from using click events to an ICommand implementation that supports CanExecute logic (such as this one from MVVM Light).

Using a command will automatically disable any bound control (e.g. button or menu item) when CanExecute is false. You can then have all the logic for controlling your commands grouped in one place, including validation that will only allow OK to be clicked when your object is in a valid state.

If you just want to go the standard WPF (non MVVM) route, you could add something like this in your window's constructor

public MyView()
{
    ....


    Ok_Button.Command = 
        new RelayCommand(() => DialogResult = true, // just setting DialogResult is sufficient, no need to call Close()
                         // put the required validation logic here  
                         () => dxSpinEdit.Value > 0 && dxSpinEdit.Value < 10);

    Cancel_Button.Command = new RelayCommand(() => DialogResult = false);

    // replace this with the actual event from SpinEdit
    dxSpinEdit.ValueChanged += (s,e) => (OK_Button.Command as RelayCommand).RaiseCanExecuteChanged();
}

Yes I know it looks ugly 😀 - I'd suggest following the MVVM design pattern instead. When using MVVM, all of the command functionality belongs in your ViewModel.

Either way, you can then remove all the click and mousedown handlers from your buttons.

Upvotes: 1

Related Questions