NotifyPropertyChanged on dependent properties: how to?

Mar 24, 2011 at 4:04 PM

Hi,

I am new to the Cinch Framework and came accross a simple task that I am not able to perform. I have a property (IsNotBusy) that depends on another (IsBusy). So when changing IsBusy I need to send NotifyPropertyChanged also to IsNotBusy. But the way I thought this would be done in Cinch throws me a null-ref exception. What is wrong with this code:

        #region Property IsBusy
        private bool _isBusy = true;

        public static readonly PropertyChangedEventArgs isBusyEventArgs = ObservableHelper.CreateArgs<ProcessControllerVM>(x => x.IsBusy);

        public bool IsBusy
        {
            get { return _isBusy; }
            set

            {  if (object.ReferenceEquals(_isBusy, value)) return;
                _isBusy = value;
                NotifyPropertyChanged(isBusyEventArgs);
                NotifyPropertyChanged(isNotBusyEventArgs);
            }
        }

        public static readonly PropertyChangedEventArgs isNotBusyEventArgs = ObservableHelper.CreateArgs<ProcessControllerVM>(x => x.IsNotBusy);
        public bool IsNotBusy
        {
            get { return !_isBusy; }
        }

        #endregion

Coordinator
Mar 24, 2011 at 5:03 PM

I have tried to repeat your arrangement and can't see any problems at all.

 

I have this for App.xaml.cs

 

 

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using Cinch;
using System.Reflection;

namespace Test
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region Initialisation
        /// <summary>
        /// Tell Cinch what Assemblies to look in for Cinch attributed types that
        /// can be cached, to prevent the user from manually having to add things
        /// to lookup Dictionaries later
        /// </summary>
        public App()
        {
            CinchBootStrapper.Initialise(new List<Assembly> { typeof(App).Assembly });
        }
        #endregion
    }
}

 

And then I have a ViewModel that looks like this

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MEFedMVVM.ViewModelLocator;
using System.ComponentModel.Composition;
using System.ComponentModel;
using Cinch;
using MEFedMVVM.Common;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;

namespace Test
{
    [ExportViewModel("MainWindowViewModel")]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class MainWindowViewModel : Cinch.ViewModelBase
    {

        private bool _isBusy = false; 
        private IMessageBoxService messageBoxService;


        [ImportingConstructor]
        public MainWindowViewModel(
            IMessageBoxService messageBoxService)
        {

            this.messageBoxService = messageBoxService;
            IsBusy = true;
        }



        public static readonly PropertyChangedEventArgs isBusyEventArgs = 
            ObservableHelper.CreateArgs<MainWindowViewModel>(x => x.IsBusy);

        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                if (object.ReferenceEquals(_isBusy, value)) return;
                _isBusy = value;
                NotifyPropertyChanged(isBusyEventArgs);
                NotifyPropertyChanged(isNotBusyEventArgs);
                String s = String.Format("IsBusy : {0}, IsNotBusy : {1}", IsBusy, IsNotBusy);
                System.Diagnostics.Debug.WriteLine(s);
            }
        }

        

        public static readonly PropertyChangedEventArgs isNotBusyEventArgs =
            ObservableHelper.CreateArgs<MainWindowViewModel>(x => x.IsNotBusy);
        public bool IsNotBusy
        {
            get { return !_isBusy; }
        }
    }
}

 

And a View that looks like this

 

 

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:meffed="clr-namespace:MEFedMVVM.ViewModelLocator;assembly=MEFedMVVM.WPF"
        meffed:ViewModelLocator.ViewModel="MainWindowViewModel">
    <Grid>
        <Label Content="{Binding IsBusy}"/>
    </Grid>
</Window>

 

And when I run this, I get this printing out in VS2010 output window, from Setting IsBusy in MainWindowViewModel

 

'Test.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Users\barbers\Desktop\Downloads\CinchV2AndPrismRegions\CinchV2AndPrismRegions\Test\bin\Debug\System.Windows.Interactivity.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
MEFedMVVM Composition Container is changing.
'Test.vshost.exe' (Managed (v4.0.30319)): Loaded 'Anonymously Hosted DynamicMethods Assembly'
'Test.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Users\barbers\Desktop\Downloads\CinchV2AndPrismRegions\CinchV2AndPrismRegions\Test\bin\Debug\Microsoft.Expression.Interactions.dll'
Attaching ViewModel MainWindowViewModel
IsBusy : True, IsNotBusy : False

See that last line, it all works for me

Mar 24, 2011 at 11:45 PM

Isn't the problem that of using object.ReferenceEquals(objectA, objectB) on a bool ?   
I think the code should be:

 ...
if (_isBusy != value)
{
    _isBusy = value;
    NotifyPropertyChanged(isBusyEventArgs);
    NotifyPropertyChanged(isNotBusyEventArgs);
}
...

You don't want to know if value and _isBusy are the SAME OBJECT, you want to compare the VALUE of each. 

I always think (simplisticly) that ReferenceEquals compares the address of two objects. 

Coordinator
Mar 25, 2011 at 7:56 AM

I think DDanz is correct, Bool will be boxed into an Object when you do the ReferenceEquals, but you should not do that. You should just use isBusy != value