I was given a nice little Pocket PC application which worked well
but took far too long to start. My first code walk through was a WTF.
It was the common scenario, a well known major consulting firm
producing code which demonstrated they did not quite understand what
they were doing. But instead of just making fun of it in the WTF style, I'll approach it from the positive side as well with a walkthrough what I did with the problem.
The compact framework has no typed datasets. Working with a database
in a CF application can be a branch of sports on itself. As a pleasant
alternative you can work with plain XML files to store data and use
dataviews in you code to sort and query this data. The DataView class
provides a view on one table in an XML dataset. I never thought it was
such a hard thing to use, but this project did show some people do
completely misunderstand it.
The application was highly configurable. XML files contain things like the caption of labels and menu items. Like this
<object>btnNext.label</object>
</controls>
<object>btnPrev.label</object>
</controls>
At startup the application loaded the xml-file in a dataset. Some snippets found to actually set a caption.
public static DataView dvControls31;
.....
dvControls31 = new DataView(vfpdata.Tables["controls"]);
dvControls31.RowFilter = "screen = 'screen3' AND object =
'btnNext.label'";
.....
if (Welcome1.dvControls31.Count != 0)
btnNext.DataBindings.Add(new Binding("Text", Welcome1.dvControls31, "value"));
A dataview is declared, instantiated on a table after which the
desired row is filtered out. After that the button gets its caption by
data-binding it to the dataview.
This does work but gets more complicated when you want to set a
property which is not data-bindable, like a menu item. It took a dummy
label, leading to this
lblCHAR1.DataBindings.Clear();
if (Welcome1.dvMenu3A.Count != 0)
{
lblCHAR1.DataBindings.Add(new Binding("Text", Welcome1.dvMenu3A, "value"));
mnuAgenda.Text = lblCHAR1.Text;
}
lblCHAR1.DataBindings.Clear();
The dataview row is bound to the label caption after which the label
caption is directly assigned to the menu text. Which is part of the
solution:
- Databindings are meant to use with data which changes. For
setting a value just once they are a total waste of time. Use a plain
assignment there.
Changing the databindings to straight assignments made the code
leaner and faster. But the greatest WTF was where the data was. You
might have noticed that the dataview objects presented so far have
different names. It turned out the application declared and
instantiated a separate dataview for every configurable control
property in the application. Well over 40. No wonder startup took so
long.
You can do more with a dataview than just filtering out one row. The
rows in a dataview can be sorted and after that you perform a find to
get to a row. As a small demonstration, a part of a class to read
the configuration files:
public class ConfigLoader : DataSet
{
private DataView controls;
private DataView fields;
public void LoadFields(string fromFile)
{
readXMLfile(fromFile);
fields = new DataView(this.Tables["fields"]);
fields.Sort = "field, attrib";
}
public string FieldCaption(string field)
{
object[] searchArgs = new object[2];
searchArgs[0] = field;
searchArgs[1] = "label";
int rowNo = fields.Find(searchArgs);
if (rowNo >= 0)
return fields[rowNo]["value"].ToString();
else
return "";
}
}
}
The dataset wraps up a controls and a fields config file. Their
contents is queried using one dataview each. The complicated code to
set up the controls now reads like
Config =
new ConfigLoader();
lblCHAR1.Text = Config.FieldCaption("CHAR1");
lblCHAR2.Text = Config.FieldCaption("CHAR2");
- Dataviews are a dynamic view on underlying data. Do not create a new dataview for a new view on the same underlying data
Having made these changes the amount of code had dramatically shrunken. Startup time had halved.
But there was more to gain. The application worked with a larger set
of data. It displayed just a subset. A dataview would be an way to get
this subset. The application did use two but each of them was on
another dataset. A large part of the code was dedicated to keeping
these two datasets in sink. Throwing out one of the datasets and
opening both dataviews on the one left was another boost in performance.
- You can have multiple Dataviews on the same data, each providing a different set of rows. Do not duplicate data
And now I'll stop kicking in open doors. It's all in Visual Studio's help files. WTFM ?