Question

I have a WPF form with multiple controls, and I want a button ('Assign') to be enabled if and only if a variety of conditions on those controls are true. Some conditions include testing whether textboxes are empty.

I initially achieved this by binding the contents of the textboxes to properties and binding the IsEnabled property of the button in the XAML:

<TextBox Name="NewName" Text="{Binding NewName}" />
(etc)

<Button Name="Assign" ... IsEnabled="{Binding Path=AssignEnabled}" />

with a corresponding method in the C# ViewModel:

public bool AssignEnabled
{
    get
    {
        return !string.IsNullOrWhiteSpace(this.NewName) && ... (etc)
    }

    set
    {
        ...
        this.NotifyPropertyChanged("AssignEnabled");
        ...
    }
}

The problem was that this caused the button to be updated when the focus was lost from the respective textbox, not whenever the text was changed. I could have used the TextChanged property on each textbox in the XAML to call code, but this seemed overcomplicated.

To fix this, I removed the binding and switched to DataTriggers like this:

<Button Name="Assign" ... >
    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="IsEnabled" Value="True"/>
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding IsChecked, ElementName=NewNameOption}" Value="true" />
                        <Condition Binding="{Binding Text, ElementName=NewName}" Value="{x:Static sys:String.Empty}" />
                    </MultiDataTrigger.Conditions>
                    <Setter Property="IsEnabled" Value="False"/>
                </MultiDataTrigger>
                ...
                (more conditions here)
                ...
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

This worked absolutely fine. The only drawback was that the unit tests I had previously written to test the IsEnabled status of the button were no longer usable (since they used the AssignEnabled property of the ViewModel which is no longer bound to the button).

To allow those unit tests to work, I needed the AssignEnabled property to reflect the IsEnabled status of the button. I expected to be able to fix this by adding a OneWayToSource binding, like this:

<Button Name="Assign" ... IsEnabled="{Binding AssignEnabled, Mode=OneWayToSource}">

with the property changed to:

public bool AssignEnabled { get; set; }

However, it seems as though this binding, even though set to OneWayToSource, overrides the DataTriggers, since the enabling/disabling of the button no longer works at all.

I know there are other options, such as using a Converter or implementing ICommand, but I would prefer to keep this simple if possible and fix the above method (or at least understand why it doesn't work). How can I access the IsEnabled property of the button without violating the ViewModel paradigm or preventing the DataTriggers from working?

Was it helpful?

Solution

You can keep your first solution and set UpdateSourceTrigger=PropertyChanged for the bindings. In this way Bindings will change instantly when text changes and not only when focus is lost. By the way this solution gives you mor flexibility as you can perform more complex testing on your fields (for exemple test email adress format).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top