Привязка динамического объекта в MAUI

В настоящее время я работаю над проектом MAUI и пытаюсь создать динамический объект и привязать его к метке (конечная цель - не метка, но ее легче протестировать на метке). Но ничего не отображается. Я пробовал с .net7 и .net 8

Мой динамический объект выглядит так:

      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));
        }
    }
}

И мой лейбл выглядит так

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

Я попробовал тот же код с обычным классом, и он работает.

У меня есть ошибка ссылки XAML:

Свойство name не найдено в TestNet8.DynamicGridPage+DynamicObjectTest, целевое свойство: Microsoft.Maui.Controls.Label.Text.

Функция TryGetMember никогда не срабатывает. У вас есть идеи?

2 ответа

Вот возможное решение.

Насколько я понимаю, вы пытаетесь привязать TextProperty метки к динамическому свойству (имени) экземпляра DynamicObject. Однако механизм привязки не может найти свойство name, что приводит к ошибке.

Решение может быть:

  1. Переопределить TryGetMember и TrySetMember альтернативным способом: класс DynamicObject предоставляет способ переопределить доступ к членам и их назначение. Вы уже переопределили эти методы, но необходимо внести некоторые улучшения.
  2. Используйте правильный регистр для имен свойств. Механизм привязки чувствителен к регистру. Вы преобразуете имена свойств в нижний регистр в своем динамическом объекте, что может вызвать проблемы. Лучше оставить оригинальный чехол.

Вот обновленная версия вашего класса DynamicObjectTest и способы ее использования (не проверялось) :

      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);

Внесены изменения:

  1. Удалено преобразование нижнего регистра. В исходном коде вы конвертировали имена свойств в нижний регистр с помощью метода связывания.Name.ToLower(). Это может привести к несоответствиям при привязке, поскольку механизм привязки чувствителен к регистру. Я удалил преобразование .ToLower(), чтобы имена свойств оставались в исходном регистре.
  2. Упрощенный TryGetMember: в исходном методе вы сначала вызывали базовый TryGetMember, затем проверяли, был ли результат ложным, прежде чем пытаться получить значение свойства из словаря. Я упростил это, попытавшись напрямую получить значение свойства из словаря, не вызывая базовый метод.
  3. Упрощенный TrySetMember: как и в случае с TryGetMember, вы сначала вызываете базовый TrySetMember. Я упростил это, напрямую задав значение свойства в словаре, не вызывая базовый метод.

Надеюсь, поможет.

Первый

Функция TryGetMember никогда не срабатывает. У вас есть идеи?

Я проверил предоставленный вами код. Если вы закодируете это:Console.WriteLine(dynamicObject.name);. Он может поразить функцию TryGetMember. Однако, когда я меняю это на это:

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

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

Он все еще получает эту ошибку:'name' property not found on ...

Второй

Вы можете обратиться к этой проблеме: Привязки к DynamicObject . И комментарий в этом выпуске может решить проблему. Вот эффект:

«l» — имя метки.

Другие вопросы по тегам