Monday, August 22, 2011

Small things learned today: Raise Base Class Event in Derived Classes.

I have to admit that I did not know this before today.

If I have a base class that have controls, I can not directly subscribe to the events invoked by the controls of this class (or, more generally, I can not directly subscribe to any events declared by the base class, but in my case I was interested in button click events).

I have to use a simple technique to achieve my goal: In the base class, provide and EventHandler (my button is called "Run Draw", hence the names)

public event EventHandler RunDrawClicked;
protected virtual void OnRunDrawClicked(EventArgs e)
{
EventHandler handler = RunDrawClicked;
if (handler != null)
{
handler(this, e);
}
}

base class can subscribe to its own button click, of course

protected void btnRunDraw_Click(object sender, System.EventArgs e)
{
MessageBox.Show("base");
OnRunDrawClicked(e);
}

and the derived class can subscribe to the event provided by the base class

protected override void OnRunDrawClicked(EventArgs e)
{
MessageBox.Show("derived");
base.OnRunDrawClicked(e);
}

Reference:

How to: Raise Base Class Events in Derived Classes by . Also posted on my website

Monday, August 15, 2011

Third Party DLL random thoughts ...

I'm always very happy then a third party dll or SDK comes with a code sample, in any language. I think this is the best what developers can do to ensure that users of their libraries will not have problems. It helped me a number of times (Oh, so I have to pass THIS to the function ... why didn't they mention it in the manual?).

But I like it a bit less when the code comes as a neat Visual Studio solution which no one obviously tested to make sure it compiles.

So I made an empty header file bc_Content_Decoder_Constants.h and added it to the project and commented out the body of a function that used the constants from the header.

Now I'm up to the next challenge: I can run the application in release mode, but not in debug mode.

Looks a bit vague.

I spent a while playing with linker settings and application manifest, but eventually ended up debugging the application in my head. In this particular case it was not as hard as it may sound - just looked at what functions were called in what sequence and duplicated that in my C# "throwaway" app and found a workaround to my problem. Sill, sample code from the third party was better than nothing.

by . Also posted on my website

Sunday, July 17, 2011

BindingList

A bug was reported in one of my applications. Well, the sequence of actions was not very likely to happen, but still it was a bug. In fact, when a certain action (printing a card) was performed, the program behaved as expected. When the same action was performed a second time, an exception was thrown. Here is what the stack trace said:

System.IndexOutOfRangeException: Index -1 does not have a value.
atSystem.Windows.Forms.CurrencyManager.get_Item(Int32 index)
atSystem.Windows.Forms.CurrencyManager.get_Current()
at System.Windows.Forms.DataGridView.DataGridViewDataConnection.OnRowEnter(DataGridViewCellEventArgs e)
atSystem.Windows.Forms.DataGridView.OnRowEnter(DataGridViewCell&dataGridViewCell, Int32 columnIndex, Int32 rowIndex, Boolean canCreateNewRow, Boolean validationFailureOccurred)
at System.Windows.Forms.DataGridView.SetCurrentCellAddressCore(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick)
at System.Windows.Forms.DataGridView.SetAndSelectCurrentCellAddress(Int32 columnIndex, Int32 rowIndex, Boolean setAnchorCellAddress, Boolean validateCurrentCell, Boolean throughMouseClick, Boolean clearSelection, Boolean forceCurrentCellSelection)
at System.Windows.Forms.DataGridView.MakeFirstDisplayedCellCurrentCell(Boolean includeNewRow)
atSystem.Windows.Forms.DataGridView.OnEnter(EventArgs e)
atSystem.Windows.Forms.Control.NotifyEnter()
atSystem.Windows.Forms.ContainerControl.UpdateFocusedControl()

Well, one thing was obvious - I had nowhere to start from. The exception was not triggered by anything in my code. In fact, when I debugged the application, it happened after a GroupBox status was set to inactive.
There were people on the internet who had this problem before, and generally the suggestion was to look at the places where CurrentCell value of a DataGridView is accessed. Supposedly the problem was that CurrentCell value is null. However, I checked all places where CurrentCell was used and there were no indications of anything wrong happening.

Eventually, I came across a simple and short comment which saved me.

DataGridView - error driving me mad!

"I had the same problem. I changed the list bind to the datasource from a List to a BindingList."

I still don't know what triggered the exception in the first place and don't have much time at the moment to spend on it - as it often happens, if it works, it's good enough already. And the application will be abandoned soon anyway and replaced by a completely new version. So it will remain a mystery for me. by . Also posted on my website

Tuesday, June 28, 2011

Cryptic Error Message

This is another cryptic error message the reason for which will go into the "mystery" basket for me.

All I needed was to tweak a setup and deployment project a little, so I made a copy from an existing one, added and removed some files, compiled without errors and tried to install.

Well, I had to work around it for now since there is really not enough information to even start investigating.

by . Also posted on my website

Monday, May 10, 2010

Where has my stored procedure parameter gone?

I had to change the database and some code to allow for proper saving/reading of Unicode from the application. Changing the parameter type in the stored procedure from varchar to nvarchar and doing the same change to the appropriate database table columns was the easy part. Doing some adjustments to the code, well, was also the easy part but with a little trick hidden inside.

So, here's how the parameters are added to the collection:

_collParam.Add(DataManager.BuildSqlParameter("@MessageText", SqlDbType.VarChar,
ParameterDirection.Input, Message.ToString()));

Just change VarChar to NVarChar, should be easy as pie.

And I end up with the SqlException "Procedure or function 'udp_MyProcedure_ups' expects parameter '@MessageText', which was not supplied". So, where has my parameter gone if I can clearly see that I'm adding it? The answer lies within the DataManager.BuildSqlParameter() function. Here's part of what it does:

if (value != "")
{
if (paramType == SqlDbType.Int)
param.Value = Convert.ToInt32(value);
else if (paramType == SqlDbType.Bit)
param.Value = Convert.ToBoolean(value);
else if (paramType == SqlDbType.DateTime)
{
param.Value = Convert.ToDateTime(value);
}
else if (paramType == SqlDbType.VarChar)
param.Value = Convert.ToString(value);
else if (paramType == SqlDbType.Float)
param.Value = (float)Convert.ToDecimal(value);
}

So what happens when the parameter does not belong to any of the types? It gets no value assigned, causing the exception. There is no "default" value. Easy to fix, but worth noting.

by . Also posted on my website

Friday, December 11, 2009

Unit Testing With Compact Framework and Visual Studio

Following up my issue with running NUnit tests for the Windows Mobile application, I came across a couple of articles on using the unit testing framework integrated in Visual Studio 2008 which is now supposed to be user friendly.
The process starts with selecting the function name, right-clicking on it and selecting "Create Unit Tests"

I can select the functions I want unit tests to be created for - I'll only choose one for now

I am then prompted for the name of the project where my tests will be created. Visual Studio adds a new project to the solution and this is the code for the test method created for the function I chose.


///
///A test for CreateDatabase
///

[TestMethod()]
public void CreateDatabaseTest()
{
DataBase target = new DataBase(); // TODO: Initialize to an appropriate value
target.CreateDatabase();
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}

This is great, except that I want to test for the things I want to test. So, of course, I need to change that. That's probably closer to what I want to test in my method:


[TestMethod()]
public void CheckDatabaseCreation()
{
DataBase target = new DataBase();
target.SetFileName(@"\Program Files\TestDB\TTrack.sdf");
target.SetConnectionString(@"Data Source=\Program Files\TestDB\TTrack.sdf");
target.DeleteDatabase();
target.CreateDatabase();
target.RunNonQuery(target.qryInsertRecord);
int count = target.RunScalar(target.qryCountUsers);
Assert.AreEqual(count, 1);
}

This is not so much different from the way tests are created in NUnit. In fact, so far there is no difference at all. Now, to run the test. There is a menu item "Test" in the top menu where I can select Test->Windows->Test View and the "Test View" becomes visible.

There I can see my tests - the auto generated one and the one I added myself.


I can run all tests or select any combination of tests I want to run from the Test View and choose either "Run Selection" or "Debug Selection" (I did not find out yet what the difference is - if I place a breakpoint inside the test method and choose "Debug Selection", the execution does not break at the breakpoint). After the test(s) finished running, I can see the result in the Test Results window.

by . Also posted on my website

Tuesday, November 24, 2009

Compact Framework and NUnit

I have the idea of a small application I could write for the Windows Mobile. The application will only use its local SQL Server database, at least initially, and it is really simple to create a local database on the device. The only things I need are the physical location of the sdf file on the device and the connection string.

In my "DataBase" class I generate them

private string GetLocalDatabasePath()
{
string applicationPath = Path.GetDirectoryName(this.GetType().Assembly.GetName().CodeBase);
string localDatabasePath = applicationPath + Path.DirectorySeparatorChar +
"TTrack.sdf";
return localDatabasePath;
}

private string GetLocalConnectionString()
{
string localConnectionString = "Data Source=" +
GetLocalDatabasePath();

return localConnectionString;
}

To create a database I just check if the database file already exists, and if not - I create it. Also, for testing purposes, the delete database function is used.

internal void CreateDatabase()
{
if (!File.Exists(localDatabasePath))
{
using (SqlCeEngine engine = new SqlCeEngine(localConnectionString))
{
engine.CreateDatabase();
}

RunNonQuery(qryCreateTables);
}
}

internal void DeleteDatabase()
{
string dbPath = GetLocalDatabasePath();
if (File.Exists(dbPath))
{
File.Delete(dbPath);
}
}

The RunNonQuery bit is just the creation of tables in the database.

internal void RunNonQuery(string query)
{
string connString = GetLocalConnectionString();

using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
}

The query for now just creates the simplest possible "dummy" table

internal string qryCreateTables = "CREATE TABLE Users (" +
"UserID uniqueidentifier PRIMARY KEY DEFAULT NEWID() NOT NULL, " +
"Name NVARCHAR(50) NOT NULL )";

The RunScalar, obviously, is used to run ExecuteScalar(). Some refactoring still required to improve the code.

internal int RunScalar(string query)
{
string connString = GetLocalConnectionString();

using (SqlCeConnection cn = new SqlCeConnection(connString))
{
cn.Open();
SqlCeCommand cmd = cn.CreateCommand();
cmd.CommandText = query;
return int.Parse(cmd.ExecuteScalar().ToString());
}
}

Now that I can create a database, I can run this simple bit of code to see if it is working.

DataBase db = new DataBase();
db.CreateDatabase();
db.RunNonQuery(db.qryInsertRecord);
MessageBox.Show(db.RunScalar(db.qryCountUsers).ToString());

Database gets created, a record gets inserted, a messagebox with "1" is shown. All is well.

Next, I decide to quickly create and run a simple test for database creation: If the database is present, I delete it, then create a new one, insert one record and test for the count of records indeed being one.

Here is the test I write in NUnit.

[Test]
public void CheckDatabaseCreation()
{
DataBase db = new DataBase();
db.SetFileName(@"\Program Files\TestDB\TTrack.sdf");
db.SetConnectionString(@"Data Source=\Program Files\TestDB\TTrack.sdf");
db.DeleteDatabase();
db.CreateDatabase();
db.RunNonQuery(db.qryInsertRecord);
int count = db.RunScalar(db.qryCountUsers);
Assert.AreEqual(count, 1);
}

This does not go as well, however:

What happened there? Oh, of course - the NUnit test runs on the desktop, but the code is supposed to run on the emulator (I don't use the actual device yet). So, it looks like I will have to work on the approach to testing ...

by . Also posted on my website