Monday, 19 February 2007

Detailed error info for datasets filling

Tyred of the "Failed to enable constraints. One
or more rows contain values violating non-null, unique, or foreign-key
constraints". " error when filling dataset?

Well, the detailed error info is buried inside the dataset (not deeply buried after all...)

But for the lazy people (like me), you can add this class to whatever project you want and just call the static method in the inmediate window, passing the dataset, to see from where the error comes from...

Example: DataSetDebugger.GetErrors(myDataset)



The class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Diagnostics;

namespace DataDebug
{
class DataSetDebugger
{
public static void GetErrors(DataSet ds)
{
Debug.WriteLine("--- Checking errors on dataset: '" + ds.DataSetName + "'---");
foreach (DataTable tb in ds.Tables)
{
if (tb.HasErrors)
{
Debug.WriteLine(" Errors found on table: '" + tb.TableName + "'");
int i = 0;
foreach (DataRow dr in tb.GetErrors())
{

Debug.WriteLine(" Errors found on row: [" + i.ToString() + "]:");
Debug.WriteLine(" - Error description: " + dr.RowError);
foreach(DataColumn col in dr.GetColumnsInError())
{
Debug.WriteLine(" - Column: " + col.ColumnName + " Error: " + dr.GetColumnError(col));
}

i++;
}
}
}
Debug.WriteLine("--- Checking done ---");
Debug.WriteLine("");
}
}
}

Friday, 16 February 2007

DataGridView multiselect without CTRL

One of my mates has asked my about how to enable a datagridview multiselection without pressing CTRL key (for a touch screen). I haven't tested it throughly, but here comes a (C#) possible solution:

I´ve tested it only with a Datagridview configured for full row selection.

Basically I hold a private collection of the rows I want to select and every time a row is pressed, it´s selected or removed accordingly

The empty override method avoids selection refresh. Try to remove it and see...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Diagnostics;

namespace MyDataGridView
{
public partial class MyDataGridV : DataGridView
{
private Hashtable mySelectedRows = new Hashtable();

public MyDataGridV()
{

}

protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
{

}

protected override void OnCellClick(DataGridViewCellEventArgs e)
{
if (mySelectedRows.ContainsKey(e.RowIndex))
{
mySelectedRows.Remove(e.RowIndex);
}
else
{
mySelectedRows.Add(e.RowIndex, this.Rows[e.RowIndex]);
}
for (int i = 0; i < this.Rows.Count; i++)
{
if (mySelectedRows.Contains(i))
{
this.SetSelectedRowCore(i, true);
}
else
{
this.SetSelectedRowCore(i, false);
}
}
Debug.Assert(this.SelectedRows.Count == mySelectedRows.Count, "Selected rows number don´t match");
base.OnCellClick(e);
}


}
}

Wednesday, 29 November 2006

chm problems

This has nothing to do with .net ... but interesting.

Today I realized that no .chm file opened in my computer. It just tried to open the process and they closed without further clues.

Thanks to MJ's Help Diagnostics tool http://www.helpware.net/downloads/MJsDiag.zip
I found, under the HTML Help Run-time Components section this...

File not found: D:\WINDOWS\system32\hhctrl.ocx

I don´t know what the *** happened to this file, but after copying it again, it worked fine.

Tuesday, 28 November 2006

Winforms applications, threads and events

Ok here I go with my first entry with a very short sample I wrote for a mate to let him play with threads on winforms, you know... threads are funny!

Windows have a very special way to use threads on winforms as all the controls are bounded to the thread that creates them, so whenever you are going to modify a control´s property, you need to make sure you are doing from the right thread.

A common problem is that you launch a thread to perform async processing and at a certain point, the thread reaches some condition and launches an event.
In the event handler you want to modify some of the user interface properties but... you need to switch to the right thread. That´s where InvokeRequired and Invoke come to help.

InvokeRequired basically compares the thread identity to which the control you are testing is bounded with the thread identity of the running thread. If they are different, it returns true. In that
case, you must use Invoke to call a delegate in the right thread.

(Well it´s a little bit more complicated... Invoke can return false if the handle of the control hasn´t been created. If you are in that case, be careful of performing any operation as the control can be created bounded to the wrong thread. Check IsHandleCreated and CreateControl functions to learn more about it...)

By the way... playing with the delays you can realize that if the secondary thread finishes when the main thread is still busy, the invoke sentence is not executed till the main thread is finished. The reason for this is easy: The main thread is running continuosly a loop (you can see it in the call stack window) and the invoke sentence puts a special message to be consumed by that loop.
Only when the loop runs the next iteration, the invoke message is consumed.



Here it comes the mini-sample... copy paste it to a winforms code file to play with it




namespace win_threadDemo
{
public partial class Form1 : Form
{
private delegate void TextDelegate(string text);
private event TextDelegate EventDelegate;

public Form1()
{
InitializeComponent();
this.EventDelegate += new TextDelegate(this.OnEvent);
}

private void button1_Click(object sender, EventArgs e)
{
label1.Text = "";
label2.Text = "";
if (Thread.CurrentThread.Name == null)
{
Thread.CurrentThread.Name = "Main thread";
}

Thread t = new Thread(new ThreadStart(SecondaryThreadOperation));
t.Name = "Secondary thread";
t.Start();
// Long running operation on main thread
Thread.Sleep(15000);
label2.Text = "Main thread finished";

}


private void SecondaryThreadOperation()
{
//Some long task
Thread.Sleep(10000);
//Raise event if any suscriber is present
if (this.EventDelegate != null)
{
this.EventDelegate("Secondary thread finished");
}

}

private void OnEvent(string text)
{
if (label1.InvokeRequired)
{
label1.Invoke(new TextDelegate(this.ChangeText), new object[] { text });
}
else
{
this.ChangeText(text);
}
}

private void ChangeText(string text)
{
label1.Text = text + " modified on " + Thread.CurrentThread.Name;
}

}
}