Reputation: 172200
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:
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.
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
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
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
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
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