Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does WPF support binding to properties of an object, but not fields?

I've got a WCF service that passes around status updates via a struct like so:

[DataContract] public struct StatusInfo {     [DataMember] public int Total;     [DataMember] public string Authority; } ... public StatusInfo GetStatus() { ... } 

I expose a property in a ViewModel like this:

public class ServiceViewModel : ViewModel {     public StatusInfo CurrentStatus     {         get{ return _currentStatus; }         set         {              _currentStatus = value;             OnPropertyChanged( () => CurrentStatus );         }     }     } 

And XAML like so:

<TextBox Text="{Binding CurrentStatus.Total}" /> 

When I run the app I see errors in the output window indicating that the Total property cannot be found. I checked and double checked and I typed it correctly. The it occurred to me that the errors specifically indicate that the 'property' cannot be found. So adding a property to the struct made it work just fine. But this seems odd to me that WPF can't handle one-way binding to fields. Syntactically you access them the same in code and it seem silly to have to create a custom view model just for the StatusInfo struct. Have I missed something about WPF binding? Can you bind to a field or is property binding the only way?

like image 448
Paul Alexander Avatar asked May 09 '09 03:05

Paul Alexander


People also ask

How does binding work in WPF?

Data binding is a mechanism in WPF applications that provides a simple and easy way for Windows Runtime apps to display and interact with data. In this mechanism, the management of data is entirely separated from the way data. Data binding allows the flow of data between UI elements and data object on user interface.

How does XAML binding work?

Data binding is a mechanism in XAML applications that provides a simple and easy way for Windows Runtime Apps using partial classes to display and interact with data. The management of data is entirely separated from the way the data is displayed in this mechanism.

What is binding path in WPF?

WPF data binding mechanism allows you to bind a dependency property (Target Property) to another property (Source Property).


1 Answers

Binding generally doesn't work to fields. Most binding is based, in part, on the ComponentModel PropertyDescriptor model, which (by default) works on properties. This enables notifications, validation, etc (none of which works with fields).

For more reasons than I can go into, public fields are a bad idea. They should be properties, fact. Likewise, mutable structs are a very bad idea. Not least, it protects against unexpected data loss (commonly associated with mutable structs). This should be a class:

[DataContract] public class StatusInfo {     [DataMember] public int Total {get;set;}     [DataMember] public string Authority {get;set;} } 

It will now behave as you think it should. If you want it to be an immutable struct, that would be OK (but data-binding would be one-way only, of course):

[DataContract] public struct StatusInfo {     [DataMember] public int Total {get;private set;}     [DataMember] public string Authority {get;private set;}      public StatusInfo(int total, string authority) : this() {         Total = total;         Authority = authority;     } } 

However, I would first question why this is a struct in the first place. It is very rare to write a struct in .NET languages. Keep in mind that the WCF "mex" proxy layer will create it as a class at the consumer anyway (unless you use assembly sharing).


In answer to the "why use structs" reply ("unknown (google)"):

If that is a reply to my question, it is wrong in many ways. First, value types as variables are commonly allocated (first) on the stack. If they are pushed onto the heap (for example in an array/list) there isn't much difference in overhead from a class - a small bit of object header plus a reference. Structs should always be small. Something with multiple fields will be over-sized, and will either murder your stack or just cause slowness due to the blitting. Additionally, structs should be immutable - unlesss you really know what you are doing.

Pretty much anything that represents an object should be immuatable.

If you are hitting a database, the speed of struct vs class is a non-issue compared to going out-of-process and probably over the network. Even if it is a bit slower, that means nothing compared to the point of getting it right - i.e. treating objects as objects.

As some metrics over 1M objects:

struct/field: 50ms class/property: 229ms 

based on the following (the speed difference is in object allocation, not field vs property). So about 5x slower, but still very, very quick. Since this is not going to be your bottleneck, don't prematurely optimise this!

using System; using System.Collections.Generic; using System.Diagnostics; struct MyStruct {     public int Id;     public string Name;     public DateTime DateOfBirth;     public string Comment; } class MyClass {     public int Id { get; set; }     public string Name { get; set; }     public DateTime DateOfBirth { get; set; }     public string Comment { get; set; } } static class Program {     static void Main()     {         DateTime dob = DateTime.Today;         const int SIZE = 1000000;         Stopwatch watch = Stopwatch.StartNew();         List<MyStruct> s = new List<MyStruct>(SIZE);         for (int i = 0; i < SIZE; i++)         {             s.Add(new MyStruct { Comment = "abc", DateOfBirth = dob,                      Id = 123, Name = "def" });         }         watch.Stop();         Console.WriteLine("struct/field: "                   + watch.ElapsedMilliseconds + "ms");          watch = Stopwatch.StartNew();         List<MyClass> c = new List<MyClass>(SIZE);         for (int i = 0; i < SIZE; i++)         {             c.Add(new MyClass { Comment = "abc", DateOfBirth = dob,                      Id = 123, Name = "def" });         }         watch.Stop();         Console.WriteLine("class/property: "                    + watch.ElapsedMilliseconds + "ms");         Console.ReadLine();     } } 
like image 78
Marc Gravell Avatar answered Oct 04 '22 07:10

Marc Gravell