
Checking the source code for WPF examples from Infragistics

We continue to test various C # projects in order to demonstrate the capabilities of the PVS-Studio static code analyzer. In this article, we will examine the results of testing WPF examples from Infragistics. Infragistics itself is a global software provider founded in 1989. The company made a name for itself by developing user interface components for third-party developers on all platforms, including .NET.
In PVS-Studio 6.00, we mainly implemented C # diagnostics of a general plan, based on the experience of the previously developed C ++ analyzer. Starting with PVS-Studio 6.01, we started creating diagnostics specialized specifically for the C # language. To start, the dependency properties (DependencyProperty) used in WPF projects were selected. The choice fell on DependencyProperty because of the complexity of their creation. The difficulty is that it is very easy to make a typo in the same code, which WPF projects gravitate towards. We have developed a number of diagnostics [ 3044 , 3045 , 3046 , 3047 , 3048 , 3049 ], specializing specifically in the analysis and verification of such properties.
As you know, one of the features of DependencyProperty is that in most cases, any error during their registration causes the program to crash at runtime (in runtime). Willy-nilly, programmers correct such errors by running the program again and again. Thus, precious minutes are spent searching for typos in the template code for creating DependencyProperty, and in total - whole hours. Moreover, as the practice of testing WPF projects has shown, not all errors in dependency properties are obvious after the first launch of the program.
The first test code for these diagnostics was the test case code from Infragistics . The archive was downloaded 02.02.2016 from here , the number of projects reaches 11, but they can be downloaded all in one archive.
Verification of the source code was performed using the PVS-Studio 6.01 static analyzer .
WPF errors
Many projects are built on the basis of a common reusable code, in which most errors were found.
Error N1
In the project “IGExtensions.Common.WPF” in the file “LambertConformalConic.cs” the following registration line was found “DependencyProperty”:
public static readonly DependencyProperty CentralMeridianProperty
= DependencyProperty.Register("CentralMeridianProperty",
typeof(double), typeof(LambertConformalConic),
new PropertyMetadata(0.0,
new PropertyChangedCallback(UpdateConstants)));
V3045 WPF: the names of the registered property 'CentralMeridianProperty', and of the property 'CentralMeridian', do not correspond with each other. LambertConformalConic.cs 130
As you can see, when registering DependencyProperty, “CentralMeridianProperty” was specified in his name instead of “CentralMeridian”. An error is made quite often due to copying the variable name, but it carries the following danger.
Namely, for writing / reading to the dependency property from C # code, the following layer property is created:
public double CentralMeridian {
get { return (double)GetValue(CentralMeridianProperty); }
set { SetValue(CentralMeridianProperty, value); }
}
When accessing from xaml markup, binding is written to the “CentralMeridian” property. WPF is smart enough to find the CentralMeridian property and read the value from there initially, but changes to the value in CentralMeridian, of course, will not be picked up.
Error N2
Continuing the topic of typos in the names of registered dependency properties, we consider the following error in the TransverseMercator.cs file of the IGExtensions.Common.WPF project.
public static readonly DependencyProperty CentralMeridianProperty
= DependencyProperty.Register("LongitudeOrigin", typeof(double),
typeof(TransverseMercator), new PropertyMetadata(0.0,
new PropertyChangedCallback(UpdateConstants)));
public double CentralMeridian { .... }
V3045 WPF: the names of the registered property 'LongitudeOrigin', and of the property 'CentralMeridian', do not correspond with each other. TransverseMercator.cs 95
As practice shows, dependency properties are registered in bulk, using multiple duplication of one line and subsequent edits. In other words, using Copy-Paste . And often somewhere in this block of the same type of code one variable is skipped and another name is assigned to it, which was next to it in the list. And considering that the list itself is usually located somewhere in a notebook [Notepad, Notepad ++, Sublime Text, etc.] next to another window, you can only check with your eyes to create all the necessary objects. It is very difficult to detect such errors, because the code is quite working, but the truth is only half.
Error N3
Everything seems to be clear with errors in the names of registered properties, but where else can you be mistaken when creating DependencyProperty? Well, for example, in the types of values that should be contained in them, as in the file "PropertyBrushColorEditor.cs" of the same project "IGExtensions.Common.WPF".
public static readonly DependencyProperty BrushColorProperty =
DependencyProperty.Register(BrushColorPropertyName,
typeof(Brush), typeof(PropertyBrushColorEditor),
new PropertyMetadata(null, (sender, e) =>
{....})
);
public SolidColorBrush BrushColor
{
get { return (SolidColorBrush)GetValue(BrushColorProperty); }
set { SetValue(BrushColorProperty, value); }
}
V3046 WPF: the type registered for DependencyProperty does not correspond with the type of the property used to access it.
If you have no questions about why it is incorrect to specify the parent class "Brush" during registration, and when referring to the "SolidColorBrush" inheritor in the "BrushColor" property, then this is good. Otherwise, we describe a simplified case of such a "game" with stored types.
For example, imagine a simple case. Let's create a simple WPF project and add the following dependency property to the window class:
public static DependencyProperty MyIndexProperty =
DependencyProperty.Register("MyIndex", typeof(int),
typeof(MainWindow), new FrameworkPropertyMetadata(1));
int MyIndex
{
get { return (int)GetValue(MyIndexProperty); }
set { SetValue(MyIndexProperty, value); }
}
In the xaml markup, write the following:
....Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource =
{RelativeSource Mode=Self}}">
And add the code for the button click event to the window class:
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Title = this.MyIndex.ToString();
}
All. How can I make sure everything works? Move the slider, the number changes. We click on the button, and the window title immediately changed its text to the current value in the slider. By the way, as you can see, integer values are displayed in TextBlock.
And now, when registering DependencyProperty, we will replace the type with "int" with the general type "object":
public static DependencyProperty MyIndexProperty =
DependencyProperty.Register("MyIndex", typeof(object),
typeof(MainWindow), new FrameworkPropertyMetadata(1));
Leave the rest unchanged and run the program again.
The program has started and now, when we move the slider, real values are displayed in the TextBlock. It’s easy to guess that if we try to press a button, the program will simply crash because it will not be able to convert a real value in MyIndexProperty to an integer in the MyIndex property. It would seem a trifle, but it led to sad consequences.
Error N4
In addition to the above errors that apply to most projects (which is especially sad, because they have never been noticed and corrected in any project), there are errors local to one IGEquityTrading.WPF project:
public static readonly DependencyProperty
AxisFinancialIndicatorYTemplateProperty =
DependencyProperty.Register("AxisFinancialIndicatorYTemplate",
typeof(DataTemplate),
typeof(DataChartEx),
new PropertyMetadata(default(DataTemplate)));
public DataTemplate AxisCategoryYTemplate{
get { return (DataTemplate)
GetValue(AxisFinancialIndicatorYTemplateProperty); }
set {
SetValue(AxisFinancialIndicatorYTemplateProperty, value); }
}
V3045 WPF: the names of the property registered for DependencyProperty, and of the property used to access it, do not correspond with each other. DataChartEx.cs 469
And again Infragistics steps on the same rake, instead of the registered name “AxisFinancialIndicatorYTemplate” create a property called “AxisCategoryYTemplate”.
Error N5
public static readonly DependencyProperty
FinancialIndicatorSeriesTemplateProperty =
DependencyProperty.Register("FinancialIndicatorTemplate",
typeof(DataTemplate),
typeof(DataChartEx),
new PropertyMetadata(default(DataTemplate)));
public DataTemplate FinancialIndicatorSeriesTemplate {
get { return (DataTemplate)
GetValue(FinancialIndicatorSeriesTemplateProperty); }
set {
SetValue(FinancialIndicatorSeriesTemplateProperty, value); }
}
V3045 WPF: the names of the property registered for DependencyProperty, and of the property used to access it, do not correspond with each other. DataChartEx.cs 344
In the latter case, the error most likely occurred after refactoring, when they decided to specify the variable, and inserted the word “Series” in the middle of the phrase “FinancialIndicatorTemplate”. The most interesting thing is that they changed everywhere, even in the XAML markup and in "#region", but they forgot about the registered property name.
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ App.xaml (123):
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ App.xaml (214): FinancialIndicatorSeriesTemplate = "{StaticResource FinancialIndicatorSeriesTemplate}"
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ Controls \ DataChartEx.cs (189): var financialIndicator = FinancialIndicatorSeriesTemplate.LoadContent () as Series;
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ Controls \ DataChartEx.cs (330): #region FinancialIndicatorSeriesTemplate (DependencyProperty)
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ Controls \ DataChartEx.cs (336): public DataTemplate FinancialIndicatorSeriesTemplate
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ Controls \ DataChartEx.cs (349): #endregion FinancialIndicatorSeriesTemplate (DependencyProperty)
- .... \ Infra \ EquityTrading \ IGEquityTrading.WPF \ Controls \ StockHistoryChart.xaml (646): FinancialIndicatorSeriesTemplate = "{StaticResource FinancialIndicatorSeriesTemplate}"
Moreover, the registered name “FinancialIndicatorTemplate” is not used anywhere, and we already know what this oversight leads to.
Unspecified C # Errors
There are no more WPF errors in these builds from Infragistics. As already mentioned, most WPF diagnostics are designed specifically for the preliminary detection of errors before compiling and starting the project. And these projects with examples of the use of components have already been tested by programmers and QA specialists. Additionally, projects were watched by users who rated the quality and usability of the product precisely by test cases. I think if they noticed a mistake, then, most likely, they notified the developers.
Naturally, besides WPF errors, there are others in these assemblies. The analyzer issued a total of several hundred warnings. Not all of the messages indicate real errors. Many warnings (for example, about comparing a double with a constant) are simply not relevant for this type of project. This is not scary, since the analyzer provides several mechanisms for suppressing uninteresting messages.
In any case, there are many warnings, and most of them indicate anomalies in the code. These are real bugs or a smell code. Therefore, we recommend that the authors of the project independently verify the project and examine the warnings. Here we will go through only the most interesting of them:
public bool IsValid
{
get {
var valid =
double.IsNaN(Latitude) || double.IsNaN(Latitude) ||
this.Weather.DateTime == Weather.DateTimeInitial;
return valid;
}
}
V3001 There are identical sub-expressions 'double.IsNaN (Latitude)' to the left and to the right of the '||' operator. WeatherStation.cs 25 It
's hard for programmers to live. They must understand not only the programming itself, but also in the area where the program should work. So it turns out that they must understand the subject area and know such words as “Vira” (Up), “Lane” (Down), “Latitude” (Latitude), “Longitude” (Longitude), etc. This only adds complexity, especially if the concepts are similar in spelling. So it turns out that we mistakenly write a check for the same variable: double.IsNaN (Latitude) || double.IsNaN (Latitude).
The following error:
private static int clipSegment(....)
{
if (xmax > rc.Right && xmax > rc.Right)
{
return -1;
}
}
V3001 There are identical sub-expressions 'xmax> rc.Right' to the left and to the right of the '&&' operator. Geometry Geometry.CubicSpline.cs 529
Checking the limits of a variable is a common thing, but it’s quite easy to make a mistake in the signs and then in the variable that you need to write. In fact, to eliminate such errors, you just need to adhere to the following pattern: the general variable in the condition is written from different sides in the expressions.
if (xmin < rc.Right && rc.Right < xmax)
Mistake is harder and easier to read.
PS True, such a trick does not work in the Entity Framework, when converting LINQ code into an SQL query, the program crashes. These are the things :)
In fact, in this project, Infragistics is something out of the ordinary with such checks. In addition to the above code, a similar error was repeated in the following lines:
private static int clipSegment(....)
{
....
if (ymin < rc.Top && ymin < rc.Top) //<= здесь
....
if (ymax > rc.Bottom && ymax > rc.Bottom) //<= и здесь
....
}
Diagnosis V3001 few of these errors, and it continues to expand the project :). Here is another example of its operation:
private static bool IsInDesignModeStatic(this Application app)
{
....
if (_isInDesignMode != null && _isInDesignMode.HasValue)
return _isInDesignMode.Value;
....
}
V3001 There are identical sub-expressions '_isInDesignMode! = Null' to the left and to the right of the '&&' operator. NavigationApp.cs 415
In this case, we are not dealing with an error, but with redundant code. It was enough to write:
if (_isInDesignMode.HasValue)
Another actuation of V3001 sends us to the IgWord.Infrastructure project:
void ParagraphSettingsPreviewAdapter_PropertyChanged(
object sender, PropertyChangedEventArgs e) {
....
if (LineSpacingType == Infrastructure.LineSpacingTypes.Exactly
|| LineSpacingType == Infrastructure.LineSpacingTypes.Exactly){
....
}
V3001 There are identical sub-expressions 'LineSpacingType == Infrastructure.LineSpacingTypes.Exactly' to the left and to the right of the '||' operator. ParagraphSettingsPreviewAdapter.cs 268
What exactly the developer wanted to do here is not very clear, but obviously not what is written.
From V3001, in order of priority, let's move on to V3010.
In the IGEarthQuake.WPF project, there are a couple of function calls:
public MapViewModel() {
....
WeakPropertyChangedListener.CreateIfNecessary(_service, this);
....
}
V3010 The return value of function 'CreateIfNecessary' is required to be utilized. MapViewModel.cs 42
public TimeLineViewModel(){
....
WeakPropertyChangedListener.CreateIfNecessary(_service, this);
....
}
V3010 The return value of function 'CreateIfNecessary' is required to be utilized. TimeLineViewModel.cs 50
Actually, in both cases one function is called, and it is quite simple. Let's look at its implementation:
public static
WeakPropertyChangedListener CreateIfNecessary(object source,
IPropertyChangedListener listener){
INotifyPropertyChanged inpc = source as INotifyPropertyChanged;
return inpc != null ?
new WeakPropertyChangedListener(inpc, listener) : null;
}
Indeed, as you can see, the function does not make any global changes, and its result is also not used. So the question arises - why was she called at all? All this is suspicious ...
A similar example awaits us in the project “IGHospitalFloorPlan.WPF":
private void ParseAllShapefiles() {
....
this.ShapeFilesMaxBounds.Expand(new Thickness(10, 10, 10, 10));
....
}
V3010 The return value of function 'Expand' is required to be utilized. HospitalView.xaml.cs 52
Its implementation is a little trickier, but in the end it just returns a new object that is not used anywhere.
We are in the middle of the article. Look at the picture. Relax and then we will continue.

One of the most common types of errors is unsuccessful Copy-Paste code:
public static EsriMapImageryView
GetImageryView(EsriMapImageryStyle imageryStyle){
....
if (imageryStyle ==
EsriMapImageryStyle.UsaPopulationChange2010Overlay)
return EsriMapImageryViews.UsaPopulationChange2010Overlay;
if (imageryStyle ==
EsriMapImageryStyle.UsaPopulationChange2010Overlay)
return EsriMapImageryViews.UsaPopulationChange2010Overlay;
....
}
V3021 There are two 'if' statements with identical conditional expressions. The first 'if' statement contains method return. This means that the second 'if' statement is senseless EsriMapImageryView.cs 97
In this case, the same code is under the same condition. At this stage, the error is an unsuccessful (redundant) Copy-Paste code. But after refactoring, a situation may arise that the programmer will change the body of the child if statement, which is never executed, which will lead to errors in the program logic.
And let's see what other errors are found in the code of the company Infragistics.
For each line below, warning V3022 was issued :
public static double GenerateTemperature(GeoLocation location){
....
else if (location.Latitude > 10 || location.Latitude < 25)
....
else if (location.Latitude > -40 || location.Latitude < 10)
....
}
public static WeatherCondition GenerateWeatherCondition(....){
....
else if (location.Latitude > 10 || location.Latitude < 25)
....
else if (location.Latitude > -40 || location.Latitude < 10)
....
}
All errors boil down to the following message:
V3022 Expression 'location.Latitude> -40 || location.Latitude <10 'is always true. Probably the '&&' operator should be used here.
What can I say? Probably the same as above, when describing one of the errors of V3001. It is useful to use a pattern when the same variable is written from different sides in the expression:
if (xmin < rc.Right && rc.Right < xmax)
This completes the description of the errors of the first level and proceed to the second and third, because the same message number, depending on the situation, has a different priority.
At the third level, diagnostic messages are collected, which the analyzer gives out when it is poorly confident in its correctness. Also at level 3 are diagnostics that are not relevant for all projects.
In practice, level 3 warnings rarely reveal true errors. Often these are still false positives or messages that may indicate, although the correct code, but has a “smell”. In any case, if there is time, these diagnostics are worth exploring and refactoring the code.
Let's start with code with two identical functions:
// 0 reference
public static double Ramp(double a) {
return a - Math.Floor(a);
}
// 1 reference
public static double Frac(double a) {
return a - Math.Floor(a);
}
V3013 It is odd that the body of 'Ramp' function is fully equivalent to the body of 'Frac' function (28, line 33). Math.cs 28
If the Frac function makes any sense, because A similar function was in Pascal, but the Ramp function has no analogue, or I did not find them. Actually, the counters of the number of places where functions are used speak for themselves (see comments).
In order not to go far from the V3013 error, consider the case when this error appeared at the second level.
public void StartCurrent()
{
StartTask("Current");
}
public void StopCurrent()
{
StartTask("Current");
}
V3013 It is odd that the body of 'StartCurrent' function is fully equivalent to the body of 'StopCurrent' function (503, line 507). DataViewModel.cs 503
Apparently, in the second case, the StartTask function was confused with StopTask. Both of these functions are in the code, and they act quite unambiguously and according to their names.
Next, consider a series of messages related to the code below:
{
IsUpdating = true;
....
IsUpdating = false;
}
A similar code is found in 4 places (in each assembly)
- V3008 The 'IsUpdating' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 201, 195. GeoRegion.cs 201
- V3008 The 'IsUpdating' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 212, 205. GeoRegion.cs 212
- V3008 The 'IsUpdating' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 226, 216. GeoRegion.cs 226
- V3008 The 'IsUpdating' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 244, 236. GeoRegion.cs 244
Initially, it seems that this variable is used for inter-thread communication. But as it turned out, in practice this variable is not found anywhere, except for the lines for which the diagnostics worked.
Well, suppose that they decide to use this variable for cross-thread synchronization. And here we are just waiting for an unpleasant surprise. A variable declaration is as follows:
protected bool IsUpdating = false;
As you can see, there is no “volatile” keyword, and as a result, the compiler successfully optimizes it and will work completely differently than we would like.
What else was interesting in the code? For example, extra calculations:
Example 1:
public static void Normalize(....)
{
var x = rect.X < boundingRect.X ? boundingRect.X : rect.X;
x = (rect.X + rect.Width) > boundingRect.Right ?
boundingRect.X : rect.X;
}
V3008 The 'x' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 96, 95. RectEx.cs
Example 2:
private static GradientStopCollection fromInterpolation(....){
....
Color color=ColorTool.FromAHSV(ahsv[0],
ahsv[1],
ahsv[2],
ahsv[3]);
color = ColorTool.FromARGBInterpolation(min, p, max[i].Color);
....
}
V3008 The 'color' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 165, 163. BrushTool.cs
Sometimes there are generally funny snippets of code:
private void UpdateAutoSavedState() {
AutoSaved = true;
AutoSaved = false;
}
V3008 The 'AutoSaved' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 691, 690. ShellViewModel.cs 691
For those who doubt this code, I quote the property declaration:
private bool autoSaved;
public bool AutoSaved
{
get { return autoSaved; }
set { autoSaved = value; }
}
And again, neither "volatile", nor anything like that that would speak of the hidden meaning of this action - no.
Let's move on to another group of lines with error V3029 :
public void OnPropertyChanged(PropertyChangedEventArgs ea) {
....
var index = this.SelectedBrushCollectionIndex;
....
if (index >= 0)
DebugManager.LogData(this.BrushCollectionList[index].ToText());
if (index >= 0)
this.SelectedBrushCollectionIndex = index;
....
}
V3029 The conditional expressions of the 'if' operators located alongside each other are identical. Check lines: 338, 339.
public static void EnableSeriesMouseDoubleClick(
this XamGeographicMap geoMap, bool isEnabled = true){
....
if (geoMap != null) geoMap.SeriesMouseLeftButtonDown +=
OnSeriesMouseLeftButtomDown;
if (geoMap != null) geoMap.SeriesMouseLeftButtonUp +=
OnSeriesMouseLeftButtonUp;
....
if (geoMap != null) geoMap.SeriesMouseLeftButtonDown -=
OnSeriesMouseLeftButtomDown;
if (geoMap != null) geoMap.SeriesMouseLeftButtonUp -=
OnSeriesMouseLeftButtonUp;
....
}
V3029 The conditional expressions of the 'if' operators located alongside each other are identical. Check lines: 92, 93. GeoMapAdapter.cs 92
V3029 The conditional expressions of the 'if' operators located alongside each other are identical. Check lines: 100, 101. GeoMapAdapter.cs 100
public void SyncSeriesViewPropertyChanges() {
if (this.SeriesView != null)
this.SeriesView.PropertyUpdated += OnSeriesViewPropertyUpdated;
if (this.SeriesView != null)
this.SeriesView.PropertyChanged += OnSeriesViewPropertyChanged;
}
V3029 The conditional expressions of the 'if' operators located alongside each other are identical. Check lines: 342, 343. GeoSeriesLayer.cs 342
As they say - "just in case, but then suddenly" ...
Although these are not mistakes, repeated checks clutter up the code and complicate its understanding.
And here is the redundant code that arose most likely during refactoring.
public Frame NavigationTarget
{
get { return (Frame)this.GetValue(NavigationTargetProperty); }
set {
var targetFrame = value as Frame;
if (targetFrame != null)
this.SetValue(NavigationTargetProperty, value);
}
}
"Value" is already of type Frame; casting is pointless. But in this case, it is necessary to consider the situation in a broader sense. Infragistics performs a null check when writing to DependencyProperty. For such checks, the developers of the dependency properties provided a callback function "ValidateValueCallback". This function checks the values that are written to DependencyProperty, and it is set when registering the dependency property.
Conclusion
Once again, our rainbow unicorn in shining armor has revealed a considerable number of problem areas (not all the errors that we found are listed in the article). Yes, now developers can fix the code and make it better than it was ... What it was when it was written ... What it was, when it was tested ... What it was, when it was rewritten, it was launched, and it crashed again or again or worked wrong, as needed ...
In my practice at the previous work, there were rummages on weekends and at night, a few days before the deadline, when it was necessary to do a lot and very quickly. The whole team knew what to do, but the rush and fatigue made themselves felt, and more time was spent on debugging. Those. we write the code, start, and it does not work as intended. We stop everything, set a breakpoint and start again. We perform all the steps for repetition, we arrive at the breakpoint and check line by line and see what happens in it. Jumping back and forth through the code and looking at the values in the variables. But in the end it turns out that somewhere in the condition they mixed up the variable or sign ... So it turns out that copy-paste takes 15 minutes of pure time to make a typo in a banal typo ...
So, no matter how much we check projects, no matter how many errors we find in them, this is just the small tip of a huge iceberg of problems that arise when writing code.
No one is immune from mistakes. Even creating a code that should serve as a role model and a business card of a company, it is impossible to avoid mistakes.
My sincere advice to you - use the PVS-Studio analyzer constantly. For this, it has all the tools. For example, there is a mode in which it checks the changed files, you don’t even have to run it, the analyzer will check what is needed and report problems in them.

If you want to share this article with an English-speaking audience, then please use the link to the translation: Vitaliy Alferov. Analyzing source code of WPF examples by the Infragistics Company .
Have you read the article and have a question?
Often our articles are asked the same questions. We collected the answers here: Answers to questions from readers of articles about PVS-Studio, version 2015 . Please see the list.