繁体   English   中英

Xamarin.Forms访问绑定对象数据

[英]Xamarin.Forms access Binding object data

我想制作一个标签,以提取绑定项目的名称或其他数据。

[Display(Description = "Gimme your goddamm first name will ya")]
public string FirstName { get; set; }

码:

public class TitleLabel : ContentView
{
  public Label Label { get; } = new Label();
  public TitleLabel()
  {
    //TODO ensure Content is not accessed manually
    Content = Label;
  }
  protected override void OnBindingContextChanged() =>
    Label.Text = GetPropertyTitle();


  string GetPropertyTitle()
  {
    var bcp = BindingContextProperty;

    //pseudo:
    var binding = GetBinding(bcp);
    var obj = binding.Object;
    var propertyName = binding.Path;
    var propertyInfo = obj.GetType().GetTypeInfo().DeclaredMembers
      .SingleOrDefault(m => m.Name == propertyName);
    if (propertyInfo == null)
      throw new InvalidOperationException();

    return propertyInfo.GetCustomAttribute<DisplayAttribute>().Description;
  }
}

XAML:

<my:TitleLabel Text="{Binding FirstName}" />

渲染结果:

<my:TitleLabel Text="Gimme your goddamm first name will ya" />

最好的选择是定义一个值转换器。

namespace SampleFormsApp {
public class DisplayNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
      object parameter, CultureInfo culture)
    {
        if (value == null || targetType != typeof(string))
            return null;

        var propertyName = parameter as string;
        if (propertyName == null)
            return null;

        var propertyInfo = value.GetType().GetTypeInfo().DeclaredMembers
          .SingleOrDefault(m => m.Name == propertyName);
        if (propertyInfo == null)
            return null;

        return propertyInfo.GetCustomAttribute<DisplayAttribute>().Name;
    }

    public object ConvertBack(object value, Type targetType,
      object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后在App.xaml的全局ResourceDictionary中声明它:

<ResourceDictionary>
    <local:DisplayNameConverter x:Key="DisplayNameConverter"/>
</ResourceDictionary>

确保声明名称空间:

xmlns:local="clr-namespace:SampleFormsApp"

然后,当您要使用它时,请绑定到包含该属性的对象,并将属性名称作为参数传递:

<Label Text="{Binding ., 
  Converter={StaticResource DisplayNameConverter}, ConverterParameter=FirstName}"/>

如果在Convert方法中引发异常(如上例所示),它将使应用程序崩溃。 在页面渲染期间,它可能会使用空值调用转换器,因此至少必须对此具有弹性。

陷阱( 要点 ):

public class DisplayExtension : IMarkupExtension<string>
{
  public object Target { get; set; }
  BindableProperty _Property;

  public string ProvideValue(IServiceProvider serviceProvider)
  {
    if (Target == null
      || !(Target is Enum
        || Target is Type
        || (Target is Binding binding && !string.IsNullOrWhiteSpace(binding.Path))))
      throw new InvalidOperationException($"'{nameof(Target)}' must be properly set.");

    var p =(IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

    if (!(p.TargetObject is BindableObject bo
      && p.TargetProperty is BindableProperty bp
      && bp.ReturnType.GetTypeInfo().IsAssignableFrom(typeof(string).GetTypeInfo())))
      throw new InvalidOperationException(
        $"'{nameof(DisplayExtension)}' cannot only be applied"
          + "to bindable string properties.");

    _Property = bp;

    bo.BindingContextChanged += DisplayExtension_BindingContextChanged;
    return null;
  }

  void DisplayExtension_BindingContextChanged(object sender, EventArgs e)
  {
    var bo = (BindableObject)sender;
    bo.BindingContextChanged -= DisplayExtension_BindingContextChanged;

    string display = null;
    if (Target is Binding binding)
      display = ExtractMember(bo, (Binding)Target);
    else if (Target is Type type)
      display = ExtractDescription(type.GetTypeInfo());
    else if (Target is Enum en)
    {
      var enumType = en.GetType();
      if (!Enum.IsDefined(enumType, en))
        throw new InvalidOperationException(
          $"The value '{en}' is not defined in '{enumType}'.");
      display = ExtractDescription(
        enumType.GetTypeInfo().GetDeclaredField(en.ToString()));
    }
    bo.SetValue(_Property, display);
  }

  string ExtractMember(BindableObject target, Binding binding)
  {
    var container = target.BindingContext;
    var properties = binding.Path.Split('.');

    var i = 0;
    do
    {
      var property = properties[i++];
      var type = container.GetType();
      var info = type.GetRuntimeProperty(property);

      if (properties.Length > i)
        container = info.GetValue(container);
      else
      {
        return ExtractDescription(info);
      }
    } while (true);
  }

  string ExtractDescription(MemberInfo info)
  {
    var display = info.GetCustomAttribute<DisplayAttribute>(true);
    if (display != null)
      return display.Name ?? display.Description;

    var description = info.GetCustomAttribute<DescriptionAttribute>(true);
    if (description != null)
      return description.Description;

    return info.Name;
  }

  object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) =>
    ProvideValue(serviceProvider);
}

用法:

<Label Text="{my:Display Target={Binding FirstName}}"/>
<Label Text="{my:Display Target={Binding User.Person.Address.City.Country}}"/>
<Label Text="{my:Display Target={Type models:Person}}"/>
<Label Text="{my:Display Target={Static models:Gender.Male}}"/>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM