1

I am not using the mvvm-pattern. I directly bind my UI to a model.

My problem is that the binding to the model-properties works. The textbox gets a red border if the model-property MyStringValue is invalid and the MyBoolValue-property also changes. But my button doesn't update the IsEnabled-property.

My XAML looks like that

<Window x:Class="OkButtonTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OkButtonTest"
        mc:Ignorable="d"
        Title="MainWindow">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Text="{Binding MyStringValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
        <Button x:Name="MyExampleButton" Grid.Row="1" IsEnabled="{Binding MyBoolValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Click="Button_Click" />
    </Grid>
</Window>

The DataContext is bound to the model at the csharp-code:

using System.Windows;

namespace OkButtonTest
{
    public partial class MainWindow : Window
    {
        ModelExample modelExample = new ModelExample();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = modelExample;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Button was clicked");
        }
    }
}

Notice that i have implemented Fody.PropertyChanged in my model, so i dont need to implement PropertyChanged for each property.

using System;
using System.ComponentModel;

namespace OkButtonTest
{
    public class ModelExample : INotifyPropertyChanged, IDataErrorInfo
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string myStringValue = "";
        public string MyStringValue
        {
            get
            {
                return myStringValue;
            }
            set
            {
                myStringValue = value;
            }
        }

        private bool myBoolValue;
        public bool MyBoolValue
        {
            get
            {
                return myBoolValue;
            }
            set
            {
                myBoolValue = value;
                Console.WriteLine($"MyBoolValue is {MyBoolValue.ToString()}");
            }
        }

        public string Error
        {
            get
            {
                if(MyStringValue.Length < 5)
                {
                    MyBoolValue = false;
                    return "Invalid string value";
                }

                MyBoolValue = true;
                return string.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                return Error;
            }
        }
    }
}

Why doesnt my button update?

Basssprosse
  • 334
  • 1
  • 3
  • 17
  • 1
    Use `ICommand ` with CanExecute http://www.c-sharpcorner.com/UploadFile/20c06b/icommand-and-relaycommand-in-wpf/ – Peter Dec 13 '17 at 10:41
  • Possible duplicate of [How does one "disable" a button in WPF using the MVVM pattern?](https://stackoverflow.com/questions/3476686/how-does-one-disable-a-button-in-wpf-using-the-mvvm-pattern) – Sinatr Dec 13 '17 at 10:52
  • if you do not want to use ICommand, try the following: put your button inside a Grid and set IsEnabled on that Grid and see if it works ` – Celso Lívero Dec 13 '17 at 10:55
  • Do you get something like "1> Fody: Fody (version 2.2.1.0) Executing 1> Fody/PropertyChanged: No reference to 'PropertyChanged' found. References not modified. 1> Fody: Finished Fody 177ms." In the output? – Janne Matikainen Dec 13 '17 at 11:27
  • I hoped a command is not neccessary because i don t work with MVVM-pattern. Finally i got the solution (missing xml-tag in fody)... – Basssprosse Dec 13 '17 at 13:06

2 Answers2

1

Seems you should be using following syntax to allow fody preprocess your viewmodel getters and setters.

[AddINotifyPropertyChangedInterface]
public class ModelExample : IDataErrorInfo
{
    ...
}

Also make sure you have this in your FodyWeavers.xml

<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
  <PropertyChanged/>
</Weavers>
Janne Matikainen
  • 5,061
  • 15
  • 21
1

Only return a string from the Error property and set the MyBoolValue property in the setter of MyStringValue:

public class ModelExample : INotifyPropertyChanged, IDataErrorInfo
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string myStringValue = "";
    public string MyStringValue
    {
        get
        {
            return myStringValue;
        }
        set
        {
            myStringValue = value;
            MyBoolValue = !string.IsNullOrEmpty(myStringValue) && myStringValue.Length > 4;
        }
    }

    private bool myBoolValue;
    public bool MyBoolValue
    {
        get
        {
            return myBoolValue;
        }
        set
        {
            myBoolValue = value;
        }
    }

    public string Error
    {
        get
        {
            if (MyStringValue.Length < 5)
            {
                return "Invalid string value";
            }
            return string.Empty;
        }
    }

    public string this[string columnName]
    {
        get
        {
            return Error;
        }
    }
}

A getter shouldn't set anything. Your current approach of setting the MyBoolValue in the getter of the Error property results in a StackoverflowExcpetion being thrown when I run it.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Yeah, I was also checking that the validation might cause an issue, because now it will validate both MyStringValue and MyBoolValue with the same validation logic, this should be decided in the this[string columnName] on what to validate based on the indexer, ie only return the Error when the columnName equals MyStringValue. And do nothing for the MyBoolValue. – Janne Matikainen Dec 13 '17 at 11:43
  • Curiously i do not get an exception. What if i have to check a validation for ten properties? In ur way i need to repeat the validation in each of of my property-setters. In my way i just need to check it in my model-getter. – Basssprosse Dec 13 '17 at 13:04
  • Just call a method from each setter where you perform the validation. In your current approach you can set the MyStringValue *without* validating, for example in a unit test. This is clearly wrong. – mm8 Dec 13 '17 at 13:04