是否存在与nameof相当的XAML?

时间:2017-08-18 14:59:09

标签: c# wpf xaml devexpress-wpf

我正在使用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中执行哪些操作等同于调用名称,而不是在视图模型中绑定到此ParentIdFieldName和NodeIdFieldName?

我意识到这可以被描述为DevExpress控制的一个问题。但是我对是否可以改进我用来解决这个问题的方法感兴趣。有没有办法可以直接在XAML中以更简单的方式做到这一点?

如果我提供的代码没有编译,我会提前道歉。我已经大大减少了我所做的工作,以提供一个例子。

2 个答案:

答案 0 :(得分:12)

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

例如:

[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;
    }
}

样本使用:

enter image description here

答案 1 :(得分:0)

出于某种原因,尽管我的设计师在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个构造函数,一个构造函数绑定,另一个构造一个Member字符串。