ExpandoBase - dynamic object for MVVM Model class

Public forum to share code snippets, screen shorts, experiences, etc.
Post Reply
User avatar
wriedmann
Posts: 3644
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

ExpandoBase - dynamic object for MVVM Model class

Post by wriedmann »

Searching for a class that could support dynamic members, I found the System.Dynamic.ExpandoObject - but unfortunately this object is sealed, so I could not inherit from this, but I could build a similar object inheriting from System.Dynamic.DynamicObject.

The use is very simple:

Code: Select all

class MyModel inherit ExpandoBase

constructor()
   self:SetValue( "Caption", "window caption" )
   return
and in the view the property "Caption" is available.

And this is the class:

Code: Select all


using System
using System.Collections.Generic
using System.Text
using System.Threading.Tasks
using System.ComponentModel
using System.Diagnostics
using System.Runtime.CompilerServices
using System.Dynamic

begin namespace rdm.MVVMHelper

/// <summary>
/// Baseclass for a dynamic object, implementing the INotifyPropertyChanged interface. 
/// Here'a sample for using the "normal" properties:
///    public string FirstName
///    {
///        get { return _Get<string>(); }
///        set { _Set<string>(value); }
///    }
/// </summary>
public abstract class ExpandoBase inherit DynamicObject implements INotifyPropertyChanged
	protect _oProperties 		:= Dictionary<string, object>{} as Dictionary<string, object> 
    public event PropertyChanged as PropertyChangedEventHandler

/// <summary>
/// When a new property is set, 
/// add the property name and value to the dictionary
/// </summary>     
public virtual method TrySetMember( oBinder as SetMemberBinder, value as object ) as logic 
    
    if ! _oProperties.ContainsKey( oBinder.Name )
        _oProperties.Add( oBinder.Name, value )
    else
        _oProperties[oBinder.Name] 	:= value
    endif

    self:OnPropertyChanged( oBinder.Name )

    return true

/// <summary>
/// When user accesses something, return the value if we have it
/// </summary>      
public virtual method TryGetMember( oBinder as GetMemberBinder, result out object ) as logic
    local lReturnValue 		as logic

    if _oProperties.ContainsKey( oBinder:Name )
        result 			:= _oProperties[oBinder:Name]
        lReturnValue 	:= true
    else
        lReturnValue 	:= super:TryGetMember( oBinder, out result )
    endif

    return lReturnValue

/// <summary>
/// If a property value is a delegate, invoke it
/// </summary>     
public virtual method TryInvokeMember( oBinder as InvokeMemberBinder, args as object[], result out object ) as logic
    local lReturnValue 		as logic

    if _oProperties.ContainsKey( oBinder:Name ) .and. _oProperties[oBinder:Name] is System.Delegate
        result 				:= ( ( System.delegate ) _oProperties[oBinder:Name] ):DynamicInvoke( args )
        lReturnValue 		:= true
    else
        lReturnValue 		:= super:TryInvokeMember( oBinder, args, out result )
    endif

    return lReturnValue

/// <summary>
/// Gets the value of a dynamic property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
public method GetValue( cName as string ) as object
	local oValue as object
	
    Debug.Assert( cName != null, "cName == null")
    oValue 			:= null
    _oProperties.TryGetValue( cName, out oValue )
    
    return oValue

/// <summary>
/// Sets the value of a dynamic property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
/// <remarks>Use this overload when implicitly naming the property</remarks>
public method SetValue( cName as string, oValue as object ) as void

    Debug.Assert( cName != null, "cName == null")
    if Equals( oValue, GetValue( cName ) )
        return
    endif       
    _oProperties[cName] 	:= oValue
    OnPropertyChanged( cName )
    
    return

/// <summary>
/// Gets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
protected method _Get<T>( [CallerMemberName] cName := null as string ) as T 
	local value 	:= null as object

    Debug.Assert( cName != null, "cName != null" )
    if _oProperties.TryGetValue( cName, out value )
    	if value == null
    		return Default(T)
    	else
    		return (T) value
    	endif
        // return value == null ? Default(T) : (T)value
    endif
    return Default(T)

/// <summary>
/// Sets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
/// <remarks>Use this overload when implicitly naming the property</remarks>
protected method _Set<T>( value as T, [CallerMemberName] cName := null as string ) as void 

    Debug.Print( "passed Name is " + cName + ", passed value is " + value.ToString() )

    Debug.Assert( cName != null, "cName != null" )
    if ( Equals( value, _Get<T>( cName) ) )
        return    
    endif
    _oProperties[cName] := value
    OnPropertyChanged( cName )
    
    return

/// <summary>
/// Return all dynamic member names
/// </summary>
/// <returns>
public virtual method GetDynamicMemberNames() as IEnumerable<string>

    return _oProperties.Keys
    
protected virtual method OnPropertyChanged([CallerMemberName] cPropertyName := null as string ) as void
	local handler as PropertyChangedEventHandler
	
    handler := self:PropertyChanged
    if handler != null
        handler( self, PropertyChangedEventArgs{ cPropertyName } )
    endif
    
    return

end class

end namespace

Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Post Reply