我想制作一个标签,用于提取绑定项目的名称或其他数据。
[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" />
答案 0 :(得分:0)
最好的选择是定义一个值转换器。
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方法中抛出异常(如上例所示),它将使应用程序崩溃。在页面渲染期间,它可能会使用空值调用转换器,因此它必须至少具有弹性。
答案 1 :(得分:0)
Gotcha(Gist):
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}}"/>