Click here to Skip to main content
15,897,519 members
Articles / Programming Languages / C#

Dependency Properties and Collections Combined with XAML

Rate me:
Please Sign up or sign in to vote.
2.40/5 (5 votes)
16 Aug 2011CPOL4 min read 40.2K   3   12
Describes a problem with using a collection type in a dependency property where the default value can't be null

I could have run this post in my Adventures while building a Silverlight Enterprise Application series, but I found it to become too easy that way, so I posted it with an actual title. :-) I did run into this while working on the Selector control as mentioned in part #11 of this series, but it's such a general pitfall, I figured it makes sense to put it outside the series.

As you may remember from this article, I placed two datagrids inside a usercontrol to allow users to select items by moving them from one list to another. As many of you already know, one of the features of the DataGrid control is the option to specify your own columns through the Columns property. I wanted the users of my control to have the same functionality.

To make everyone's lives as easy as possible, I thought I'd simply copy the DataGrid behaviour for this. I dug into the documentation and found that the DataGrid.Columns property is in fact an ObservableCollection of DataGridColumn objects. Thus, I defined a dependency property of this type. I have pasted some example code below, but before you dive into that, please let me state that I always use the propdp code snippet for dependency properties and I don't find it useful to clean it up as I consider it to be generated code. If anyone disagrees, please let me know (and why, of course).

So, the dependency property code looked like this:

C#
public ObservableCollection<DataGridColumn> Columns
{
get { return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty); }
set
{
SetValue(ColumnsProperty, value);
UpdateColumns();
}
}

// Using a DependencyProperty as the backing store for Columns.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", 
typeof(ObservableCollection<DataGridColumn>),
    typeof(Selector), new PropertyMetadata(null));

The UpdateColumns that is called from the properties setter simply copies all the columns from the Columns property into the Columns properties of either DataGrids. So I figured I would try this out in XAML. The following code snippet is slightly altered to conceal some of the details of the underlying project.

XML
<controls:Selector x:Name="stringsSelector"
SourceHeaderText="My source header"
TargetHeaderText="My target header"
>
<controls:Selector.Columns>
<data:DataGridTextColumn />
</controls:Selector.Columns>
</controls:Selector>

As you can see, I simply tried to add a DataGridTextColumn to my Columns collection. As I run this, the InitializeComponent() method of the page fails with a parse exception, stating that there is some invalid property (any Silverlight developer knows how useful these are :-( ). After I then stop the debugger, Visual Studio does put a blue line under the DataGridTextColum tag in the XAML, but also with the same exception. It doesn't make much sense as the DataGridTextColumn class should be valid in this position and I do hope some better exception handling will be introduced for problems like these.

At first, I thought that it may have something to do with the different namespaces and how they have to work together, but I ruled this out by building a small trial application with a similar scenario. I figured I would also add a collection variant to this application and it worked as well! Then I thought, maybe it had something to do with the DataGridColumn and I added that as well and it worked! Then I went in and compared the dependency properties and I found out that I had changed the default value of my dependency property in the trial application. I initialized an empty collection there, instead of setting it to null by default. I changed this in my trial application and it failed with the parser exception again. So here is the changed dependency property as it now works.

C#
public ObservableCollection<DataGridColumn> Columns
{
get { return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty); }
set
{
SetValue(ColumnsProperty, value);
UpdateColumns();
}
}

// Using a DependencyProperty as the backing store for Columns.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", 
typeof(ObservableCollection<DataGridColumn>),
    typeof(Selector), new PropertyMetadata
    (new ObservableCollection<DataGridColumn>()));

Conclusion

The dependency property gets accessed by the parser well before it gets set and apparently some member is accessed as well, causing a null reference exception, which is translated into a parser exception. By simply creating an empty instance, this goes away. A decent exception would have saved me several hours of troubleshooting, so I hope Microsoft handles this in future releases.

UPDATE

Microsoft has put in place a more descriptive error message in this particular case. Also, I should point out that calling code from the property setter is, in the case of a dependency property, not correct, as the setter is not called when accessed from XAML. To fix that, you should implement a PropertyChangedCallback delegate and pass that as an argument to the PropertyMetadata. That does not change the fact that you should still provide an instance as a default value for this dependency property.

Thanks for reading in again and I hope you found this article useful. If you have any questions or remarks, please leave them below. I always enjoy reading them and replying whenever needed.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) KnowledgePlaza
Netherlands Netherlands
Since early 2001 I've been working full time as a software developer and since 2004 I've been working mostly with Microsoft technology.
I started out as a product developer, but after a few years I switched to a project company where my roles ranged from developer up to consultant and from team lead and coach to manager.
Eventually I switched jobs and focused on the consultant part and then I got back to building a product once again. Now I work in a job where I get to do both.

Comments and Discussions

 
GeneralMy vote of 3 Pin
IvanHch9-Mar-11 2:01
IvanHch9-Mar-11 2:01 
GeneralMy vote of 1 Pin
KNH Prod27-Feb-11 16:15
KNH Prod27-Feb-11 16:15 
GeneralRe: My vote of 1 Pin
mrjvdveen11-Aug-11 20:37
professionalmrjvdveen11-Aug-11 20:37 
GeneralRe: My vote of 1 Pin
KNH Prod16-Aug-11 19:36
KNH Prod16-Aug-11 19:36 
GeneralRe: My vote of 1 Pin
mrjvdveen16-Aug-11 20:58
professionalmrjvdveen16-Aug-11 20:58 
GeneralUnexpected reuse of default value for collection types? Be careful Pin
Jared Thirsk20-Oct-09 8:18
Jared Thirsk20-Oct-09 8:18 
GeneralRe: Unexpected reuse of default value for collection types? Be careful Pin
Jared Thirsk20-Oct-09 11:24
Jared Thirsk20-Oct-09 11:24 
AnswerRe: Unexpected reuse of default value for collection types? Be careful [modified] Pin
IvanHch9-Mar-11 2:00
IvanHch9-Mar-11 2:00 
GeneralMy vote of 1 Pin
leckey18-Aug-09 6:37
leckey18-Aug-09 6:37 
QuestionRe: My vote of 1 Pin
mrjvdveen18-Aug-09 23:17
professionalmrjvdveen18-Aug-09 23:17 
Hi there,
First of all, thank you for voting.
Could you please state the reason for you voting 1? Unfortunately I'm not getting your report (I guess it goes to someone from CodeProject).
This way I might actually learn something and you might actually end up with better content.
Thanks in advance.
AnswerRe: My vote of 1 Pin
Jared Thirsk20-Oct-09 8:24
Jared Thirsk20-Oct-09 8:24 
GeneralRe: My vote of 1 Pin
mrjvdveen21-Oct-09 3:30
professionalmrjvdveen21-Oct-09 3:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.