0

I'm currently working on a MAUI Project and i'm trying to create a dynamicObject and bind it to a label ( final goal is not a label but it's easier to test on a label) But nothing is displayed I tried with .net7 and .net 8

My dynamic object look like this :

public class DynamicObjectTest : DynamicObject, INotifyPropertyChanged 

{

    public Dictionary<string, object> _dictionary = new Dictionary<string, object>();
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public void SetPropertyValue(string propertyName, object value)
    {
        if (_dictionary.ContainsKey(propertyName)) {
            _dictionary[propertyName] = value;
        }
        else { 
            _dictionary.Add(propertyName, value);
        }
        OnPropertyChanged(propertyName);
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _dictionary.Keys.ToArray();
    }

    public object GetPropertyValue(string propertyName)
    {
        return _dictionary.ContainsKey(propertyName) ? _dictionary[propertyName] : null;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        bool ret = base.TryGetMember(binder, out result);
 
        if (ret == false)
        {
            result = GetPropertyValue(binder.Name.ToLower());
            if (result != null)
            {
                ret = true;
            }
        }
        return ret;
    }

 

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        bool ret = base.TrySetMember(binder, value);

        if (ret == false)
        {
           SetPropertyValue(binder.Name.ToLower(), value);
            ret = true;
        }
        return ret;
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And my label looks like that

dynamic dynamicObject = new DynamicObjectTest();
dynamicObject.name = "test dynamic";
Label dynamicLabel = new Label();
dynamicLabel.BindingContext = dynamicObject;
dynamicLabel.SetBinding(Label.TextProperty, "name");
MainStack.Add(dynamicLabel);

I tried the same code with a regular class and it's working

I have this XAML link error :

'name' property not found on 'TestNet8.DynamicGridPage+DynamicObjectTest', target property: 'Microsoft.Maui.Controls.Label.Text'

The TryGetMember function is never trigged Do you have an idea ?

Nesta
  • 3
  • 3

2 Answers2

1

Here is a possble solution.

To my understanding you are trying to bind a Label's TextProperty to a dynamic property (name) of a DynamicObject instance. However, the binding engine is unable to find the name property, resulting in the error.

Solution might be:

  1. Override TryGetMember and TrySetMember in a alternative way: The DynamicObject class provides a way to override member access and member assignment. You've already overridden these methods, but there are some improvements to be made.
  2. Use Proper Case for Property Names: The binding engine is case-sensitive. You're converting property names to lowercase in your dynamic object, which can cause issues. It's better to keep the original case.

Here's a updated version of your DynamicObjectTest class and how to use it (Not tested):

public class DynamicObjectTest : DynamicObject, INotifyPropertyChanged 
{
    private Dictionary<string, object> _dictionary = new Dictionary<string, object>();
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public void SetPropertyValue(string propertyName, object value)
    {
        if (_dictionary.ContainsKey(propertyName)) 
        {
            _dictionary[propertyName] = value;
        }
        else 
        { 
            _dictionary.Add(propertyName, value);
        }
        OnPropertyChanged(propertyName);
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _dictionary.Keys;
    }

    public object GetPropertyValue(string propertyName)
    {
        _dictionary.TryGetValue(propertyName, out var value);
        return value;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = GetPropertyValue(binder.Name);
        return result != null;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        SetPropertyValue(binder.Name, value);
        return true;
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// Usage:
dynamic dynamicObject = new DynamicObjectTest();
dynamicObject.name = "test dynamic"; // Note: 'name' is case-sensitive

Label dynamicLabel = new Label();
dynamicLabel.BindingContext = dynamicObject;
dynamicLabel.SetBinding(Label.TextProperty, "name"); // Bind to 'name', not 'Name'
MainStack.Add(dynamicLabel);

Changes made:

  1. Removed Lowercase Conversion: In your original code, you were converting property names to lowercase using binder.Name.ToLower(). This can cause mismatches when binding, as the binding engine is case-sensitive. I removed the .ToLower() conversion to ensure that the property names remain in their original case.
  2. Simplified TryGetMember: In your original method, you were calling the base TryGetMember first, then checking if the result was false before attempting to get the property value from the dictionary. I simplified this by directly attempting to get the property value from the dictionary without calling the base method.
  3. Simplified TrySetMember: Similar to TryGetMember, you were calling the base TrySetMember first. I simplified this by directly setting the property value in the dictionary without calling the base method.

Hope it helps.

AllramEst
  • 1,319
  • 4
  • 23
  • 47
  • Unfortunately, it doesn't seem to work The TryGetMember function is never called Thanks for the suggestion – Nesta Jul 25 '23 at 12:09
0

First

The TryGetMember function is never trigged Do you have an idea ?

I tested the code you provided. If you code this: Console.WriteLine(dynamicObject.name);. It can hit the TryGetMember function. However, when I change it to this:

dynamicLabel.SetBinding(Label.TextProperty, "name");

=>> dynamicLabel.SetBinding(Label.TextProperty, nameof(dynamicObject.name));

It still get this error: 'name' property not found on ...

Second

You can refer to this issue: Bindings to a DynamicObject. And the comment in this issue can fix problem. Here is the Effect:

enter image description here

"l" is Name of label.

Jianwei Sun - MSFT
  • 2,289
  • 1
  • 3
  • 7