有没有XAML中类似于nameof的等效方法?

24

我正在使用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中完成呢?

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

2个回答

28
你可以创建自定义的标记扩展。
例如:
[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;
    }
}

示例用法:

在此输入图片描述


1
Resharper抱怨成员未解决符号,但将其作为构造函数参数传递可以解决该问题。 - Shahin Dohan
这个扩展能否在绑定中使用,例如作为字典键?语法会是什么样子? - wondra
有人找到了一种使用这个来指定x:Key的方法吗?我得到了以下错误:字典的键不能是'MyNamespace.NameOfExtension'类型。只支持String、TypeExtension和StaticExtension。 - DMX David Cardinal
在绑定中使用时,请勿使用 Path=,例如:{Binding {local:NameOf Type={x:Type local:MainWindow}, Member=Title}} - Maxence

11

由于某些原因,我的设计师与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}}" />

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


谢谢这个版本,看起来更加干净,而且没有 XAML 错误显然是一件好事。 - wonea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接