How to use Exception Messages for IDataErrorInfo on WPF - c#

i was wondering how to best validate some user inputs on the WPF mvvm pattern? I've implemented IDataErrorInfo in my ViewModel. But I don't know how to use this interface with raising an exception. My purpose is to not have any logic in my viewModel. So the validation has to be in the business logic class.
The bounded field is the Property "Name" in my ViewModel. It is
ValidatesOnDataErrors=True
The Property in my viewmodel looks like this:
//Property in ViewModel
public string Name
{
get
{
return myBl.Name;
}
set
{
try
{
myBl.Name = value;
}
catch(InvalidCastException e)
{
Console.WriteLine(String.Format("Es ist eine Exception aufgetreten: {0}", e.Message));
}
}
}
The businesslogic lookes like that:
//BusinessLogic Class
public string Name
{
get { return name; }
set
{
if (value.Contains('1'))
throw new InvalidCastException("Cannot contain 1");
name = value;
}
}
The Exception is thrown as proposed but how to move on? I want the e.Message to be the ValidationErrorMessage.
The only examples i found were with validation in
public string this[string propertyName]
{
get
{ throw new NotImplementedException()}
}
But it not seems to be the doable way with exceptions.

Don't catch any exceptions in the ViewModel properties:
public string Name
{
get
{
return myBl.Name;
}
set
{
myBl.Name = value;
}
}
and check the ValidateOnExceptions option in the data binding dialog (or ValidatesOnExceptions=True in the XAML code).

Related

Two Way binding to a Dependency Property in a User Control and call a method

I know, title is a little confusing so let me explain. I have a user control that has a dependency property. I access this dependency property with a regular property called Input. In my view model I also have a property called Input. I have these two properties bound together in XAML using two-way binding as shown below:
<uc:rdtDisplay x:Name="rdtDisplay" Input="{Binding Input, Mode=TwoWay}" Line1="{Binding myRdt.Line1}" Line2="{Binding myRdt.Line2}" Height="175" Width="99" Canvas.Left="627" Canvas.Top="10"/>
Okay in my view model, I call a method whenever the value of Input is changed as shown in my property:
public string Input
{
get
{
return input;
}
set
{
input = value;
InputChanged();
}
}
The problem with this is that when I set the value of Input in my view model it only updates the value of the variable input as per my setter in my property. How can I get this to update back to the dependency property in the user control? If I leave the code input = value; out then I get a compilation error.
I need something like this:
public string Input
{
get
{
return UserControl.Input;
}
set
{
UserControl.Input = value;
InputChanged();
}
}
If I make the Input property in my view model look like this:
public string Input
{
get; set;
}
then it works, however, I am unable to call the InputChanged() method that I need to call when the Property is changed. All suggestions are appreciated.
Implement INotifyPropertyChanged in your ViewModel
public class Sample : INotifyPropertyChanged
{
private string input = string.Empty;
public string Input
{
get
{
return input;
}
set
{
input = value;
NotifyPropertyChanged("Input");
InputChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In your case, you can do it in the code behind of your usercontrol

ViewModel properties with multiple calls to PropertyChanged

Recently I've been learning C# and WPF for work. I'm trying to use MVVM on a project I'm working on, just to keep the code organized and learn how it works.
In MVVM, controls on the View bind to properties on the ViewModel, which implements INotifyPropertyChanged. Pretty often, when a certain property is updated, I'll want a bunch of other properties to get updated as a result.
For example, I have a ListBox with a TextBox above it. You can type in the TextBox, and it filters the stuff in the ListBox. But I also need to be able to clear the TextBox from code in certain cases. The code ends up looking like this:
private Collection<string> _listOfStuff;
public Collection<string> FilteredList
{
get
{
if (String.IsNullOrWhiteSpace(SearchText))
{
return _listOfStuff;
}
else
{
return new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
}
}
set
{
if (value != _listOfStuff)
{
_listOfStuff = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText"); // Tells the view to change the value of the TextBox
OnPropertyChanged("FilteredList"); // Tells the view to update the filtered list
}
}
}
As this project gets bigger, this is starting to feel sloppy. I have one setter with 6 calls to OnPropertyChanged and it's getting hard to keep track of stuff. Is there a better way to do this?
I tried out Assisticant on a project about a year ago. It figures out which of your properties need to raise notifications and also which are related. There is a good course for it on Pluralsight and the examples on the website are pretty good. If nothing else you could check out the source code to see how he did it.
Also some good suggestions from Change Notification in MVVM Hierarchies.
They mentioned:
Use an attribute -> e.g. [DependsUpon(nameof(Size))]
and
Josh Smith's PropertyObserver
Could put the raise property change calls in a method if you just need to raise the same notifications every time.
First you shouldn't do potentially expensive operations in a command, then you'll be able to remove the OnPropertyChanged("FilteredList"); from your SearchText.
So you should move that code from the getter and into it's own command and bind it from XAML (either as Command on a button or using Blends Interactivity Trigger to call it when the text fields value changes).
public ICommand SearchCommand { get; protected set; }
// Constructor
public MyViewModel()
{
// DelegateCommand.FromAsyncHandler is from Prism Framework, but you can use
// whatever your MVVM framework offers for async commands
SearchCommand = DelegateCommand.FromAsyncHandler(DoSearch);
}
public async Task DoSearch()
{
var result = await _listOfStuff.Where(x => x.Contains(SearchText)).ToListAsync();
FilteredList = new Collection<string>(result);
}
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
get
{
return _filteredList;
}
set
{
if (value != _filteredList)
{
_filteredList = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get
{
return _searchText;
}
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText");
}
}
}
On a side note: You can also use OnPropertyChanged(nameof(FilteredList)); to have a refactor friendly version, when you rename your property all of your OnPropertyChanged calls will be updated to. Requires C# 6.0 though, but it's compatible with older .NET Frameworks (back to 2.0), but requires Visual Studio 2015 or later
For anyone searching for a good solution to this type of problem: Check out ReactiveUI.
It is a framework based on Reactive Extensions (Rx), with the idea that you model this type of dependencies between properties explicitly, without a jungle of RaisePropertyChanged(..).
Specifically check out the ObservableAsPropertyHelper (sometimes called OAPH).
You should only raise OnPropertyChanged in the setter of the property itself.
A cleaner implementation of your ViewModel can be:
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
get
{
return _filteredList;
}
set
{
if (value != _filteredList)
{
_filteredList = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText");
FilteredList = new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
}
}
}
if you just don't wanna type only other option is to fire OnPropertyChanged for all properties which can be done by passing a null or string.Empty, although it will be sloppier code!
OnPropertyChanged(Null);
or
OnPropertyChanged(String.Empty);

How to validate view controls against data in the viewmodel

I'm new at WPF, and I'm getting my head around validators, it seems that you need to inherit from ValidationRule and override the Validate function, this totally separated from the view model, but what if I want to validate against some list/collection/set/dictionary in the viewmodel, to check whether this new input is not already in the list, a good example would be creating a validation to see whether or not an username is not already taken.
There are several different ways to do Validation in WPF. There's two main ways I can think of off the top of my head
Create Validation Rules then apply them in XAML
Implement IDataErrorInfo in your ViewModel
Validation Rules are specified in your XAML (GUI), while implementing IDataErrorInfo moves the Validation logic into your ViewModel (Business logic). While, ValidationRules are nice because you can create your own and reuse them, they also fail to provide validation in your business logic which is most likely required.
The concept of Client vs. Server side validation is interesting, perhaps as it pertains to Silverlight, but since you tagged this as WPF, I'm assuming the only difference is whether the validation occurs in the Views or ViewModels (UI or Business logic). It seem to me that even if your UI validated inputs, your ViewModels would still need to do proper validation.
Therefore, I suggest implementing IDataErrorInfo. By the way, the reason IDataErrorInfo works, is because a ValidationRule exists that checks for the IDataErrorInfo Interface! Here's an example of how I would do it in my ViewModelBase class:
Note: the immediately following examples ignore the fact you will likely need INotifyPropertyChanged notifications to update your bindings and instead focuses simply on Validation.
public class ViewModelBase : IDataErrorInfo
{
private Dictionary<string, string> errors = new Dictionary<string, string>();
// required for IDataErrorInfo
public virtual string Error
{
get { return String.Join(Environment.NewLine, errors); }
}
// required for IDataErrorInfo
public string this[string propertyName]
{
get
{
string result;
errors.TryGetValue(propertyName, out result);
return result;
}
}
// Useful property to check if you have errors
public bool HasErrors
{
get
{
return errors.Count > 0;
}
}
protected void SetError<T>(string propertyName, String error)
{
if (error == null)
errors.Remove(propertyName);
else
errors[propertyName] = error;
OnHasErrorsChanged();
}
protected string GetError<T>(string propertyName, String error)
{
String s;
errors.TryGetValue(propertyName, out s);
return s;
}
protected virtual void OnHasErrorsChanged()
{
}
}
Then your ViewModels can implement it like this:
public class MyViewModel : ViewModelBase
{
private string someProperty;
public string SomeProperty
{
get
{
return someProperty;
}
set
{
if(someProperty != null)
{
someProperty = value;
SetError("SomeProperty", ValidateSomeProperty());
}
}
}
private string ValidateSomeProperty()
{
if(String.IsNullOrEmpty(SomeProperty))
return "Value is required";
return null;
}
}
In your UI, you'll need to add ValidatesOnDataErrors and NotifyOnValidationError like this:
Text="{Binding SomeProperty, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
Note: Passing in strings to represent properties is kinda ugly (it's not refactor safe if you rename the property but forget to rename the string). INotifyPropertyChanged is the same way when you want to notify of property changes for DataBindings. Prism's NotificationObject has a refactor safe solution to this and it looks like this instead:
Replace GetError / SetError in the previous example with this:
protected void SetError<T>(Expression<Func<T>> prop, String error)
{
String propertyName = PropertySupport.ExtractPropertyName(prop);
if (error == null)
errors.Remove(propertyName);
else
errors[propertyName] = error;
OnHasErrorsChanged();
}
protected string GetError<T>(Expression<Func<T>> prop, String error)
{
String propertyName = PropertySupport.ExtractPropertyName(prop);
String s;
errors.TryGetValue(propertyName, out s);
return s;
}
And then my ViewModelBase is something like this:
public class ViewModelBase : NotificationObject, IDataErrorInfo
Then to implement properties:
public class MyViewModel : ViewModelBase
{
private string someProperty;
public string SomeProperty
{
get
{
return someProperty;
}
set
{
if(someProperty != null)
{
someProperty = value;
SetError( () => SomeProperty, ValidateSomeProperty()); // update validation for property
RaisePropertyChanged( () => SomeProperty); // notify data bindings
}
}
}
I didn't show the implementation for RaisePropertyChanged but it is in Prism's NotificationObject which is open source and a free download. You can instead implement INotifyPropertyChanged yourself and either raise the event with strings (not refactor safe) or implement it yourself similar to the above implemention for SetError (extract the property name and fire the event with it).
You'll need this helper method:
public static class PropertySupport
{
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpresssion)
{
if (propertyExpresssion == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpresssion.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException("The expression is not a member access expression.", "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException("The member access expression does not access a property.", "propertyExpression");
}
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
{
throw new ArgumentException("The referenced property is a static property.", "propertyExpression");
}
return memberExpression.Member.Name;
}
}
Edit: Simplified Refactor Safe Alternative
If you are using .NET 4.5 or later, you can use the CallerMemberAttribute like this example shows for INotifyPropertyChanged and my SetError implementation. Then you wouldn't need to extract the property name yourself via reflection as above (It simplifies it quite a bit).
Sorry to get off track talking about Property Change Notifications, but they go hand in hand if you want your DataBindings and Validation to work!
I Finally did it, this is how :
Xaml:
<Window.Resources>
<l:DataResource x:Key="ValidateFieldMethod" BindingTarget="{Binding IsFieldValid}"/>
</Window.Resources>
<xctk:IntegerUpDown Width="50" Maximum="300" Minimum="0">
<xctk:IntegerUpDown.Value>
<Binding Path="SelectedItem.TargetPosition" Mode="TwoWay">
<Binding.ValidationRules>
<l:CustomValidationRule Validator="{l:DataResourceBinding DataResource={StaticResource ValidateFieldMethod}}" />
</Binding.ValidationRules>
</Binding>
</xctk:IntegerUpDown.Value>
</xctk:IntegerUpDown>
The ValidatorRule:
public delegate bool CheckAgainstDataDelegate(object newValue, string fieldName);
public class CustomValidationRule : ValidationRule
{
public CheckAgainstDataDelegate Validator { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingGroup owner)
{
return Validate((object)owner, cultureInfo);
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingExpressionBase owner)
{
switch (ValidationStep)
{
case ValidationStep.UpdatedValue:
case ValidationStep.CommittedValue:
value = (object)owner;
break;
}
return new ValidationResult(Validator(value, ((BindingExpression) owner).ResolvedSourcePropertyName), null);
}
[Obsolete("Use Validator property of type delegate instead to validate the data",true)]
public override ValidationResult Validate(object value, CultureInfo cultureInfo) { return null; } //not used anymore
}
In the viewModel :
private CheckAgainstDataDelegate _isFieldValid;
public CheckAgainstDataDelegate IsFieldValid
{
get
{
return _isFieldValid
?? (_isFieldValid = delegate (object newValue,string propertyName)
{
switch (propertyName)
{
case "TargetPosition":
var newV = (int) newValue;
return Items.All(e => e.TargetPosition != newV);
default:
throw new Exception("Property Assigned to unknown field");
}
});
}
}
I used the help of http://www.wpfmentor.com/2009/01/how-to-add-binding-to-property-on.html to Bind in the ValidationrRule.
What do you Think?

ViewModel with IDataErrorInfo always calls getter after setting property?

I want to give this question as much context as possible, but, in summary, I'm basically asking two questions:
Does WPF always call the getter after setting a bound property when the setter doesn't throw an exception?
Is it possible to prevent the getter of a bound property from being called after an error has occurred in the setter when the ViewModel implements IDataErrorInfo?
I currently have a Model class that implements validation by throwing an exception from the property setter. Additionally, many of the properties are coupled, so modifying the value of one of them may cause several others to be recalculated. It implements INotifyPropertyChanged to alert outside listeners whenever a recalculation has occurred. It looks something like this:
public class Model : INotifyPropertyChanged
{
private double property1;
public double Property1
{
get { return property1; }
set
{
if (value < 0.0)
throw new Exception("Property1 cannot be less than 0.0.");
property1 = value;
OnPropertyChanged(new PropertyChangedEventArgs("Property1"));
}
}
// ...Everything needed to support INotifyPropertyChanged...
}
Initially, I implemented the ViewModel for this class to act as a thin wrapper around the model properties, providing additional view-specific behaviors whenever an error occurs (flagging invalid data, disabling buttons, etc.):
public class ViewModel : INotifyPropertyChanged
{
private readonly Model model;
public ViewModel()
{
model = new Model();
model.PropertyChanged += (sender, args) => OnPropertyChanged(args);
}
public string Property1
{
get { return model.Property1.ToString(); }
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch (Exception)
{
// Perform any view-specific actions
throw;
}
}
}
// ...Everything needed to support INotifyPropertyChanged
}
Notably, the ViewModel doesn't have any additional backing fields; all of its properties are directly linked to the corresponding properties in the Model, and any PropertyChanged notifications from the Model are passed along by the ViewModel.
But, I've frequently heard that using exceptions for validation can be limiting, and I'm starting to realize the same, specifically as the need for cross-coupled validation rules has increased in this application.
I didn't want to change behaviors of the Model, since it is already being used in several other places, but I went about changing the ViewModel to implement IDataErrorInfo:
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private readonly Model model;
public ViewModel()
{
model = new Model();
model.PropertyChanged += (sender, args) =>
{
errorList.Remove(args.PropertyName);
OnPropertyChanged(args);
};
}
public string Property1
{
get { return model.Property1.ToString(); }
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch(Exception ex)
{
// Perform any view-specific actions
errorList["Property1"] = ex.Message;
}
}
}
private readonly Dictionary<string, string> errorList = new Dictionary<string, string>();
public string this[string propertyName]
{
get
{
string errorMessage;
if (errorList.TryGetValue(propertyName, out errorMessage))
return errorMessage;
return String.Empty;
}
}
public string Error { get { return String.Empty; } }
// ...Everything needed to support INotifyPropertyChanged
}
However, this caused a drastic unwanted change in behavior. When using exceptions, after the user entered an invalid value, the Validation.ErrorTemplate for the control is displayed and the invalid value remains in the control, thus giving the user the opportunity to correct their mistake. When using IDataErrorInfo, WPF seems to call the property getter after the setter has completed. Since the Model hasn't changed whenever an error occurs, the invalid value is replaced by the previous value. So now I have a control displaying the Validation.ErrorTemplate but with a VALID being value displayed!
It seems crazy to me that WPF would automatically call a property getter without receiving a PropertyChanged notification (after the window has been initialized). And it doesn't attempt to call the getter after an exception is thrown, so why would it do it when IDataErrorInfo is used?
Am I doing something wrong that's causing this behavior? Is there a way to prevent WPF from calling the property getter in the ViewModel after an error has occurred in the setter when the ViewModel implements IDataErrorInfo?
I've tried adding backing fields to the ViewModel to store the invalid value, but due to the fact Model properties can be modified outside of the ViewModel (that's the reason it implements INotifyPropertyChanged in the first place), the solution ends up being quite complex and basically unsustainable in an environment with programmers of varying skill levels.
Here's the approach you want to have for form validation in MVVM
Your model
public class Product:IDataErrorInfo
{
public string ProductName {get;set;}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "ProductName":
validationResult = ValidateName();
}
}
}
}
Then in your ViewModel
public string ProductName
{
get { return currentProduct.ProductName; }
set
{
if (currentProduct.ProductName != value)
{
currentProduct.ProductName = value;
base.OnPropertyChanged("ProductName");
}
}
}
As another consideration, when I want to validate numbers (such as your double validation), keep the model as having a double instead of a string
public double? Property1 {get;set;}
then you can do this
<Textbox Name="myDoubleTextbox" >
<Binding ValidatesOnDataErrors="true" Path="Property1" TargetValueNull="" />
/>
so when they type something incorrect into the double box, it sends null to your model and you can check against that.
Although it doesn't answer the questions I asked, my coworker suggested that the desired behavior could be achieved by adding the invalid value to the errorList and modifying the getter in the ViewModel to return the invalid value whenever there is an error.
So the properties in the ViewModel look like this:
public string Property1
{
get
{
ErrorInfo error;
if (errorList.TryGetValue("Property1", out error))
return error.Value;
return model.Property1.ToString();
}
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch (Exception ex)
{
// Perform any view-specific actions
errorList["Property1"] = new ErrorInfo(value, ex.Message);
}
}
}
With the following updates to the IDataErrorInfo methods:
private struct ErrorInfo
{
public readonly string Value;
public readonly string Message;
public ErrorInfo(string value, string message)
{
Value = value;
Message = message;
}
}
private readonly Dictionary<string, ErrorInfo> errorList = new Dictionary<string, ErrorInfo>();
public string this[string propertyName]
{
get
{
ErrorInfo error;
if (errorList.TryGetValue(propertyName, out error))
return error.Message;
return String.Empty;
}
}
public string Error { get { return String.Empty; } }
This allows the PropertyChanged event handler to stay the same, and only requires small changes to the property getters and setters.

Data Binding to an object in C#

Objective-c/cocoa offers a form of binding where a control's properties (ie text in a textbox) can be bound to the property of an object. I am trying to duplicate this functionality in C# w/ .Net 3.5.
I have created the following very simple class in the file MyClass.cs:
class MyClass
{
private string myName;
public string MyName
{
get
{
return myName;
}
set
{
myName = value;
}
}
public MyClass()
{
myName = "Allen";
}
}
I also created a simple form with 1 textbox and 1 button. I init'd one instance of Myclass inside the form code and built the project. Using the DataSource Wizard in Vs2008, I selected to create a data source based on object, and selected the MyClass assembly. This created a datasource entity. I changed the databinding of the textbox to this datasource; however, the expected result (that the textbox's contents would be "allen") was not achieved. Further, putting text into the textbox is not updating the name property of the object.
I know i'm missing something fundamental here. At some point i should have to tie my instance of the MyClass class that i initialized inside the form code to the textbox, but that hasn't occurred. Everything i've looked at online seems to gloss over using DataBinding with an object (or i'm missing the mark entirely), so any help is great appreciated.
Edit:
Utilizing what I learned by the answers, I looked at the code generated by Visual Studio, it had the following:
this.myClassBindingSource.DataSource = typeof(BindingTest.MyClass);
if I comment that out and substitute:
this.myClassBindingSource.DataSource = new MyClass();
I get the expected behavior. Why is the default code generated by VS like it is? Assuming this is more correct than the method that works, how should I modify my code to work within the bounds of what VS generated?
You must assign the textbox's data source to be your new datasource. But additionally, you must assign the datasource's datasource to be an instance of your class.
MyDataSource.DataSource = new MyClass();
TextBox1.DataSource = MyDataSource;
That should work for your first pass. As others have mentioned, you may need to implement additional interfaces on your class (INotifyPropertyChanged etc), if you are going to be modifying the class properties via any background processes.
If you are only updating the properties via the form, then you do not need this step.
You should implement the INotifyPropertyChanged interface to your MyClass type:
public class MyClass : INotifyPropertyChanged
{
private string _myName;
public string MyName
{
get { return _myName; }
set
{
if( _myName != value )
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if( PropertyChanged != null )
PropertyChanged( this , new PropertyChangedEventArgs(propertyName) );
}
}
This interface is required for the DataBinding infrastructure if you want to support simple databinding.
The INotifyPropertyChanged interface is used to notify a 'binding' that a property has changed, so the DataBinding infrastructure can act accordingly to it.
Then, you can databind the MyName property to the Text Property of the textbox.
I get an error message in the DataBinding.Add("TEXT", myObject, myObjectProperty) method
This is probably because you're missing the explicit {get;set;} on the property declaration!
BAD:
public string FirstName; //<-- you will not be able to bind to this property!
GOOD:
public string FirstName { get; set; }
Looks like you probably need a Bindable attribute on your MyName property (and follow Frederik's suggestion as well):
[System.ComponentModel.Bindable(true)]
public string MyName
{
get { return _myName; }
set
{
if( _myName != value )
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
Via: http://support.microsoft.com/kb/327413
I don't have any code in front of me, but I think the data source is kind of like a collection. You have to add an instance of MyClass to the data source, and that's what the form fields will bind to. There's also methods for navigating through the data source to multiple instances of MyClass, but it doesn't sound like you need that. Check the docs for DataSource.
I don't think you need to implement any fancy interfaces. I seem to remember there's a method on the data source that lets you refresh or rebind the current item after you change some values.
using System.Collections.Generic;
public class SiteDataItem
{
private string _text;
private string _url;
private int _id;
private int _parentId;
public string Text
{
get
{
return _text;
}
set
{
_text = value;
}
}
public string Url
{
get
{
return _url;
}
set
{
_url = value;
}
}
public int ID
{
get
{
return _id;
}
set
{
_id = value;
}
}
public int ParentID
{
get
{
return _parentId;
}
set
{
_parentId = value;
}
}
public SiteDataItem(int id, int parentId, string text, string url)
{
_id = id;
_parentId = parentId;
_text = text;
_url = url;
}
public static List<SiteDataItem> GetSiteData()
{
List<SiteDataItem> siteData = new List<SiteDataItem>();
siteData.Add(new SiteDataItem(1, 0, "All Sites", ""));
siteData.Add(new SiteDataItem(2, 1, "Search Engines", ""));
siteData.Add(new SiteDataItem(3, 1, "News Sites", ""));
siteData.Add(new SiteDataItem(4, 2, "Yahoo", "http://www.yahoo.com"));
siteData.Add(new SiteDataItem(5, 2, "MSN", "http://www.msn.com"));
siteData.Add(new SiteDataItem(6, 2, "Google", "http://www.google.com"));
siteData.Add(new SiteDataItem(7, 3, "CNN", "http://www.cnn.com"));
siteData.Add(new SiteDataItem(8, 3, "BBC", "http://www.bbc.co.uk"));
siteData.Add(new SiteDataItem(9, 3, "FOX", "http://www.foxnews.com"));
return siteData;
}
}
More detail you can read tutorial dapfor. com

Categories