Привязка динамического объекта в 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, что приводит к ошибке.
Решение может быть:
- Переопределить TryGetMember и TrySetMember альтернативным способом: класс DynamicObject предоставляет способ переопределить доступ к членам и их назначение. Вы уже переопределили эти методы, но необходимо внести некоторые улучшения.
- Используйте правильный регистр для имен свойств. Механизм привязки чувствителен к регистру. Вы преобразуете имена свойств в нижний регистр в своем динамическом объекте, что может вызвать проблемы. Лучше оставить оригинальный чехол.
Вот обновленная версия вашего класса 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);
Внесены изменения:
- Удалено преобразование нижнего регистра. В исходном коде вы конвертировали имена свойств в нижний регистр с помощью метода связывания.Name.ToLower(). Это может привести к несоответствиям при привязке, поскольку механизм привязки чувствителен к регистру. Я удалил преобразование .ToLower(), чтобы имена свойств оставались в исходном регистре.
- Упрощенный TryGetMember: в исходном методе вы сначала вызывали базовый TryGetMember, затем проверяли, был ли результат ложным, прежде чем пытаться получить значение свойства из словаря. Я упростил это, попытавшись напрямую получить значение свойства из словаря, не вызывая базовый метод.
- Упрощенный 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» — имя метки.