1
votes

I have a class called ViewModel that implements the INotifyPropertyChanged interface. In my XAML I have 3 textboxes that have a binding property that match my ViewModel properties:

TestsOrdered TestsCompleted TestsRemaining

I have a PropertyChanged event handler in my code-behind. I have a context "MyContext" attached to the XAML window. I am unsure why the UI does not update when I change the TestsOrdered and TestsCompleted values. I expect the UI to update when I tab out of the textboxes but does not until I tab out of the TestsRemaining textbox. Where is the flaw in my design ?

public partial class MainWindow : Window
{
    public ViewModel vm;

    public MainWindow()
    {
        InitializeComponent();
        vm = new ViewModel();
        //ct = new CompletionTime();
        //ct.TestsOrdered = 8000;

        vm.Name = "John Doe";
        vm.TestsCompleted = 0;
        vm.TestsOrdered = 0;
        vm.TestsRemaining = 0;

        this.MyContext.DataContext = vm;
    }

    private void btnName_Click(object sender, RoutedEventArgs e)
    {
        vm.Name = "Joe";
    }
}

public class ViewModel : INotifyPropertyChanged
{
    private string name;
    private int mTestsOrdered;
    private int mTestsRemaining;
    private int mTestsCompleted;

    public string Name
    {
        get { name; }
        set
        {
            if (name != value)
            {
                name = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Name"));
                }
            }
        }
    }


    public int TestsOrdered
    {
        get { return mTestsOrdered; }
        set
        {
            if (mTestsOrdered != value)
            {
                mTestsOrdered = value;
                PropertyChanged(this, new PropertyChangedEventArgs("TestsOrdered"));
            }
        }
    }


    public int TestsCompleted
    {
        get { return mTestsCompleted; }
        set
        {
            if (mTestsCompleted != value)
            {
                mTestsCompleted = value;
                PropertyChanged(this, new PropertyChangedEventArgs("TestsCompleted"));
            }
        }
    }


    public int TestsRemaining
    {
        get { return TestsOrdered - TestsCompleted; }
        set
        {
            if (mTestsRemaining != value)
            {
                mTestsRemaining = value;
                PropertyChanged(this, new PropertyChangedEventArgs("TestsRemaining"));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

MyContext is the DataContext that holds a ViewModel object.

XAML:

 <Grid Margin="-1,-9,1,9">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="244*"/>
            <ColumnDefinition Width="251*"/>
        </Grid.ColumnDefinitions>
        <TextBox Width="100"  HorizontalAlignment="Center" Margin="96.791,40,54,0" TextWrapping="Wrap" Text="{Binding Path=Name}" VerticalAlignment="Top" Grid.Column="1"/>

        <Button x:Name="btnName" Content="Button" HorizontalAlignment="Left" Margin="171,210,0,0" VerticalAlignment="Top" Width="148" Height="47" Click="btnName_Click" Grid.ColumnSpan="2"/>
        <TextBox Grid.Column="0" HorizontalAlignment="Left" Height="23" Margin="97,84,0,0" TextWrapping="Wrap" Text="{Binding Path=TestsOrdered}" VerticalAlignment="Top" Width="120"/>
        <TextBox Grid.Column="0" HorizontalAlignment="Left" Height="23" Margin="97,112,0,0" TextWrapping="Wrap" Text="{Binding Path=TestsCompleted}" VerticalAlignment="Top" Width="120"/>
        <TextBox Grid.Column="0" HorizontalAlignment="Left" Height="23" Margin="97,140,0,0" TextWrapping="Wrap" Text="{Binding Path=TestsRemaining}" VerticalAlignment="Top" Width="120"/>

    </Grid>
1
Could you please provide a code example of when you bind the property in xaml?Anders
Also, what is MyContext?Anders
Hi Nautilous. I have added some of the XAML. I am under the impression that I assign the Binding Path in XAML and WPF takes care of the rest behind the scenes. What am I missing ?Bill Greer
You are correct, but you place the view model instance on the wrong property of your window instance. The Binding class uses the Window property: DataContext as it's default object to bind to. You could say that the full path of your bindings are <WindowInstance>.DataContext.<PathDefinded>. The Solution is to set your view model instance on the property DataContext. I'll write a answer to you.Anders

1 Answers

1
votes

Your Bindings and your Properties are correctly written. However the issue is in your assignment of the viewmodel instance to the Window. You need to set the instance of your viewmodel on the property DataContext for the bindings to work. Like this:

 public MainWindow()
{
    InitializeComponent();
    vm = new ViewModel();
    //ct = new CompletionTime();
    //ct.TestsOrdered = 8000;

    vm.Name = "John Doe";
    vm.TestsCompleted = 0;
    vm.TestsOrdered = 0;
    vm.TestsRemaining = 0;

    //this.MyContext.DataContext = vm;
    this.DataContext = vm; // <-- Like so
}

The reason is the way bindings work. If no other source (Source, RelativeSource or ElementName properties of Binding) are provided, bindings will use the DataContext property of the FrameworkElement they are assigned to. In your case the TextBox.

When you set the DataContext property of the Window, then all it's child objects inherit that object instance, and their children, and so on.

Try to change your ViewModel assignment and you will see the bindings work.

//Cheers