How to set a property by calling a method - silverlight

I'd like to know how to set a custom property of a custom user control by calling a method that would serve as "data provider" for that property.
What I mean is I'd like to write something like this:
<CatsUserControl Cats={FindAllCats} />,
where CatsUserControl is a User Control I created (which has a Property named Cats), and FindAllCats() is a method written in C# that returns a list of Cat objects.
Is this possible?
I appreciate any ideas. Thanks!
EDIT:
The whole thing began because I wanted to be able to assign a list to a property in XAML, the same way you do <Button Content="Value"/> (except, in my case, Value is a more complex type than a string, it's a List<KeyValuePair<String, Boolean>>). Because I didn't find any way to do that, I thought it might be possible to call a method that returns this list and assign the returned value to the property instead.
Hope that makes things clearer.

It seems like what you are trying to do is really just databinding?
In other words, you would approach it like this:
<CatsUserControl ItemsSource="{Binding AllCats}" />
Binding to a collection
If you want to bind specifically to a property called Cats you need to create an object that implements INotifyPropertyChanged, I think. (semi-pseudo code follows):
//binding to this public property in the CatsUserControl
public List<Cat> Cats;
public class BindingToCats : INotifyPropertyChanged
{
private List<Cat> allCats;
public List<Cat> AllCats
{
get { return allCats; }
set
{
allCats = value;
OnPropertyChanged("AllCats");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and then bind to it with your custom usercontrol:
<CatsUserControl Cats="{Binding AllCats, Source={StaticResource BindingToCats}}" />

What you are probably looking for is a TypeConverter.
Here is an example of how you can set a List< KeyValuePair< String, Boolean>> using a string in attribute syntax in xaml. The Xaml will end up looking something like:
<c:CustomButton Values="Hello World,true; Foo,false; Bar,true" />
The exact syntax of how you want to represent the list of key/value pairs is up to you.
Here is the code necessary to do this:
public class CustomButton : Button
{
[TypeConverter(typeof(ListOfStringAndBoolPairsTypeConverter))]
public List<KeyValuePair<String, Boolean>> Values { get; set; }
}
public class ListOfStringAndBoolPairsTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
//TODO: Should add better error handling here.
var stringValue = (string)value;
var pairs = stringValue.Split(';').Select(ps => ParsePair(ps));
var result = new List<KeyValuePair<String, Boolean>>();
result.AddRange(pairs);
return result;
}
private KeyValuePair<String, Boolean> ParsePair(string pairStringValue)
{
var splitString = pairStringValue.Split(',');
var key = splitString[0];
var value = Boolean.Parse(splitString[1].Trim());
return new KeyValuePair<string, bool>(key, value);
}
}

You ought to be able to do what you're asking by subscribing to events instead of binding directly from the XAML.
Also, you could use a converter, which is described here.

Related

Looking for work-around for inability of DataGridView control to bind to hierarchical (OO) data

It would seem that the DataGridView control can only bind to data sources that are flat (all the Properties are primative types). My data is hierarchal. For example:
interface INestedObj
{
string Prop3 { get; }
}
interface IParentObj
{
public string Prop1 { get; }
public string Prop2 { get; }
public INestedObj NestedObj { get; }
}
Given this, how does one bind to an object implementing IParentObj? Eventually you are faced with having to do something like this:
grid.Columns["prop1Col"].DataPropertyName = "Prop1";
grid.Columns["prop2Col"].DataPropertyName = "Prop2";
grid.Columns["prop3Col"].DataPropertyName = "How to display Prop3?";
grid.Columns["prop3Col"].DataPropertyName = "NestedObj.Prop3"; // does not work
I am looking for advice and/or work-arounds.
TIA
You can expose properties from INestedObj for binding, but the solution is very messy.To give some background, all WinForms controls which support databinding use TypeDescriptor to determine which properties exist on the objects they're binding to. Through TypeDescriptionProvider and CustomTypeDescriptor, you can override the default behaviour and thusly add/hide properties - in this case, hiding the NestedObj property and replacing it with all of the properties on the nested type.
The technique i'm going to show has 2 (big-ish) caveats:
Since you're working with interfaces (and not concrete classes), you have to add the custom type descriptor at runtime.
The custom type descriptor needs to be able to create a concrete instance of IParentObj, therefore it must know one such class which has a default constructor.
(Please excuse the lengthy code)
First, you need a way of wrapping a PropertyDescriptor from the nested type so that it can be accessed from the parent type:
public class InnerPropertyDescriptor : PropertyDescriptor {
private PropertyDescriptor innerDescriptor;
public InnerPropertyDescriptor(PropertyDescriptor owner,
PropertyDescriptor innerDescriptor, Attribute[] attributes)
: base(owner.Name + "." + innerDescriptor.Name, attributes) {
this.innerDescriptor = innerDescriptor;
}
public override bool CanResetValue(object component) {
return innerDescriptor.CanResetValue(((IParentObj)component).NestedObj);
}
public override Type ComponentType {
get { return innerDescriptor.ComponentType; }
}
public override object GetValue(object component) {
return innerDescriptor.GetValue(((IParentObj)component).NestedObj);
}
public override bool IsReadOnly {
get { return innerDescriptor.IsReadOnly; }
}
public override Type PropertyType {
get { return innerDescriptor.PropertyType; }
}
public override void ResetValue(object component) {
innerDescriptor.ResetValue(((IParentObj)component).NestedObj);
}
public override void SetValue(object component, object value) {
innerDescriptor.SetValue(((IParentObj)component).NestedObj, value);
}
public override bool ShouldSerializeValue(object component) {
return innerDescriptor.ShouldSerializeValue(
((IParentObj)component).NestedObj
);
}
}
Then you need to write a custom type descriptor that exposes the properties from the nested type:
public class ParentObjDescriptor : CustomTypeDescriptor {
public override PropertyDescriptorCollection GetProperties(
Attribute[] attributes) {
PropertyDescriptorCollection properties
= new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor outer in TypeDescriptor.GetProperties(
new ParentObj() /* concrete implementation of IParentObj */,
attributes, true)) {
if (outer.PropertyType == typeof(INestedObj)) {
foreach (PropertyDescriptor inner in TypeDescriptor.GetProperties(
typeof(INestedObj))) {
properties.Add(new InnerPropertyDescriptor(outer,
inner, attributes));
}
}
else {
properties.Add(outer);
}
}
return properties;
}
}
...and then you need a way of exposing the descriptor from above:
public class ParentObjDescriptionProvider : TypeDescriptionProvider {
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance) {
return new ParentObjDescriptor();
}
}
Finally, at run-time (before you bind to the DataGridView), you must associate the type description provider with the IParentObj interface. You can't do this at compile-time because TypeDescriptionProviderAttribute can't be placed on interfaces...
TypeDescriptor.AddProvider(new ParentObjDescriptionProvider(), typeof(IParentObj));
I tested this by binding a DataGridView to an IParentObj[] and, low and behold, it creates columns for Prop1, Prop2 and NestedObj.Prop3.
You have to ask yourself, though... is it really worth all that effort?
Here is a simple solution that came to me at the end of a long day.
I used a Linq query and projection to create an anonymous type that displays the proper information in the DataGridView.
var query = from pt in parentObjCollection
select new {Prop1=pt.Prop1, Prop2=pt.Prop2, NestedObj.Prop3=pt.NestedObj.Prop3};
I had to supply the proper value (NestedObj.Prop3) to the DataPropertyName property to get the value to display in the grid.
When I have more time I am going to try and implement Bradley's solution.
You could probably add an unbound column for "NestedObj.Prop3" and manually handle its value. To get the column populated, handle the CellFormatting event of the DataGridView, get the DataBoundItem from the current row and get the Prop3 from that. To update the data source, handle the CellValidated event to update the DataBoundItem.
There may be more appropriate events to use than the ones I mentioned, but you get the idea.
The easiest way I found is to create a Self property. See this solution:
Databinding a combobox column to a datagridview per row (not the entire column)

Simple small INotifyPropertyChanged implementation

Say I have the following class:
public MainFormViewModel
{
public String StatusText {get; set;}
}
What is the easiest smallest way to get my changes to StatusText to reflect to any controls that bind to it?
Obviously I need to use INotifyPropertyChanged, but is there a cool way to do it that does not clutter up my code? need lots of files? etc?
Note: If this is a dupe then I am sorry. I searched and could not find any thing but using T4 code Generation which does not sound easy (to setup at least).
Unfortunately C# doesn't offer an easy mechanism to do that automatically... It has been suggested to create a new syntax like this :
public observable int Foo { get; set; }
But I doubt it will ever be included in the language...
A possible solution would to use an AOP framework like Postsharp, that way you just need to decorate your properties with an attribute:
public MainFormViewModel : INotifyPropertyChanged
{
[NotifyPropertyChanged]
public String StatusText {get; set;}
}
(haven't tried, but I'm pretty sure Postsharp allows you to do that kind of thing...)
UPDATE: OK, I managed to make it work. Note that it's a very crude implementation, using reflection on a private field to retrieve the delegate... It could certainly be improved, but I'll leave it to you ;)
[Serializable]
public class NotifyPropertyChangedAttribute : LocationInterceptionAspect
{
public override void OnSetValue(LocationInterceptionArgs args)
{
object oldValue = args.GetCurrentValue();
object newValue = args.Value;
base.OnSetValue(args);
if (args.Instance is INotifyPropertyChanged)
{
if (!Equals(oldValue, newValue))
{
RaisePropertyChanged(args.Instance, args.LocationName);
}
}
}
private void RaisePropertyChanged(object instance, string propertyName)
{
PropertyChangedEventHandler handler = GetPropertyChangedHandler(instance);
if (handler != null)
handler(instance, new PropertyChangedEventArgs(propertyName));
}
private PropertyChangedEventHandler GetPropertyChangedHandler(object instance)
{
Type type = instance.GetType().GetEvent("PropertyChanged").DeclaringType;
FieldInfo propertyChanged = type.GetField("PropertyChanged",
BindingFlags.Instance | BindingFlags.NonPublic);
if (propertyChanged != null)
return propertyChanged.GetValue(instance) as PropertyChangedEventHandler;
return null;
}
}
Note that your class still need to implement the INotifyPropertyChanged interface. You just don't have to explicitly raise the event in your property setters.
Have a go of this http://code.google.com/p/notifypropertyweaver/
All you need to do is implement INotifyPropertyChanged
So your code will look like
public MainFormViewModel : INotifyPropertyChanged
{
public String StatusText {get; set;}
#region INotifyPropertyChanged Implementation
}
The build task will compile this (you never see the below code)
public MainFormViewModel : INotifyPropertyChanged
{
public String StatusText {get; set;}
private string statusText;
public string StatusText
{
get { return statusText; }
set
{
if (value!= statusText)
{
statusText = value;
OnPropertyChanged("StatusText");
}
}
}
#region INotifyPropertyChanged Implementation
}
By leveraging EqualityComparer.Default you can reduce the property setter code down to one line as follows:
private int unitsInStock;
public int UnitsInStock
{
get { return unitsInStock; }
set { SetProperty(ref unitsInStock, value, "UnitsInStock"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T field, T value, string name)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
If your view models inherit from a base class that defines the SetProperty method and the PropertyChanged event, then the amount of code required to support INotifyPropertyChanged in your child view models becomes very minimal (1 line).
This approach is more verbose then the code weaving methods mentioned in other answers, but doesn't require you to modify your build process to accomplish it.
Be sure to take a look at the upcoming C# 5 Caller Info attributes as well as it looks like they will allow us to avoid using a magic string in the method without the performance cost of reflection.
UPDATE (March 1st, 2012):
The .NET 4.5 Beta is out, and with it, you can further refine the above code to this which removes the need for the string literal in the caller:
private int unitsInStock;
public int UnitsInStock
{
get { return unitsInStock; }
set
{
SetProperty(ref unitsInStock, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I have a blog post that talks about it in slightly more detail.
Ive always liked this method
private string m_myString;
public string MyString
{
get { return m_myString; }
set
{
if (m_myString != value)
{
m_myString = value;
NotifyPropertyChanged("MyString");
}
}
}
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
or for less code bloat
set
{
m_myString = value;
NotifyPropertyChanged("MyString");
}
I have a base class called "Model". It exposes a protected object called DataPoints, which is essentially a dictionary.
C#
public String StatusText {
get {
return (string)DataPoints["StatusText"];
}
set {
DataPoints["StatusText"] = value;
}
}
VB
public Property StatusText as String
get
return DataPoints!StatusText
end get
set
DataPoints!StatusText = value
end set
end property
When you set a value in the DataPoints dictionary it does the following:
Checks to make sure the value actually changed.
Saves the new value
Sets the IsDirty property to true.
Raises the Property Changed event for the named property as well as the IsDirty and IsValid properties.
Since it is a dictionary, it also makes loading objects from a database or XML file really easy.
Now you may think reading and writing to dictionary is expensive, but I've been doing a lot of performance testing and I haven't found any noticable impact from this in my WPF applications.
The PropertyChanged.Fody NuGet package does this.
https://github.com/Fody/PropertyChanged
Add the PropertyChanged.Fody package to your project.
Reference PropertyChanged in your model: using PropertyChanged;
Add the [ImplementPropertyChanged] attribute to your class.
All of the properties in the class will now magically implement INotifyPropertyChanged. Note - Fody works by modifying the emitted IL so you will never actually see the code in VS - it just magically does it.
Additional docs:
https://github.com/Fody/PropertyChanged/wiki/Attributes

How do I dynamically generate columns in a WPF DataGrid?

I am attempting to display the results of a query in a WPF datagrid. The ItemsSource type I am binding to is IEnumerable<dynamic>. As the fields returned are not determined until runtime I don't know the type of the data until the query is evaluated. Each "row" is returned as an ExpandoObject with dynamic properties representing the fields.
It was my hope that AutoGenerateColumns (like below) would be able to generate columns from an ExpandoObject like it does with a static type but it does not appear to.
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Results}"/>
Is there anyway to do this declaratively or do I have to hook in imperatively with some C#?
EDIT
Ok this will get me the correct columns:
// ExpandoObject implements IDictionary<string,object>
IEnumerable<IDictionary<string, object>> rows = dataGrid1.ItemsSource.OfType<IDictionary<string, object>>();
IEnumerable<string> columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string s in columns)
dataGrid1.Columns.Add(new DataGridTextColumn { Header = s });
So now just need to figure out how to bind the columns to the IDictionary values.
Ultimately I needed to do two things:
Generate the columns manually from the list of properties returned by the query
Set up a DataBinding object
After that the built-in data binding kicked in and worked fine and didn't seem to have any issue getting the property values out of the ExpandoObject.
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Results}" />
and
// Since there is no guarantee that all the ExpandoObjects have the
// same set of properties, get the complete list of distinct property names
// - this represents the list of columns
var rows = dataGrid1.ItemsSource.OfType<IDictionary<string, object>>();
var columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string text in columns)
{
// now set up a column and binding for each property
var column = new DataGridTextColumn
{
Header = text,
Binding = new Binding(text)
};
dataGrid1.Columns.Add(column);
}
The problem here is that the clr will create columns for the ExpandoObject itself - but there is no guarantee that a group of ExpandoObjects share the same properties between each other, no rule for the engine to know which columns need to be created.
Perhaps something like Linq anonymous types would work better for you. I don't know what kind of a datagrid you are using, but binding should should be identical for all of them. Here is a simple example for the telerik datagrid.
link to telerik forums
This isn't actually truly dynamic, the types need to be known at compile time - but this is an easy way of setting something like this at runtime.
If you truly have no idea what kind of fields you will be displaying the problem gets a little more hairy. Possible solutions are:
Creating a type mapping at runtime by using Reflection.Emit, I think it's possible to create a generic value converter that would accept your query results, create a new type (and maintain a cached list), and return a list of objects. Creating a new dynamic type would follow the same algorithm as you already use for creating the ExpandoObjectsMSDN on Reflection.Emit
An old but useful article on codeproject
Using Dynamic Linq - this is probably the simpler faster way to do it.Using Dynamic Linq
Getting around anonymous type headaches with dynamic linq
With dynamic linq you can create anonymous types using a string at runtime - which you can assemble from the results of your query. Example usage from the second link:
var orders = db.Orders.Where("OrderDate > #0", DateTime.Now.AddDays(-30)).Select("new(OrderID, OrderDate)");
In any case, the basic idea is to somehow set the itemgrid to a collection of objects whose shared public properties can be found by reflection.
my answer from Dynamic column binding in Xaml
I've used an approach that follows the pattern of this pseudocode
columns = New DynamicTypeColumnList()
columns.Add(New DynamicTypeColumn("Name", GetType(String)))
dynamicType = DynamicTypeHelper.GetDynamicType(columns)
DynamicTypeHelper.GetDynamicType() generates a type with simple properties. See this post for the details on how to generate such a type
Then to actually use the type, do something like this
Dim rows as List(Of DynamicItem)
Dim row As DynamicItem = CType(Activator.CreateInstance(dynamicType), DynamicItem)
row("Name") = "Foo"
rows.Add(row)
dataGrid.DataContext = rows
Although there is an accepted answer by the OP, it uses AutoGenerateColumns="False" which is not exactly what the original question asked for. Fortunately, it can be solved with auto-generated columns as well. The key to the solution is the DynamicObject that can have both static and dynamic properties:
public class MyObject : DynamicObject, ICustomTypeDescriptor {
// The object can have "normal", usual properties if you need them:
public string Property1 { get; set; }
public int Property2 { get; set; }
public MyObject() {
}
public override IEnumerable<string> GetDynamicMemberNames() {
// in addition to the "normal" properties above,
// the object can have some dynamically generated properties
// whose list we return here:
return list_of_dynamic_property_names;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
// for each dynamic property, we need to look up the actual value when asked:
if (<binder.Name is a correct name for your dynamic property>) {
result = <whatever data binder.Name means>
return true;
}
else {
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
// for each dynamic property, we need to store the actual value when asked:
if (<binder.Name is a correct name for your dynamic property>) {
<whatever storage binder.Name means> = value;
return true;
}
else
return false;
}
public PropertyDescriptorCollection GetProperties() {
// This is where we assemble *all* properties:
var collection = new List<PropertyDescriptor>();
// here, we list all "standard" properties first:
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this, true))
collection.Add(property);
// and dynamic ones second:
foreach (string name in GetDynamicMemberNames())
collection.Add(new CustomPropertyDescriptor(name, typeof(property_type), typeof(MyObject)));
return new PropertyDescriptorCollection(collection.ToArray());
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes) => TypeDescriptor.GetProperties(this, attributes, true);
public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(this, true);
public string GetClassName() => TypeDescriptor.GetClassName(this, true);
public string GetComponentName() => TypeDescriptor.GetComponentName(this, true);
public TypeConverter GetConverter() => TypeDescriptor.GetConverter(this, true);
public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(this, true);
public PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(this, true);
public object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(this, editorBaseType, true);
public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(this, true);
public EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(this, attributes, true);
public object GetPropertyOwner(PropertyDescriptor pd) => this;
}
For the ICustomTypeDescriptor implementation, you can mostly use the static functions of TypeDescriptor in a trivial manner. GetProperties() is the one that requires real implementation: reading the existing properties and adding your dynamic ones.
As PropertyDescriptor is abstract, you have to inherit it:
public class CustomPropertyDescriptor : PropertyDescriptor {
private Type componentType;
public CustomPropertyDescriptor(string propertyName, Type componentType)
: base(propertyName, new Attribute[] { }) {
this.componentType = componentType;
}
public CustomPropertyDescriptor(string propertyName, Type componentType, Attribute[] attrs)
: base(propertyName, attrs) {
this.componentType = componentType;
}
public override bool IsReadOnly => false;
public override Type ComponentType => componentType;
public override Type PropertyType => typeof(property_type);
public override bool CanResetValue(object component) => true;
public override void ResetValue(object component) => SetValue(component, null);
public override bool ShouldSerializeValue(object component) => true;
public override object GetValue(object component) {
return ...;
}
public override void SetValue(object component, object value) {
...
}

PropertyChanged notification for calculated properties

I'm developing an application in Silverlight2 and trying to follow the Model-View-ViewModel pattern. I am binding the IsEnabled property on some controls to a boolean property on the ViewModel.
I'm running into problems when those properties are derived from other properties. Let's say I have a Save button that I only want to be enabled when it's possible to save (data has been loaded, and we're currently not busy doing stuff in the database).
So I have a couple of properties like this:
private bool m_DatabaseBusy;
public bool DatabaseBusy
{
get { return m_DatabaseBusy; }
set
{
if (m_DatabaseBusy != value)
{
m_DatabaseBusy = value;
OnPropertyChanged("DatabaseBusy");
}
}
}
private bool m_IsLoaded;
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
}
}
}
Now what I want to do is this:
public bool CanSave
{
get { return this.IsLoaded && !this.DatabaseBusy; }
}
But note the lack of property-changed notification.
So the question is: What is a clean way of exposing a single boolean property I can bind to, but is calculated instead of being explicitly set and provides notification so the UI can update correctly?
EDIT: Thanks for the help everyone - I got it going and had a go at making a custom attribute. I'm posting the source here in case anyone's interested. I'm sure it could be done in a cleaner way, so if you see any flaws, add a comment or an answer.
Basically what I did was made an interface that defined a list of key-value pairs to hold what properties depended on other properties:
public interface INotifyDependentPropertyChanged
{
// key,value = parent_property_name, child_property_name, where child depends on parent.
List<KeyValuePair<string, string>> DependentPropertyList{get;}
}
I then made the attribute to go on each property:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class NotifyDependsOnAttribute : Attribute
{
public string DependsOn { get; set; }
public NotifyDependsOnAttribute(string dependsOn)
{
this.DependsOn = dependsOn;
}
public static void BuildDependentPropertyList(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
var obj_interface = (obj as INotifyDependentPropertyChanged);
if (obj_interface == null)
{
throw new Exception(string.Format("Type {0} does not implement INotifyDependentPropertyChanged.",obj.GetType().Name));
}
obj_interface.DependentPropertyList.Clear();
// Build the list of dependent properties.
foreach (var property in obj.GetType().GetProperties())
{
// Find all of our attributes (may be multiple).
var attributeArray = (NotifyDependsOnAttribute[])property.GetCustomAttributes(typeof(NotifyDependsOnAttribute), false);
foreach (var attribute in attributeArray)
{
obj_interface.DependentPropertyList.Add(new KeyValuePair<string, string>(attribute.DependsOn, property.Name));
}
}
}
}
The attribute itself only stores a single string. You can define multiple dependencies per property. The guts of the attribute is in the BuildDependentPropertyList static function. You have to call this in the constructor of your class. (Anyone know if there's a way to do this via a class/constructor attribute?) In my case all this is hidden away in a base class, so in the subclasses you just put the attributes on the properties. Then you modify your OnPropertyChanged equivalent to look for any dependencies. Here's my ViewModel base class as an example:
public class ViewModel : INotifyPropertyChanged, INotifyDependentPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
// fire for dependent properties
foreach (var p in this.DependentPropertyList.Where((x) => x.Key.Equals(propertyname)))
{
PropertyChanged(this, new PropertyChangedEventArgs(p.Value));
}
}
}
private List<KeyValuePair<string, string>> m_DependentPropertyList = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> DependentPropertyList
{
get { return m_DependentPropertyList; }
}
public ViewModel()
{
NotifyDependsOnAttribute.BuildDependentPropertyList(this);
}
}
Finally, you set the attributes on the affected properties. I like this way because the derived property holds the properties it depends on, rather than the other way around.
[NotifyDependsOn("Session")]
[NotifyDependsOn("DatabaseBusy")]
public bool SaveEnabled
{
get { return !this.Session.IsLocked && !this.DatabaseBusy; }
}
The big caveat here is that it only works when the other properties are members of the current class. In the example above, if this.Session.IsLocked changes, the notification doesnt get through. The way I get around this is to subscribe to this.Session.NotifyPropertyChanged and fire PropertyChanged for "Session". (Yes, this would result in events firing where they didnt need to)
The traditional way to do this is to add an OnPropertyChanged call to each of the properties that might affect your calculated one, like this:
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
OnPropertyChanged("CanSave");
}
}
}
This can get a bit messy (if, for example, your calculation in CanSave changes).
One (cleaner? I don't know) way to get around this would be to override OnPropertyChanged and make the call there:
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "IsLoaded" /* || propertyName == etc */)
{
base.OnPropertyChanged("CanSave");
}
}
You need to add a notification for the CanSave property change everywhere one of the properties it depends changes:
OnPropertyChanged("DatabaseBusy");
OnPropertyChanged("CanSave");
And
OnPropertyChanged("IsEnabled");
OnPropertyChanged("CanSave");
How about this solution?
private bool _previousCanSave;
private void UpdateCanSave()
{
if (CanSave != _previousCanSave)
{
_previousCanSave = CanSave;
OnPropertyChanged("CanSave");
}
}
Then call UpdateCanSave() in the setters of IsLoaded and DatabaseBusy?
If you cannot modify the setters of IsLoaded and DatabaseBusy because they are in different classes, you could try calling UpdateCanSave() in the PropertyChanged event handler for the object defining IsLoaded and DatabaseBusy.

Silverlight DataBinding cross thread issue

I have an Image control with it's source bound to a property on an object(string url to an image). After making a service call, i update the data object with a new URL. The exception is thrown after it leaves my code, after invoking the PropertyChanged event.
The data structure and the service logic are all done in a core dll that has no knowledge of the UI. How do I sync up with the UI thread when i cant access a Dispatcher?
PS: Accessing Application.Current.RootVisual in order to get at a Dispatcher is not a solution because the root visual is on a different thread(causing the exact exception i need to prevent).
PPS: This only is a problem with the image control, binding to any other ui element, the cross thread issue is handled for you.
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});
Also look here.
Have you tried implementing INotifyPropertyChanged?
The property getter for RootVisual on the Application class has a thread check which causes that exception. I got around this by storing the root visual's dispatcher in my own property in my App.xaml.cs:
public static Dispatcher RootVisualDispatcher { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Page();
RootVisualDispatcher = RootVisual.Dispatcher;
}
If you then call BeginInvoke on App.RootVisualDispatcher rather than Application.Current.RootVisual.Dispatcher you shouldn't get this exception.
I ran into a similar issue to this, but this was in windows forms:
I have a class that has it's own thread, updating statistics about another process, there is a control in my UI that is databound to this object. I was running into cross-thread call issues, here is how I resolved it:
Form m_MainWindow; //Reference to the main window of my application
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
if(m_MainWindow.InvokeRequired)
m_MainWindow.Invoke(
PropertyChanged, this, new PropertyChangedEventArgs(propertyName);
else
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
This seems to work great, if anyone has suggestions, please let me know.
When ever we want to update UI related items that action should happen in the UI thread else you will get an invalid cross thread access exception
Deployment.Current.Dispatcher.BeginInvoke( () =>
{
UpdateUI(); // DO the actions in the function Update UI
});
public void UpdateUI()
{
//to do :Update UI elements here
}
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed.
For change notification to occur in a binding between a bound client and a data source, your bound type should either:
Implement the INotifyPropertyChanged interface (preferred).
Provide a change event for each property of the bound type.
Do not do both.
Example:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
// Change the namespace to the project name.
namespace TestNotifyPropertyChangedCS
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer type
// in the list does.
public partial class Form1 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();
// This DataGridView control displays the contents of the list.
private DataGridView customersDataGridView = new DataGridView();
// This BindingSource binds the list to the DataGridView control.
private BindingSource customersBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
// Set up the "Change Item" button.
this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Click +=
new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);
// Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);
this.Size = new Size(400, 200);
}
private void Form1_Load(object sender, EventArgs e)
{
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
// Attach the BindingSource to the DataGridView.
this.customersDataGridView.DataSource =
this.customersBindingSource;
}
// Change the value of the CompanyName property for the first
// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer> customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer>;
// Change the value of the CompanyName property for the
// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
}
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}

Resources