Windows Forms data binding - c#

So, my question is about the exact methodology behind windows form data binding.
I wrote a simple code, where i created a View, an IViewModel interface and a ViewModel.
interface IVM
{
}
and
public class Vm : IVM
{
int number;
public int Number
{
get
{
return this.number;
}
set
{
this.number = value;
}
}
}
the form looks like:
public partial class Form1 : Form
{
private IVM vm;
public Form1()
{
InitializeComponent();
this.vm = new Vm();
this.iVMBindingSource.DataSource = this.vm;
}
}
and the related designer part is:
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.iVMBindingSource, "Number", true));
...
this.iVMBindingSource.DataSource = typeof(WindowsFormsApplication1.IVM);
You can clearly see that IViewModel interface does not publish a Number property, but the concrete ViewModel class has a Number property.
Although in design time i can't use the designer to bind the property (since IVM has no Number prop), i can manually write "iVMBindingSource - Number" into the textbox's Test property, to bind it.
My question is, how does binding work EXACTLY? Why don't I receive a runtime error, while trying to access IVM's not existing Number property?
(I tested and it actually changes the VM's Number prop properly)
Does it use some kind of reflection? How does this "magic" binding string works?
Thanks for your answers!

Jup it's done by reflection. I just checked the code and the binding is done by the Binding class. There is a method called CheckBindings which ensures the property you want to bind on is available. It basically works like this:
if (this.control != null && this.propertyName.Length > 0)
{
// ...certain checks...
// get PropertyDescriptorCollection (all properties)
for (int index = 0; index < descriptorCollection.Count; ++index)
{
// select the descriptor for the requested property
}
// validation
// setup binding
}
As Ike mentioned, you can find the source code here:
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Binding.cs,3fb776d540d0e8ac
MSDN Reference: https://msdn.microsoft.com/en-us/library/system.windows.forms.binding(v=vs.110).aspx

As derape already mentioned, Binding uses reflection. It must use reflection because it cannot know anything about the class you are using. The evaluation will be done at runtime. Since your concrete type Vm got the specified property Number, reflection will return it and Binding class is satisfied. Binding is really flexible as long as the property name is valid.
On the other hand, when you are using the designer, it cannot know which concrete type you will use. Therefore it only allows you to use properties of the common base IVM. If you enter the string manually, design time evaluation will be skipped and input is passed to the binding constructor.
If you want to use designer support, just use the the concrete type or if you don't know the concrete type but need the property Number, simply create a new interface and derive from IMV.

Related

Code generation for struct property of user control

I create a user control like below:
public partial class TestControl : UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public State MyState
{
get { return new State(this); }
}
internal int[] _internalStates;
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct State
{
private TestControl _myControl;
public State(TestControl _) { _myControl = _; }
public int Data
{
get { return _myControl._internalStates[0]; }
set { _myControl._internalStates[0] = value; }
}
}
}
Then I can drag the control from toolbox and modify the Data value in the designer.
The problem is the designer will generate this code in InitializeComponent method:
this.testControl1.MyState.Data = 0;
But this line will throw an error:
Cannot modify the return value of 'TestControl.MyState' because it is not a variable
I understand why the statement is error, the question is how can I control the code generation to correct the error, for example to generate code like this?
var myState = this.testControl1.MyState;
myState.Data = 0;
More information
State struct is just a bridge to modify the internal property in TestControl
So I want to keep State as a struct to avoid GC overhead.
The reason for not define property in TestControl class is there are multiple states in the class, and a state will contain multiple properties, so I need to wrap the modification methods rather than define a lot of properties in the TestControl class.
Why a compile time error for Control.StructProperty.Member = Value;?
Consider the following statement
this.Control.StructProperty.Value = 0;
StructProperty is a property, so first its getter will execute and since it's a structure and is a value type, it will return a copy of the struct and setting a property for that copy is not useful/working. The knows about the situation well and instead of compiling a confusing non-working code, it generates Compiler Error CS1612:
Cannot modify the return value of 'expression' because it is not a
variable
How can I generate a working code for a Struct property?
You probably have noticed that you cannot assign this.Size.Width = 100 with the same reason. And the way that form generates the code for Size property is:
this.Size = new Size(100,100);
You also can generate code for the property the same way, by implementing a type descriptor by deriving from TypeConverter returning an InstanceDescriptor in its ConvertTo method to generate the code for your structure property using a parametric constructor which you should have for the struct.
In general, I suggest using classes rather that structures for such property.

Make updating a property in one class trigger the setter for a property of that type in another class

I am using MVVM with Galasoft MVVMLight libraries.
I have two models; each has a boolean property and different properties of the same type.
public class Model1 : ObservableObject
{
public EnumPair<YesNoInherit> Model1Property
{
get { return _model1Property; }
set
{
_model1Property = value;
Updated = true
RaisePropertyChanged("Model1Property");
}
}
public bool Updated
{
get { return _updated; }
set
{
_updated = value;
RaisePropertyChanged("Updated");
}
}
}
public class Model2 : ObservableObject
{
public EnumPair<YesNoInherit> Model2Property
{
get { return _model2Property; }
set
{
_model2Property = value;
Updated = true
RaisePropertyChanged("Model2Property");
}
}
public bool Updated
{
get { return _updated; }
set
{
_updated = value;
RaisePropertyChanged("Updated");
}
}
}
The type YesNoInherit is an enum having values No, Yes, and Inherit.
Here is the EnumPair class.
public class EnumPair<T> : ObservableObject where T : struct, IConvertible
{
public T EnumValue
{
get { return _enumValue; }
set
{
if (Type.Equals(value, _enumValue) == false)
{
_enumValue = value;
RaisePropertyChanged();
}
}
}
public string SourceName
{
get { return _sourceName; }
set
{
_sourceName = value;
RaisePropertyChanged();
}
}
}
In my view, I am trying to use a ComboBox to let the user select one of the three enum values, and, in some cases, display custom text. The resource "enumComboBoxTemplate" allows the ComboBox drop-down to show enum descriptions. The converter "inheritanceEnum2Desc" is where the custom text would be applied. "object1" is an instance of "Model1".
<ComboBox ItemTemplate=ItemTemplate="{StaticResource enumComboBoxTemplate}"
EnumSource="enums:YesNoInherit">
<ComboBox.Text>
<MultiBinding Converter="{StaticResource inheritanceEnum2Desc}">
<Binding Path="object1.EnumValue"/>
<Binding Path="object1.SourceName"/>
</MultiBinding>
</ComboBox.Text>
</ComboBox>
"Model2" would be used in future programming employing similar functionality, but with different data.
When I change the selection in the ComboBox, I want to change the value of "Updated" (from false to true) so I can enable a button in the view. This appears to require that the EnumPair class somehow make the program execute the setter for Model1Property. Since the two model classes have properties of type EnumPair, I don't believe I can add any code in EnumPair specific to either model class.
How can I accomplish this? I would greatly appreciate any assistance.
Basically, you have two options: either use some kind of message-bus to update the other model (Prism has EventAggregator, not sure about MVVMLight) or make both model instances forward their properties to a common data source that notifies all of its users when a property changes.
If you want to be able to easily change from one class to another in the future without rewriting all of your code, you need to create an Interface that defines all of the things that the two models have in common, and both model classes need to implement the interface. You could call the interface IModel1
So, instead of having a "Model1" in your viewmodel, you would have an "IModel1" in your viewmodel instead. You could pass in the same object you are passing in now, which is of type Model1, because it implements the IModel1 interface. When you are ready to switch, pass in a Model2 instead, and it will work without having to rewrite anything in your view or viewmodel. Your setters can be completely different - as long as both models have all of the methods and properties that are required by the interface, you will be OK.
Alternately, if Model2 is exactly like Model1 except that it has "extra stuff," you can make Model2 a derived class which derives from Model1.
Google searching either of those terms should point you toward a good tutorial.

Winforms Bindingsource Datamember property interface displays nothing

Setup:
Class Model with one property which type is an interface.
Create an instance and add it to a winforms bindingsource
Add a textbox to winforms and configure the text value to use the bindingsource with the property as a datamember
Model
public class Model
{
public ICustomer Customer { get; set; }
}
public class Customer : ICustomer
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
public interface ICustomer
{
string Name { get; set; }
}
Binding
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bsModel, "Customer", true));
Problem
When I run this code, the textbox remains empty (instead of showing the name of the customer).
BUT it does show it when I change the type of the Customer property in Model to the concrete Customer type.
I can't find any reason on MSDN why this is? Any ideas?
(Preferably no workaround like storing the toString value into another property, there is a framework doing this binding where I prefer not to hack into)
Probably you should specify the property that should be binded to the Text property of the TextBox
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding
("Text", this.bsModel, "Customer.Name", true));
If you remove the interface ICustomer and use directly the concrete class Customer then the binding code uses the ever present ToString() method that you have overridden in the concrete class and thus you get your textbox set.
For example, try to change the ToString to return a Surname property
Set the formattingEnabled property to false fixes it so it uses the toString() method as in the concrete implementation.
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bsModel, "Customer", false));
Problem:
I just browsed through the source at msdn and bumped into the following line:
if (e.Value != null && (e.Value.GetType().IsSubclassOf(type) || e.Value.GetType() == type || e.Value is System.DBNull))
return e.Value;
According to MSDN:
The IsSubclassOf method cannot be used to determine whether an interface derives from another interface, or whether a class implements an interface.
So this will evaluate to false and the further conversion will end up in returning null.
By setting formattingEnabled to false the parse method won't be called and the value will be simply returned instead.
Not sure if it's done on purpose or it's a bug. But I've got the feeling I better set the formattingEnabled to false for concrete types as well.
#Steve solution is working as well! (thanks)
But he's working around the interface type.
I just prefer simply working with toString(), since it can change over time and it's much easier to maintain.

How to make custom property of Linq2SQL class bindable?

My Database has a Table Foo with the Property IsTrue.
I made a Linq2SQL Class "myData.dbml" for this Database and added the generated Foo-Class to my Datasources bar.
I have a Simple Winforms Window with a ToggleButton which should bind to the inverse of IsTrue.
To achieve this I tried to add the Property NotTrue to the myData.dbml but this caused an SQLException, invalid row name "NotTrue".
I tried to add the Property Code to the myData.cs like this:
public bool NotTrue {
get {
return !this._IsTrue;
}
set {
if (this._IsTrue == value) {
this.OnIsTrueChanging(!value);
this.SendPropertyChanging();
this._IsTrue = !value;
this.SendPropertyChanged("IsTrue");
this.SendPropertyChanged("NotTrue");
this.OnIsTrueChanged();
}
}
}
But then the Property didnt appear when adding Foo to Datasources even when adding [Bindable(true)]
What worked, was binding to IsTrue and negating the Binding with this:
private void InvertBoolBinding(ControlBindingsCollection collection,string property) {
foreach (Binding b in collection) {
if (b.PropertyName.Equals(property)) {
ConvertEventHandler invert = new ConvertEventHandler((sender, e) => { e.Value = !((bool)e.Value); });
b.Format += invert;
b.Parse += invert;
}
}
}
Called like this:
InvertBoolBinding(lockToggleButton.DataBindings, "IsChecked");
Though in my main project there's a huge amount of those Bindings, this approach is quite a hassle. It seems that Datasources isn't able to connect properties through partial classes. When I add the property to the myData.designer.cs, it is found. But this class get regularly regenerated and my property gets lost.
So maybe I need a completely different approach or maybe theres a way to make partial class extensions work.
I hope you got some idea on that. Thanks!
Moving the Linq2SQL class to a different assembly makes Visual Studio resolve the partial connection.
Found here.

HDI: Constrain Property Set on Linq-To-Sql class

How do I constrain property setter on Linq-To-Sql class
I have a custom field that needs validation and the designer class can be over written.
I have overrider setter methods which would work but how to I restrict setting on the Linq-To-Sql class?
public partial class Frequency : INotifyPropertyChanging, INotifyPropertyChanged
{
public void SetStartTime(TimeSpan startTime)
{
if(startTime.Days > 0){throw new Exception("No day value is valid for start time value";}
this._StartTime = string.Format("{0}:hh\\:mm\\:ss", startTime);
}
public TimeSpan GetStartTime()
{
IEnumerable<int> startTime = this._StartTime.Split(':').Cast<int>();
return new TimeSpan(startTime.ElementAt<int>(0), startTime.ElementAt<int>(1), startTime.ElementAt<int>(2));
}
}
LINQ 2 SQL has everything you need to overcome this problem if you use the LINQ to SQL Classes designer.
Let's say your table has a column "Number" of type Int32.
The designer will create:
Field -> _Number
Property -> Number
Method -> OnNumberChanging(int value)
Method -> OnNumberChanged()
The last 2 methods are partial. This means you don't have to touch the designer generated files in case you refresh your classes from the database.
By creating the following in another file:
public partial class MyLinqToSqlClass
{
partial void OnNumberChanging(int value)
{
//your code here
//throw exception if necessary when validating
}
}
you get what you need.
This piece of code gets called inside the set method of the Number property right before the value of the field gets changed.
This way you don't worry about using the set method of the property.
Hope this helps.

Categories