Search code examples
c#dynamic-language-runtimedynamicobject

Why is TryGetMember not invoked on my DynamicObject?


I have the following class for binding to dynamically generated columns on a WPF DataGrid. The column binding has to be a property name, and I can add appropriately named properties to a DynamicObject:

public class BindableDynamicDictionary : DynamicObject
{
    private readonly Dictionary<string, object> _dictionary;

    public BindableDynamicDictionary()
    {
        _dictionary = new Dictionary<string, object>();
    }

    public object this[string key]
    {
        get { return _dictionary[key]; }
        set { _dictionary[key] = value; }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return _dictionary.TryGetValue(binder.Name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        _dictionary[binder.Name] = value;
        return true;
    }
}

Just FYI, each day property stores an AppointmentRowViewModel.

When I've populated an instance of the above with one dynamic property for each day of a week, all the dynamic properties are listed, and the dictionary access works fine, but property name access throws an exception. E.g (from my Immediate Window):

apptDynamic.GetDynamicMemberNames()
Count = 9
    [0]: "20161205"
    [1]: "Monday"
    [2]: "20161206"
    [3]: "Tuesday"
    [4]: "20161208"
    [5]: "Thursday"
    [6]: "20161211"
    [7]: "Sunday"
    [8]: "20161215"
apptDynamic.Monday
error CS1061: 'AppointmentCalendarViewModel.TimeAppointmentDictionary' does not contain a definition for 'Monday' and no extension method 'Monday' accepting a first argument of type 'AppointmentCalendarViewModel.TimeAppointmentDictionary' could be found (are you missing a using directive or an assembly reference?)
apptDynamic["Monday"]
{ApptEase.Client.Prism.CSharp.ViewModels.AppointmentRowViewModel}

Is this perhaps because I only set the properties using dictionary access? I don't know how else to without property name literals, so I do it like this:

BindableDynamicDictionary apptDynamic = Appointments[start];
DateTime loopDate = ActiveDate.FirstDayOfWeek(WeekStartDay);
for (var i = 0; i < DaysVisible; i++)
{
    loopDate = loopDate.AddDays(i);
    if (appt.StartDateTime.Date == loopDate.Date)
    {
        apptDynamic[loopDate.ToString("yyyyMMdd")] = appt;
        apptDynamic[loopDate.DayOfWeek.ToString()] = appt;
        continue;
    }
    apptDynamic[loopDate.ToString("yyyyMMdd")] = null;
    apptDynamic[loopDate.DayOfWeek.ToString()] = null;
}

Where ActiveDate.FirstDayOfWeek(WeekStartDay) uses a DateTime extension method to get the date of the first day of the week that ActiveDate occurs in, depending on which the the week starts on.

I thought part of the main idea of DynamicObject was to be able to access a dictionary item using it's key as if it were a property name. Why is this not working for me?


Solution

  • I can't add comments due to my low rep so I will leave an answer.

    from your code sample provided I can only see the way you are storing the dynamic object as

    BindableDynamicDictionary apptDynamic = Appointments[start];
    

    for the dynamic property access to work you need to use the dynamic keyword

    dynamic apptDynamic = Appointments[start];
    

    reference links

    https://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.trygetmember(v=vs.110).aspx

    Differences between ExpandoObject, DynamicObject and dynamic