Heinzi
Heinzi

Reputation: 172200

Simple, generic way to get a value from a WPF DataContext in code

Let's say I have a WPF control that was bound to some DataContext. Now let's say I have some UI code that needs to get some value from the DataContext. How do I do that?


I am aware of the following workarounds:

  1. Cast the DataContext to its original type, e.g.

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

    or

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

    I don't like this, because it means that in my UI code I need information about the type of the underlying data source.

  2. Bind the required value to some UI field and extract it from there, e.g.

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

    and in code

    var myValue = (TypeOfMyValue)myButton.Tag;
    

Is there some generic way to extract a value from a DataContext, i.e., do whatever Binding does to get the value? I'm looking for something like this:

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

I'm pretty sure that something like this exists (after all, Binding works like this), I just cannot find it...

Upvotes: 0

Views: 4849

Answers (4)

Ricardo Veloz
Ricardo Veloz

Reputation: 19

//Into the View:

public Ingreso Ingreso { get; set; }

public CapturaDatos()
{
    InitializeComponent();
    Ingreso = new Ingreso();
    DataContext = Ingreso;
}

//Into the model

internal class IngresoViewModel
{
    public Ingreso IngresoDetails { get; set; } 
}

//Into de View XAML

xmlns:modelo="clr-namespace:WpfApp2.ViewModel"
<Page.DataContext>
    <modelo:IngresoViewModel/>
</Page.DataContext>
<TextBox x:Name="textBoxCurp" TextChanged="textBoxCurp_TextChanged" CharacterCasing="Upper" Text="{Binding IngresoDetails.Curp}" />

Then you can use the Ingreso DataBinding in your XAML file or in your code.

Upvotes: 0

Heinzi
Heinzi

Reputation: 172200

I found a way which works for both ViewModel classes (CLR properties) and lists bound to a ADO.NET DataTables (DataRowView fields), which is to use the TypeDescriptor offered by both:

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

Here is a short working example:

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"

Upvotes: 2

Ra&#250;l Nu&#241;o
Ra&#250;l Nu&#241;o

Reputation: 313

The answer is Reflection. This will work for non-indexed properties:

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

Upvotes: 1

Gope
Gope

Reputation: 1778

You will probably have to build this by yourself. We did this at work, but I cannot share the code because its an internal asset. Although it's not too easy, but also not too hard thinking of also respecting indexed properties f.e.. Still it should be done in one day. You use reflection to get the values but make sure to cache the generated getters. Its not more than that. Really... :)

Maybe a litte more information on this as some people believe that is not a good way: Given a complex propertypath you cycle through the path and resolve the objects one after another. Create a Resolver-Class that gets the DataContext-Instance. Create getter-delegates and cache those for propertytype and propertypath (just the segment). This gets really fast. The only things WPF does different is the Value-Getter. It analyses the property for its type. INotifyPropertyChanged-Supporters and DependendyObjects are treated differently, but it doesn't make much of a performance problem if you cache the delegates...

Upvotes: 0

Related Questions