IValueProvider & ViewModel

Oct 26, 2010 at 8:59 AM

Hi guys,

I don't like exposing a IValueProvider on my ViewModel, I therefore created a IValueProvider extension. Following its use:

What do you think?

//In my Constructor
ExpressionObserver.Execute(() => this._selectedSlots.Count() > 1).PushTo(() => this.ShowMultipleDetails);

//Property of my ViewModel
private bool _showMultipleDetails;

public bool ShowMultipleDetails
{
    get { return _showMultipleDetails; }

    set
    {
        if (_showMultipleDetails == value) return;

        _showMultipleDetails = value;
        RaisePropertyChanged("ShowMultipleDetails");
    }
}

//Extension
public static void PushTo<T>(this IValueProvider<T> vp, Expression<Func<T>> expression)
{
    MemberExpression body = (MemberExpression) (expression.Body);

    object target = ((ConstantExpression) (body.Expression)).Value;
    PropertyInfo property = (PropertyInfo) body.Member;

    property.SetValue(target, vp.Value, null);

    ((INotifyPropertyChanged) vp).PropertyChanged += (s, a)
                                                      =>
                                                          {
                                                              if (a.PropertyName == "Value")
                                                                  property.SetValue(target, vp.Value, null);
                                                          };
}

Best regards,
Vincent BOUZON

Coordinator
Oct 27, 2010 at 7:51 AM

Hi Vincent,

This is some interesting work. There is one important difficulty with it though and that is that you run a serious risk of memory leaks. You should always unregister event handlers when you are done with them. Especially with Obtics. It creates a meriad of event bindings form sources to your object and these bindings might keep your object alive longer than you want (forever).

Here are two patterns that I use in cases like yours. The first (X) has an automatic unregistration though you still must be weary of 'circular references'. Meaning that if you would have two objects a and b of type X and a listens to Npc events on b and b listens to Npc events on a then they will prevent each other from unregistering. The second (Y) uses a more 'classic' unregistration with IDispose. In that case though you will always have to manually call the Dispose method which can be quite awkward sometimes.

    class XBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        private event PropertyChangedEventHandler _PropertyChanged;
        public event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                if (_PropertyChanged == null)
                    OnNpcClientsRegistered();

                _PropertyChanged += value;
            }
            remove
            {
                bool hadSome = _PropertyChanged != null;

                _PropertyChanged -= value;

                if (hadSome && _PropertyChanged == null)
                    OnNpcClientsUnregistered();
            }
        }

        protected virtual void OnNpcClientsRegistered()
        {}

        protected virtual void OnNpcClientsUnregistered()
        {}

        protected virtual void OnPropertyChanged(string propName)
        {
            if (_PropertyChanged != null)
                _PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }

        #endregion
        
    }

    class X : XBase
    {
        public X()
        {
            _Prop = ExpressionObserver.Execute(() => a.b.c);
        }

        protected override void OnNpcClientsRegistered()
        {
            base.OnNpcClientsRegistered();
            ((INotifyPropertyChanged) _Prop).PropertyChanged += _PropChanged;
        }

        protected virtual void OnNpcClientsUnregistered()
        {
            base.OnNpcClientsUnregistered();
            ((INotifyPropertyChanged) _Prop).PropertyChanged -= _PropChanged;
        }

        void _PropChanged(object sender, PropertyChangedEventArgs args)
        {
            if(args.PropertyName == "Value")
                OnPropertyChanged("Prop");
        }

        private IValueProvider<int> _Prop;
        public int Prop
        { get{ return _Prop.Value; } }
    }

    class YBase : INotifyPropertyChanged, IDisposable
    {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }

        #endregion     
   
        #region IDisposable Members

        public void Dispose()
        { Dispose(true); }

        ~YBase()
        { Dispose(false); }

        protected virtual void Dispose(bool disposing)
        { }

        #endregion

    }

    class Y : YBase
    {
        public Y()
        {
            _Prop = ExpressionObserver.Execute(() => a.b.c);
            ((INotifyPropertyChanged)_Prop).PropertyChanged += _PropChanged;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                ((INotifyPropertyChanged)_Prop).PropertyChanged -= _PropChanged;
            base.Dispose(disposing);
        }

        void _PropChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == "Value")
                OnPropertyChanged("Prop");
        }

        private IValueProvider<int> _Prop;
        public int Prop
        { get { return _Prop.Value; } }    
    }

In both cases you loose some advantages that you would have if you exposed value providers directly (properties with type IValueProvider<T> and not storing the value providers in fields).

  1. Code is much simpler.
  2. Lazy initialization of the value providers. (they get created only when you need them and not on beforehand)
  3. Automatic cleanup of value providers that are not used anymore. Because you don't store them in fields. When clients release all references to the value provider the GC can dispose of them. This can be significant when you have a lot of objects.

Hope this helps,

Thomas.

Nov 2, 2010 at 3:03 PM
Edited Nov 2, 2010 at 3:21 PM

Thank you for your reply. Yes, there are also some other problems .. it is better to expose value providers directly.

Best regards,
Vincent

 

Nov 8, 2010 at 2:08 PM

Hi,

When i expose value provider directly, the binding throw an exception MethodAccessException.

I must add 

[assembly: InternalsVisibleTo("System.Windows,PublicKey=00240000048000009400000006020000002400005253413100040000010001008D56C76F9E8649383049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B37AB", AllInternalsVisible=false)]

in the file AssemblyInfo.cs.

Are you aware about this problem ?

Vincent

 

Coordinator
Nov 8, 2010 at 8:29 PM

Yes,

Silverlight is not able to discover the public Value property of the public IValueProvider interface but instead tries to use the internal property implementation. I guess this is because an object might have multiple Value properties on mutliple interfaces and it can not determine which one to use just by name. You will need the Concrete() extension method in the BindingHelper library.

Check out this thread: http://obtics.codeplex.com/Thread/View.aspx?ThreadId=65753

Regs,

Thomas.

Nov 9, 2010 at 2:22 AM

Ok thank you, for now I use InternalsVisibleTo because I use the Silverlight version provided here: 

http://obtics.codeplex.com/Thread/View.aspx?ThreadId=215764


I tried to take the latest version of the library, and I did not have the same behavior as before, and more
missing file ObservableEnumerable.Cache.cs.

Vincent