I came across a bug in the .Net Framework the other day.
When specifying SortDescriptions on a CollectionViewSource there is a problem when using a complex path for the property name. This occurs when part of the path being checked is null. If I have the classes defined below
class Foo{
string Name {get; set;}
}
class Bar{
Foo FooMember {get; set;}
}
If there is a CollectionViewSource that contains a list of Bar's and it is desired to sort each bar by Foo's name a SortDescription can be created as
SortDescription d = new SortDescription("FooMember.Name", ListSortDirection.Ascending);
If that SortDescription is added to the sort descriptions of the CollectionViewSoure it will properly order the items with one exception. This one exception is if there is an instance of Bar in the collection that the CollectionViewSource uses as the source. In this case an ArgumentException stating that there is a type mismatch and the element must be a string occurs.
The reason this occurs is that in the Compare method of the SoftFieldComparer obtains a strange object when its accessing the instance of Bar that has a null FooMember. Rather than just being null, its an MS.Internal.NamedObject with a _name value of "DependencyProperty.UnsetValue". This is a problem because it then tries to do a comparison betwen the string and the NamedObject which cannot be compared, which causes the ArgumentException. This case needs to be handled to be able to sort items with null members.
I have submitted this as an issue to Microsoft https://connect.microsoft.com/VisualStudio/feedback/details/539559/sortfieldcomparer-compare-method-can-throw-an-exception
Friday, March 5, 2010
Wednesday, March 3, 2010
ContentPresenter, GridViewRowPresenter, and ListViewItems
There are two distinct ways that content can be presented within control templates. These are the GridViewRowPresenter and the ContentPresenter. The only place that the GridViewRowPresenter is used is within a GridView to present the cell of data. This is needed for the binding path to be evaluated properly. If for example I have the class Foo defined below:
public class Foo{
public string X {get; set;}
public string Y {get; set;}
}
If I want to use a ListView presented using a GridView inside it to present the values of both X and Y as columns you would write the following assuming the FooCollectionView is a collection view source or some other collection of Foo's
<ListView ItemsSource={Binding FooCollectionView}">
<ListView.View>
<GridView >
<GridViewColumn Header="X" DisplayMemberBinding="{Binding Path=X}" />
<GridViewColumn Header="Y" DisplayMemberBinding="{Binding Path=Age}" />
</GridView>
</ListView.View>
</ListView>
If you want to define a style or control template that applies to this control instead of the normal location, where you'd put a ContentPresenter in the template you put a GridViewRowPresenter. What happens if you use a ContentPresenter instead? You get the content presented but its not in a grid and its just the ToString() of the object. This obviously is not what you want to occur.
Therefore, it seems fairly obvious that you should use a GridViewRowPresenter. It however, is not that straightforward because what if you want to not use a GridView and instead just want to list them (the same as would be in a ListBox) so you have something like the following?
<ListView ItemsSource={Binding FooCollectionView}">
What happens when you define the style with GridViewRowPresenter to present the content? The ListView will appear to have no content because there is no GridView to present the content of. This does not present a huge problem if you are defining a style on a per ListView instance. However, it is a problem if you want to define a general style like you would in a theme. This presents a big problem because either you cannot use a GridView or must always use a GridView. You can get around this by using a ListBox anywhere you don't want to use a GridView. This strategy would mean letting the style dictate the form of the application which shouldn't be the case.
However, the real problem occurs when defining a reusable theme. If you want to define a general purpose theme that others can reuse without having to alter thier application such as in the WPFThems project then you need it be able to handle both cases. On a side-note some of the themes in WPFThemes use the ContentPresenter and some use the GridRowViewPresenter. This can make it so that the ListBox is shown properly with some themes applied and improperly with others.
The fix to this is a bit of a hack but ultimatley turns out to work. It basically involves defining both presenters inside the control template. So where you put the presenters you put code that looks like the following:
<GridViewRowPresenter x:Name="gridrowPresenter"
Content="{TemplateBinding Property=ContentControl.Content}"/>
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding Property=ContentControl.Content}" Visibility="Collapsed"/>
To get the content to correctly display doing this. For the template the following trigger will need to be added:
<Trigger Property="GridView.ColumnCollection" Value="{x:Null}">
<Setter TargetName="contentPresenter" Property="Visibility" Value="Visible"/>
</Trigger>
This trigger will show the ContentPresenter when the GridViewRowPresenter has no content. Since there is no GridView the GridViewRowPresenter will not display anything visually.
Obviously, this is a bit of a hack to get around a flaw with how WPF works. Hopefully, in a future version this will be addressed in how the framework works.
public class Foo{
public string X {get; set;}
public string Y {get; set;}
}
If I want to use a ListView presented using a GridView inside it to present the values of both X and Y as columns you would write the following assuming the FooCollectionView is a collection view source or some other collection of Foo's
<ListView ItemsSource={Binding FooCollectionView}">
<ListView.View>
<GridView >
<GridViewColumn Header="X" DisplayMemberBinding="{Binding Path=X}" />
<GridViewColumn Header="Y" DisplayMemberBinding="{Binding Path=Age}" />
</GridView>
</ListView.View>
</ListView>
If you want to define a style or control template that applies to this control instead of the normal location, where you'd put a ContentPresenter in the template you put a GridViewRowPresenter. What happens if you use a ContentPresenter instead? You get the content presented but its not in a grid and its just the ToString() of the object. This obviously is not what you want to occur.
Therefore, it seems fairly obvious that you should use a GridViewRowPresenter. It however, is not that straightforward because what if you want to not use a GridView and instead just want to list them (the same as would be in a ListBox) so you have something like the following?
<ListView ItemsSource={Binding FooCollectionView}">
What happens when you define the style with GridViewRowPresenter to present the content? The ListView will appear to have no content because there is no GridView to present the content of. This does not present a huge problem if you are defining a style on a per ListView instance. However, it is a problem if you want to define a general style like you would in a theme. This presents a big problem because either you cannot use a GridView or must always use a GridView. You can get around this by using a ListBox anywhere you don't want to use a GridView. This strategy would mean letting the style dictate the form of the application which shouldn't be the case.
However, the real problem occurs when defining a reusable theme. If you want to define a general purpose theme that others can reuse without having to alter thier application such as in the WPFThems project then you need it be able to handle both cases. On a side-note some of the themes in WPFThemes use the ContentPresenter and some use the GridRowViewPresenter. This can make it so that the ListBox is shown properly with some themes applied and improperly with others.
The fix to this is a bit of a hack but ultimatley turns out to work. It basically involves defining both presenters inside the control template. So where you put the presenters you put code that looks like the following:
<GridViewRowPresenter x:Name="gridrowPresenter"
Content="{TemplateBinding Property=ContentControl.Content}"/>
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding Property=ContentControl.Content}" Visibility="Collapsed"/>
To get the content to correctly display doing this. For the template the following trigger will need to be added:
<Trigger Property="GridView.ColumnCollection" Value="{x:Null}">
<Setter TargetName="contentPresenter" Property="Visibility" Value="Visible"/>
</Trigger>
This trigger will show the ContentPresenter when the GridViewRowPresenter has no content. Since there is no GridView the GridViewRowPresenter will not display anything visually.
Obviously, this is a bit of a hack to get around a flaw with how WPF works. Hopefully, in a future version this will be addressed in how the framework works.
Edit 11/16/10 Added Visibility="Collapsed" property to the contentPresenter element which was a bug in the implementation
Wednesday, February 10, 2010
SourceName In MultiTriggers in WPF Themes
There is a bug in the WPF code that can cause a null pointer exception. The null pointer exception occurs on line 5924 of System.Windows.StyleHelper.cs :
object evaluationValue = evaluationNode.GetValue( conditions[i].Property );
The problem has to do when you are defining a MultiTrigger on an element in a theme. This bug manifested itself when on our project we were refreshing a collection that was bound to a TreeViewer and the TreeViewItem style had a MultiTrigger in it. The style in question comes from the WPFThemes project and was the ExpressionDark Theme (the problem occurs in about 7 of the other themes in the project). The one Condition on the MultiTrigger in the theme has a SourceName defined for it. The SourceName was not needed because it was the element right inside the root element anyway. This bug was especially subtle because it would only manifest itself after other conditions would have happened that would cause the MultiTrigger to fire.
I suspect, but have not verified, that the precondition for causing this bug is having a currently selected TreeViewItem that is removed upon refreshing the control and a newly generated item in the refresh was being set as the selected item in its stead. The trigger was checking the condition but the new item hadn't fully been generated so when it was looking for the child element it didn't yet exist. This makes sense in context of the code in the StyleHelper class. The code that returns the element using the GetChild method defined on line 6388. The comment inside the method says:
This method was returning null because
was evaluating to true because styledChilderen was null.
The null pointer exception was occurring because line 5924
was assuming evaluationNode as not null.
It was assuming it could do this because of the Debug.Assert on line 5903:
This brings up a point about using Debug.Asserts in code. People often use them (I see it on my project often) when they don't want to perform the null check overhead or other conditions that they feel should never happen. The idea is that you should catch any of these cases in testing. Obviously this is a case that was over looked in Microsoft's testing. The method that set the value of evaluationNode before the Assert has a perfectly legitimate reason for returning null (see the comments earlier) then its not reasonable to use the Assert here. More on a future post about using Asserts versus null checks.
The work around for this bug was to remove the SourceName from the MultiTrigger. This does not change the style in a meaningful way and removes the null pointer exception
object evaluationValue = evaluationNode.GetValue( conditions[i].Property );
The problem has to do when you are defining a MultiTrigger on an element in a theme. This bug manifested itself when on our project we were refreshing a collection that was bound to a TreeViewer and the TreeViewItem style had a MultiTrigger in it. The style in question comes from the WPFThemes project and was the ExpressionDark Theme (the problem occurs in about 7 of the other themes in the project). The one Condition on the MultiTrigger in the theme has a SourceName defined for it. The SourceName was not needed because it was the element right inside the root element anyway. This bug was especially subtle because it would only manifest itself after other conditions would have happened that would cause the MultiTrigger to fire.
I suspect, but have not verified, that the precondition for causing this bug is having a currently selected TreeViewItem that is removed upon refreshing the control and a newly generated item in the refresh was being set as the selected item in its stead. The trigger was checking the condition but the new item hadn't fully been generated so when it was looking for the child element it didn't yet exist. This makes sense in context of the code in the StyleHelper class. The code that returns the element using the GetChild method defined on line 6388. The comment inside the method says:
// Notice that if we are requesting a childIndex that hasn't been
// instantiated yet we return null. This could happen when we are
// invalidating the dependents for a property on a TemplateNode and
// the dependent properties are meant to be on template nodes that
// haven't been instantiated yet.
This method was returning null because
if (styledChildren == null || childIndex > styledChildren.Count)
was evaluating to true because styledChilderen was null.
The null pointer exception was occurring because line 5924
object evaluationValue = evaluationNode.GetValue( conditions[i].Property );
was assuming evaluationNode as not null.
It was assuming it could do this because of the Debug.Assert on line 5903:
Debug.Assert(evaluationNode != null,
"Couldn't find the node corresponding to the ID and name given in the trigger. This should have been caught somewhere upstream, like StyleHelper.SealTemplate()." );
This brings up a point about using Debug.Asserts in code. People often use them (I see it on my project often) when they don't want to perform the null check overhead or other conditions that they feel should never happen. The idea is that you should catch any of these cases in testing. Obviously this is a case that was over looked in Microsoft's testing. The method that set the value of evaluationNode before the Assert has a perfectly legitimate reason for returning null (see the comments earlier) then its not reasonable to use the Assert here. More on a future post about using Asserts versus null checks.
The work around for this bug was to remove the SourceName from the MultiTrigger. This does not change the style in a meaningful way and removes the null pointer exception
Tuesday, October 20, 2009
Problems with Parameters and the Command Object in ODBC and OLE DB
In testing a web service that retrieves a set of records a member of my project team came across an odd behavior. The function that selected a list of records was slowing down when selecting a high volume of records 800-2000. At the time we were using the ODBC Command object and placing parameters in to narrow the selection of records to the set that was to be returned. By instrumenting the code with timestamps he was able to determine that the unexpected slowdown was in the retrieval of records. The odd part about the slow down was that it was occurring somewhere around 300 records in when he was selecting 800 records. Somewhere around the 300th record there would just be about a 90 second pause in the retrieval. This was an odd boundary. My first inclination was that there was something bad with a specific record that caused this. However, on running multiple iterations it was not always the same record that was causing the slow down. What was being selected was also only two fields. One field was an int and the other was a string no larger than 128 characters.
Having this information made the problem even stranger. I suggested that he change the ODBC objects over to be the newer OLE DB objects figuring it might be a bug with the ODBC Command or with something with how ODBC Command was retrieving records. The same behavior was demonstrated using OLE DB. This made the problem even more puzzling. My next thought was that there was some sort of locking taking place somewhere. This seemed unlikely given the simplicity of the database accesses and the lack of long running transactions. The repeatability also make this unlikely as well. Again, my team member came back to me with a negative report.
At this point I had him test it with retrieving more records. The results of this were odd as well. When he tested it with 1500 records the problem would happen at about 1000. When he used 2000 records the pause happened at about 1500. He couldn't test it with many more than 2000 records because that is the maximum number of parameters that can be put on a command object. This was a very odd pattern to be exhibiting. Why approximately 500 records from the end of the result set was a pause occurring.
It was at this time that the maximum of approximately 2000 records retrieved was realized because of the parameter limit. The reason that this issue was first discovered was that we were looking to be able to run the method in the Web Service to retrieve 5000 records efficiently. Obviously we could not have a 2000 record limit or we'd have to have multiple queries. We however were not really using parameters for their intended purposes really. One of the fatal flaws with all database connection objects I've seen in Java and in C# are that there is no way to easily push a list into an IN clause. Instead you wind up writingvalue = 1 OR value = 2 etc. What we were doing was just adding where the values were and modifying the query string and then adding a parameter for each '?'. This strikes me as bad to begin with. If you are already modifying the query string you are not getting any benefits from having the compiled version cached. Instead of entering the '?' we should have just been using a StringBuilder and creating the query on the fly. We really weren't using parameters the right way anyway.
After some discussion we decided that making this change made sense in the code. It was implemented building the condition dynamically and tested again. Magically the 90 seconds of delay disappeared. That meant we killed two problems at once. Our delay that caused the Web Service to time out was gone and we were no longer capped at about 2000 records. What did this mean about our problem though. It appears that there is a bug with both ODBC and OLE that if you try to have more than 500 parameters or so, they will freeze for an undetermined amount of time, for no good reason. You must therefore heavily consider what you are doing if you have 500 or more parameters. Analyze your code and you probably will find that there is some other change that can be made to eliminate that many parameters and you should no longer have that problem.
Having this information made the problem even stranger. I suggested that he change the ODBC objects over to be the newer OLE DB objects figuring it might be a bug with the ODBC Command or with something with how ODBC Command was retrieving records. The same behavior was demonstrated using OLE DB. This made the problem even more puzzling. My next thought was that there was some sort of locking taking place somewhere. This seemed unlikely given the simplicity of the database accesses and the lack of long running transactions. The repeatability also make this unlikely as well. Again, my team member came back to me with a negative report.
At this point I had him test it with retrieving more records. The results of this were odd as well. When he tested it with 1500 records the problem would happen at about 1000. When he used 2000 records the pause happened at about 1500. He couldn't test it with many more than 2000 records because that is the maximum number of parameters that can be put on a command object. This was a very odd pattern to be exhibiting. Why approximately 500 records from the end of the result set was a pause occurring.
It was at this time that the maximum of approximately 2000 records retrieved was realized because of the parameter limit. The reason that this issue was first discovered was that we were looking to be able to run the method in the Web Service to retrieve 5000 records efficiently. Obviously we could not have a 2000 record limit or we'd have to have multiple queries. We however were not really using parameters for their intended purposes really. One of the fatal flaws with all database connection objects I've seen in Java and in C# are that there is no way to easily push a list into an IN clause. Instead you wind up writing
After some discussion we decided that making this change made sense in the code. It was implemented building the condition dynamically and tested again. Magically the 90 seconds of delay disappeared. That meant we killed two problems at once. Our delay that caused the Web Service to time out was gone and we were no longer capped at about 2000 records. What did this mean about our problem though. It appears that there is a bug with both ODBC and OLE that if you try to have more than 500 parameters or so, they will freeze for an undetermined amount of time, for no good reason. You must therefore heavily consider what you are doing if you have 500 or more parameters. Analyze your code and you probably will find that there is some other change that can be made to eliminate that many parameters and you should no longer have that problem.
Labels:
C#,
Command,
Database,
Microsoft,
ODBC,
OLE DB,
Parameter,
Performance,
Query,
SQL Server,
WebService
Sunday, October 18, 2009
Windows Forms & WPF Element Host Bug And How to Fix It
If you haven't had the chance to look at what Microsoft did with the advent of the Windows Presentation Framework (WPF), I suggest you check it out. As a revamp of how Windows Applications are written it is pretty spot on with what needed to be done. I could ramble about how much I like WPF for a while but that's not the point of this post.
In an ideal world we would be able to rewrite our applications with new technologies, especially ones that improve our maintainability and improve its over all quality. However, in the real world this is not really feasible. We have budget, time, and customer constraints. Many times existing code has domain knowledge impeded in a non-extractable way. As much we as developers want to play with technologies and use the latest greatest things these constraints don't allow us to. Microsoft has recognized that we want to use new technologies in existing applications or we want to take an incremental approach to transition our applications. In order to help with either of these plans they have provided the ElementHost control which allows you to put WPF controls on a Windows Form.
The ElementHost is great conceptually and great when it is working, however it has a fundamental bug. What is this bug you may ask? The bug is that by default the controls contained in the element host will always be disabled because the enable property won't propagate through to them. This basically makes them read only controls. The good news is that there is a work around for it, however it is non-intuitive because it uses Win32 Interop. I was only able to know what to do once I found Microsoft's page about the bug. There are not really any references to it unless you search very specifically. The solution to the problem can be found here: http://support.microsoft.com/kb/955753
I recommend you create a class that derive a class from ElementHost and having that as part of your repository and use that for incorperating WPF controls into windows forms.
A couple side notes. This is only necessary if you have a Windows Form that you want to place WPF controls on. You can open a WPF Window in a Windows Form application as if was just another form so this is not necessary if you just want to combine WPF and Windows Forms inside the same application.
In an ideal world we would be able to rewrite our applications with new technologies, especially ones that improve our maintainability and improve its over all quality. However, in the real world this is not really feasible. We have budget, time, and customer constraints. Many times existing code has domain knowledge impeded in a non-extractable way. As much we as developers want to play with technologies and use the latest greatest things these constraints don't allow us to. Microsoft has recognized that we want to use new technologies in existing applications or we want to take an incremental approach to transition our applications. In order to help with either of these plans they have provided the ElementHost control which allows you to put WPF controls on a Windows Form.
The ElementHost is great conceptually and great when it is working, however it has a fundamental bug. What is this bug you may ask? The bug is that by default the controls contained in the element host will always be disabled because the enable property won't propagate through to them. This basically makes them read only controls. The good news is that there is a work around for it, however it is non-intuitive because it uses Win32 Interop. I was only able to know what to do once I found Microsoft's page about the bug. There are not really any references to it unless you search very specifically. The solution to the problem can be found here: http://support.microsoft.com/kb/955753
I recommend you create a class that derive a class from ElementHost and having that as part of your repository and use that for incorperating WPF controls into windows forms.
A couple side notes. This is only necessary if you have a Windows Form that you want to place WPF controls on. You can open a WPF Window in a Windows Form application as if was just another form so this is not necessary if you just want to combine WPF and Windows Forms inside the same application.
Initial Posting
I've always thought blogs were kind of a dumb idea where people talk about the minutia of life. I've recently come around on the concept of blogs being useful for technical purposes. I am finding that more and more in my professional life that blogs are good places for people to help others with posting information. There will be none of the unnecessary posting of crap that nobody will care about on this blog. Instead I will be posting things I find to be relevant and useful to other people. This means that this will be mainly focused on technical topics that I find useful.
I am a software developer. That means technical topics that are interesting to me will be what winds up on this blog. I spent the first few years of my career working in a Java environment but have been spending my recent time working in .Net. I have an appreciation for both platforms and think both have their uses and appropriate usages. I get excited about new ideas and new ways that improve how we program. I did start programming in C/C++ and don't know if I can ever go back to the unmanaged programming. Java and C# both have transformed the way I think of code and what I expect out of a compiler. I have also started to work some in Python so I have a appreciation for that language as well.
What I expect to wind-up on this blog is musings, technical tips, and just general technical topics I find interesting. I hope that what I write here is going to be a help to someone at some point. I have received so much help from others through blogs that I think this is really me giving back to others. I hope something here someday helps you
I am a software developer. That means technical topics that are interesting to me will be what winds up on this blog. I spent the first few years of my career working in a Java environment but have been spending my recent time working in .Net. I have an appreciation for both platforms and think both have their uses and appropriate usages. I get excited about new ideas and new ways that improve how we program. I did start programming in C/C++ and don't know if I can ever go back to the unmanaged programming. Java and C# both have transformed the way I think of code and what I expect out of a compiler. I have also started to work some in Python so I have a appreciation for that language as well.
What I expect to wind-up on this blog is musings, technical tips, and just general technical topics I find interesting. I hope that what I write here is going to be a help to someone at some point. I have received so much help from others through blogs that I think this is really me giving back to others. I hope something here someday helps you
Labels:
C#,
Introduction,
Java
Subscribe to:
Posts (Atom)