【问题标题】:Simple, generic way to get a value from a WPF DataContext in code在代码中从 WPF DataContext 获取值的简单通用方法
【发布时间】:2014-05-27 07:56:44
【问题描述】:

假设我有一个绑定到某个 DataContext 的 WPF 控件。现在假设我有一些 UI 代码需要从 DataContext 中获取一些值。我该怎么做?


我知道以下解决方法:

  1. 将 DataContext 转换为其原始类型,例如

    var myValue = ((MyViewModel)myControl.DataContext).SomeProperty;
    

    var myValue = ((DataRowView)myControl.DataContext).Item("SomeDatabaseField");
    

    我不喜欢这样,因为这意味着在我的 UI 代码中我需要有关底层数据源类型的信息。

  2. 将所需的值绑定到某个 UI 字段并从那里提取它,例如

    <Button Click="..." Tag="{Binding SomeProperty}" />
    

    在代码中

    var myValue = (TypeOfMyValue)myButton.Tag;
    

是否有一些 通用 方法可以从 DataContext 中提取值,即执行 Binding 所做的任何事情来获取值?我正在寻找这样的东西:

var myValue = SomeGenericExtractMethod(myControl.DataContext, "SomeProperty");

我很确定存在这样的东西(毕竟Binding 是这样工作的),我就是找不到它......

【问题讨论】:

    标签: c# wpf data-binding datacontext


    【解决方案1】:

    答案是反射。这适用于非索引属性:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
    
            this.DataContext = new MyViewModel();
            var myValue = this.DataContext.GetType().GetProperty("MyIntValue").GetValue(this.DataContext, null);
        }
    }
    
    public class MyViewModel
    {
        private int myIntValue = 6;
        public int MyIntValue
        {
            get
            {
                return this.myIntValue;
            }
        }
    }
    

    【讨论】:

    • +1,非常适合 ViewModel。但不适用于绑定到 ADO.NET DataTables 的控件。
    【解决方案2】:

    我找到了一种适用于 ViewModel 类(CLR 属性)和绑定到 ADO.NET DataTables(DataRowView 字段)的列表的方法,即使用两者提供的TypeDescriptor

    var myValue = TypeDescriptor.GetProperties(myControl.DataContext)["SomeProperty"]
        .GetValue(myControl.DataContext);
    

    这是一个简短的工作示例:

    var vm = new { MyString = "Test1" };         // ViewModel
    
    var dt = new DataTable();
    dt.Columns.Add("MyString", typeof(String));
    dt.Rows.Add("Test2");
    var drv = dt.DefaultView[0];                 // DataRowView
    
    var value1 = TypeDescriptor.GetProperties(vm)["MyString"].GetValue(vm);
    var value2 = TypeDescriptor.GetProperties(drv)["MyString"].GetValue(drv);
    
    // value1 = "Test1", value2 = "Test2"
    

    【讨论】:

    • 那么我的最后一篇文章就是这个。如果需要看性能的话,有一个关于 Typedescriptor Caching 的 CodeProject Entry:codeproject.com/Articles/18450/…
    【解决方案3】:

    您可能必须自己构建它。我们在工作中这样做了,但我不能分享代码,因为它是内部资产。虽然这不太容易,但也不太难考虑也尊重索引属性 f.e.. 仍然应该在一天之内完成。您使用反射来获取值,但请确保缓存生成的 getter。它不止于此。真的……:)

    也许有更多关于此的信息,因为有些人认为这不是一个好方法: 给定一个复杂的属性路径,您可以循环遍历路径并一个接一个地解析对象。创建一个获取 DataContext-Instance 的解析器类。创建 getter-delegates 并为 propertytype 和 propertypath 缓存那些(只是段)。这变得非常快。 WPF 唯一不同的是 Value-Getter。它分析属性的类型。 INotifyPropertyChanged-Supporters 和 DependendyObjects 的处理方式不同,但如果您缓存委托,它不会造成太大的性能问题...

    【讨论】:

    • 请您不要只投反对票而是解释一下。我们通过可靠的实施做到了这一点。绑定的工作方式相同。主要是通过反思......
    • 这是一个糟糕的解释,因为不清楚所描述的内容。请添加最少的代码示例来说明您提出的解决方案。
    • 其实我只是想说一个人可以为自己的项目构建它。我不能分享示例代码,因为它会破坏我的披露协议。我们将其用于拥有 4k 用户的 WPF 应用程序框架。解析机制用作控件的附加属性,然后在后台进行配置。一个 DataGrid f.e.获取 SelectedItem、ItemsSource 绑定、SingleClick 编辑等。您不能只发布几行。本质上就像我说的:你想要这个值:通过循环中的反射来解决它,但是缓存 PropertyInfos。只想说能做好!
    • 我将尝试构建一个准系统示例,仅显示缓存,而不考虑索引属性等特殊情况。这将需要几个小时...
    • +1,因为我非常感谢您的努力(尽管如果框架中没有内置解决方案,我可能会继续使用其中一种解决方法)。
    猜你喜欢
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 2015-01-18
    • 2020-01-18
    • 2019-12-02
    相关资源
    最近更新 更多