是否有一个 XAML 等同于 nameof?

Is there a XAML equivalent to nameof?

我正在使用 DevExpress 的 WPF 树列表视图,我遇到了一个我认为更普遍的问题,该问题与重命名用作项目源的对象的属性有关。在树列表视图中,需要指定 ParentFieldName 和 KeyFieldName(用于确定树的结构)。这些字段是字符串。

这导致了重构代码的问题。例如,将我用作 ItemSource 的对象的 属性 重命名将破坏树视图,因为 ParentFieldName 和 KeyFieldName 不再与 属性 名称同步。我通过在我的视图模型 "ParentFieldName" 和 "KeyFieldName" 中创建属性来解决这个问题,它们使用 nameof 将 属性 名称呈现给视图。

这是该控件的简化版本:

    <UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"              
             d:DesignHeight="300" d:DesignWidth="300">
      <UserControl.DataContext>
          <ViewModel />
      </UserControl.DataContext>
        <dxg:TreeListControl AutoGenerateColumns="AddNew"
                         EnableSmartColumnsGeneration="True" ItemsSource="{Binding Results}"                        
                         SelectionMode="Row">
            <dxg:TreeListControl.View>
                <dxg:TreeListView                                   
                              ParentFieldName="{Binding ParentIdFieldName}" KeyFieldName="{Binding NodeIdFieldName}" 
                              ShowHorizontalLines="False" ShowVerticalLines="False"
                              ShowNodeImages="True"/>
            </dxg:TreeListControl.View>
        </dxg:TreeListControl>
    </UserControl>

和视图模型:

using DevExpress.Mvvm;    

public sealed class ViewModel : ViewModelBase
{
    public string ParentIdFieldName => nameof(TreeNode.ParentId);

    public string NodeIdFieldName => nameof(TreeNode.NodeId);

    public ObservableCollection<TreeNode> Results
    {
        get => GetProperty(() => Results);
        set => SetProperty(() => Results, value);
    } 
}

和树节点:

public sealed class TreeNode
{
    public int ParentId {get; set;}
    public int NodeId {get; set;}
}

我的解决方案运行良好,但我想知道是否有更好的方法。例如,我可以在 XAML 中做一些相当于 nameof 调用的事情,而不是绑定到视图模型中的这个 ParentIdFieldName 和 NodeIdFieldName 吗?

我意识到这可以描述为 DevExpress 控件的问题。但是,我感兴趣的是我用来解决这个问题的方法是否可以改进。有没有一种方法可以直接在 XAML 中以更简单的方式执行此操作?

如果我提供的代码无法编译,我提前道歉。我已经大大减少了我正在使用的内容以提供一个示例。

您可以创建自定义标记扩展。

例如:

[ContentProperty(nameof(Member))]
public class NameOfExtension : MarkupExtension
{
    public Type Type { get; set; }
    public string Member { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (serviceProvider == null)
            throw new ArgumentNullException(nameof(serviceProvider));

        if (Type == null || string.IsNullOrEmpty(Member) || Member.Contains("."))
            throw new ArgumentException("Syntax for x:NameOf is Type={x:Type [className]} Member=[propertyName]");

        var pinfo = Type.GetRuntimeProperties().FirstOrDefault(pi => pi.Name == Member);
        var finfo = Type.GetRuntimeFields().FirstOrDefault(fi => fi.Name == Member);
        if (pinfo == null && finfo == null)
            throw new ArgumentException($"No property or field found for {Member} in {Type}");

        return Member;
    }
}

示例用法:

出于某种原因,我的设计人员无法很好地使用 G.Sharada 的解决方案,尽管它在运行时运行良好。

我走的路线略有不同:

public class NameOfExtension : MarkupExtension
{
    private readonly PropertyPath _propertyPath;

    public NameOfExtension(Binding binding)
    {
        _propertyPath = binding.Path;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var indexOfLastVariableName = _propertyPath.Path.LastIndexOf('.');
        return _propertyPath.Path.Substring(indexOfLastVariableName + 1);
    }
}

这样我可以做到:

<TextBlock Text="{local:NameOf {Binding Property1.Property2}}" />

这显然不是替代品,因为我们可能没有要绑定的实例化对象。我想我可以有 2 个构造函数,一个接受绑定,另一个接受成员字符串。