0

I have a view with a standard validation rule. It works as expected, if I tpye some text in the field, and then delete it, error icon and text are showing. Now, I would like to have the logic, if the user clicks the save button, the validation rule triggers if the field is empty. I have found on the net solutions with XAML codebehind, but I would like to have it in a proper MVVM pattern. Is this even possible? If yes, how to do that?

This is the viewmodel:

public class CapacityTypeViewModel
{
    private readonly AppDataContext _appDataContext;
    private readonly CapacityTypeService _capacityTypeService;

    // commands
    public UICommand SaveCommand { get; set; }

    // model
    private CapacityTypeModel _capacityType;
    public string Type { get; set; }

    public CapacityTypeViewModel(AppDataContext appDataContext)
    {
        _appDataContext = appDataContext;
        _capacityTypeService = new CapacityTypeService(_appDataContext);

        SaveCommand = new UICommand(SaveCapacity, CanSave);

        _capacityType = new CapacityTypeModel();

        Type = _capacityType.Type;
    }

    private void SaveCapacity()
    {
        _capacityTypeService.Save(Type);
        ThemedMessageBox.Show("Kapacitástípus", "A kapacitástípus sikeresen mentésre került", MessageBoxButton.OK, MessageBoxImage.Information);
    }

    private bool CanSave() => true;
}

And this is the related part of the view:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>
    <dxb:ToolBarControl UseWholeRow="True" AllowQuickCustomization="False">
        <dxb:BarButtonItem Content="Mentés" Glyph="{dx:DXImage 'SvgImages/Save/Save.svg'}" Command="{Binding SaveCommand}"
                           IsEnabled="{Binding ElementName=container, Path=(dxe:ValidationService.HasValidationError), Converter={dxmvvm:BooleanNegationConverter}}"></dxb:BarButtonItem>
    </dxb:ToolBarControl>
    <dxlc:LayoutControl Name="container" Grid.Row="1" dxe:ValidationService.IsValidationContainer="True">
        <dxlc:LayoutGroup>
            <dxlc:LayoutItem Label="Kapacitástípus" LabelPosition="Top">
                <dxe:TextEdit Name="tbCapacityType">
                    <Binding Path="Type" UpdateSourceTrigger="PropertyChanged">
                        <Binding.ValidationRules>
                            <Helpers:RequiredValidationRule FieldName="Kapacitástípus"/>
                        </Binding.ValidationRules>
                    </Binding>
                </dxe:TextEdit>
            </dxlc:LayoutItem>
        </dxlc:LayoutGroup>
    </dxlc:LayoutControl>
</Grid>
derstauner
  • 1,478
  • 2
  • 23
  • 44

1 Answers1

0

Simply invoke the validation rule before executing the command.

You should always let the binding source i.e. your view model implement INotifyPropertyChanged to avoid bad performance and memory leaks.

It's also recommended to validate the view model by implementing INotifyDataErrorInfo rather than using binding validation.

The following example validates on exception to keep the example simple. As noticed before it's recommended to implement INotifyDataErrorInfo instead of throwing exceptions. Exception generally have a negative performance impact.

public class CapacityTypeViewModel
{
    public ICommand SaveCommand => new UICommand(SaveCapacity, CanSave);

    public string Type { get; set; }
    private Dictionary<string, ValidationRule> ValidationRules { get; }

    public CapacityTypeViewModel(AppDataContext appDataContext)
    {
        _appDataContext = appDataContext;

        this.ValidationRules = new Dictionary<string, ValidationRule> 
        { 
            { nameof(this.Type), new RequiredValidationRule {FieldName = "Kapacitástípus"} } 
        };        
    }

    private void SaveCapacity()
    {
        if (!this.ValidationRules.TryGetValue(nameof(this.Type), out Validationrule rule)
          || !rule.Validate(this.Type, CultureInfo.CurrentCulture).IsValid)
        {
            // Notify the view (via the data binding) about the validation error.
            // Requires the Binding to enable Binding.ValidatesOnException.
            // Consider to let the view model implement INotifyDataErrorInfo instead of throwing exceptions.
            throw new ArgumentException(rule.ErrorContent as string);
        }

        _capacityTypeService.Save(Type);
        ThemedMessageBox.Show("Kapacitástípus", "A kapacitástípus sikeresen mentésre került", MessageBoxButton.OK, MessageBoxImage.Information);
    }

    private bool CanSave() => true;
}

View

<dxe:TextEdit Name="tbCapacityType">
  <Binding Path="Type"  
           ValidatesOnExceptions="True" 
           UpdateSourceTrigger="PropertyChanged" />
</dxe:TextEdit>
BionicCode
  • 1
  • 4
  • 28
  • 44