Modern UI for WPF - ModernDialog: Allow Ok Clicking Only Under Some Conditions

I started a new small project using ModernUI for WPF.

I need to display a dialog with two text boxes and two buttons (ok / cancel).

The two text boxes have some validation (one name must be> 0, the other must be electronic).

I currently have the following:

        var userEditionForm = new UserEditionForm(oneUserConfigurationViewModel);
        var dlg = new ModernDialog
        {
            Title = "User edition",
            Content = userEditionForm,
            Width = 300
        };
        dlg.Buttons = new[] {dlg.OkButton, dlg.CancelButton};
        dlg.ShowDialog();
        return dlg.DialogResult.HasValue && dlg.DialogResult.Value;

      

Content control:

<UserControl x:Class="Test.UserControls.UserEditionForm"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel.Resources>
                <Style TargetType="StackPanel">
                    <Setter Property="Orientation" Value="Horizontal" />
                    <Setter Property="Margin" Value="0,0,0,4" />
                </Style>
                <Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
                    <Setter Property="Width" Value="100" />
                    <Setter Property="VerticalAlignment" Value="Center" />
                </Style>
            </StackPanel.Resources>

            <StackPanel>
                <Label Content="Name" Target="{Binding ElementName=TextFirstName}" />
                <TextBox x:Name="TextFirstName" Width="150"
                         Text="{Binding User.Name, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay, ValidatesOnDataErrors=True}" />
            </StackPanel>
            <StackPanel>
                <Label Content="Email" Target="{Binding ElementName=TextEmail}" />
                <TextBox x:Name="TextEmail" Width="150"
                         Text="{Binding User.Email, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay, ValidatesOnDataErrors=True}" />
            </StackPanel>
        </StackPanel>
    </Grid>
</UserControl>

      

The validation is in progress, but I want the user not to click OK until the validation is good. Is it possible? If so, how?

+3


source to share


1 answer


It was quite tricky, but it worked for me:

ViewModel

public class UserInfo : INotifyPropertyChanged, IDataErrorInfo
{
    private static Regex emailRegex = new Regex(@"^\w+(?:\.\w+)*?@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$");

    private string firstname;
    private string email;

    public string FirstName
    {
        get { return firstname; }
        set { firstname = value; OnPropertyChanged(); }
    }

    public string EMail
    {
        get { return email; }
        set { email = value; OnPropertyChanged(); }
    }

    public string Error
    {
        get { return null; }
    }

    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "FirstName":
                    return string.IsNullOrWhiteSpace(FirstName) ? "Firstname is required!" : null;
                case "EMail":
                    return EMail == null || !emailRegex.IsMatch(EMail) ? "The Email Address is not valid!" : null;
                default:
                    return null;
            }
        }
    }

    public LoginCommand LoginCommand { get; private set; }

    public UserInfo()
    {
        LoginCommand = new LoginCommand(this);
    }

    private void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

      

Team (don't know what you want to call)



public class LoginCommand : ICommand
{
    private UserInfo info;
    private ICommand attachedCommand;

    public bool CanExecute(object parameter)
    {
        return parameter != null && info["FirstName"] == null && info["EMail"] == null;
    }

    public event EventHandler CanExecuteChanged = delegate { };

    public void Execute(object parameter)
    {
        Debug.WriteLine("This Works!");

        //Add execution logic here

        if (attachedCommand != null)
        {
            attachedCommand.Execute(parameter); //should close the window
        }

    }

    private void Info_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        CanExecuteChanged(this, new EventArgs());
    }

    public LoginCommand(UserInfo info)
    {
        this.info = info;
        info.PropertyChanged += Info_PropertyChanged;
    }

    public void AttachCommand(ICommand command) //attach the original command here
    {
        attachedCommand = command;
    }
}

      

Using with ModernUI

UserInfo info = new UserInfo();
UserInfoForm form = new UserInfoForm(info);
ModernDialog dialog = new ModernDialog
{
    Title = "User edition",
    Content = form,
    Width = 300
};
Button btnOk = dialog.OkButton;
ICommand originalCommand = btnOk.Command; //the original command should close the window so i keep it
btnOk.Command = info.LoginCommand;
info.LoginCommand.AttachCommand(originalCommand); //and attach it to my command
dialog.Buttons = new[] { btnOk, dialog.CancelButton };
dialog.ShowDialog();

      

+3


source







All Articles