Wednesday, 29 August 2007

Dataset serialization shuffles tables

Yes, another serialization problem.

We have defined a BIG dataset, then we create a instance of it. In a certain portion of the code, we serialize it with a binary formatter, but ... surprise when we deserialize, the tables order doesn't match!

Well, so far I haven't had much time to investigate, but setting the remoting format to binary does the trick:

ProcessDataSet.RemotingFormat = SerializationFormat.Binary

Tuesday, 28 August 2007

Serialization of a class that inherit from HashTable (or any class that implements ISerializable)

This is another funny problem related to serialization. Have you serialized a class and when deserialized again all the field values are set to default values? Well, check if this is your case:

If you have a class (Class1) that inherits from another one that is ISerializable (i.e. HashTable),

then you MUST write a constructor to handle SerializationInfo:

[Serializable]
class Class1: Hashtable
{
    public Class1(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {

    }

    public Class1()
    {
    }
    protected int number;
    private int othernumber;
    public string text;

    public int OtherNumber
    {
        get
        {
            return othernumber;
        }
        set
        {
            othernumber = value;
        }
    }

    public int Number
    {
        get
        {
            return number;
        }
        set
        {
            number = value;
        }
    }
}

What’s the problem related to this?. Well, write a short win application with a button to test:

private void button1_Click(object sender, EventArgs e)
{

    Class1 obj = new Class1();
    obj.Number = 99;
    obj.OtherNumber = 100;
    obj.text = "hello";

    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    byte[] serializedObject;
    System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();

    //formatter.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.
    formatter.Serialize(memoryStream, obj);
    serializedObject = memoryStream.GetBuffer();

    System.IO.MemoryStream memoryStream2 = new System.IO.MemoryStream(serializedObject);
    Class1 obj2 = (Class1)formatter.Deserialize(memoryStream2);
}

And now, check the obj2: None of the values where properly retrieved! Why?

Well, as the Class1 class inherits from hashtable and hastable implements ISerializable, you need to write the special constructor above, but once you write the constructor, you are telling .net that you will be in charge of saving the data you really want to save… If you save no information for your fields, no data is saved when serialized, so no data is retrieved when deserialized.

The quick (and dirty ;) ) way to solve this: Use reflection to automatically traverse the properties and save them instead of write code to save them one by one:

[Serializable]
class Class1: Hashtable
{
    public Class1(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        FieldInfo[] fields = this.GetFields(this);
        foreach (FieldInfo fieldInfo in fields)
        {
            Object obj = info.GetValue(fieldInfo.Name,fieldInfo.FieldType);
            fieldInfo.SetValue(this,obj);
        }
    }
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        FieldInfo[] fields = this.GetFields(this);
        foreach (FieldInfo fieldInfo in fields)
        {
            info.AddValue(fieldInfo.Name,fieldInfo.GetValue(this));
        }
        base.GetObjectData(info, context);
    }
    public Class1()
    {
    }
    protected int number;
    private int othernumber;
    public string text;

    public int OtherNumber
    {
        get
        {
            return othernumber;
        }
        set
        {
            othernumber = value;
        }
    }

    public int Number
    {
        get
        {
            return number;
        }
        set
        {
            number = value;
        }
    }

    private FieldInfo[] GetFields(object obj)
    {
        FieldInfo[] fields =
        obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        return fields;
    }
}

Note that I’ve include the BindingFlags I’ve added to obtain the fields. This can be modified if other kind of filters are needed.

Friday, 27 April 2007

Identifying if the next/s control in z order hides a given control

This is a quite simple c# class that lets identify if for a certain control, there is another above that hides the control (partially or completely, depending on the option).

I've uploaded a sample project here: Z order sample project

Here is the ZOrder class code:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;

namespace ZOrderFunctions
{
///
/// Defines helper function for testing zorder relationships
///

// By Javier Alvarez, 27 april 2007
class ZOrder
{
#region GetWindow
private const int GW_HWNDNEXT = 2;
private const int GW_HWNDPREV = 3;
private const int GW_CHILD = 5;


[DllImport("User32.dll")]
private static extern IntPtr GetWindow(IntPtr hwndSibling,
int wFlag);
//Parameters
//hWnd
//[in] Handle to a window. The window handle retrieved is relative to this window, based on the value of the uCmd parameter.
//uCmd
//[in] Specifies the relationship between the specified window and the window whose handle is to be retrieved. This parameter can be one of the following values.
//GW_CHILD
//The retrieved handle identifies the child window at the top of the Z order, if the specified window is a parent window; otherwise, the retrieved handle is NULL. The function examines only child windows of the specified window. It does not examine descendant windows.
//GW_ENABLEDPOPUP
//Windows 2000/XP: The retrieved handle identifies the enabled popup window owned by the specified window (the search uses the first such window found using GW_HWNDNEXT); otherwise, if there are no enabled popup windows, the retrieved handle is that of the specified window.
//GW_HWNDFIRST
//The retrieved handle identifies the window of the same type that is highest in the Z order. If the specified window is a topmost window, the handle identifies the topmost window that is highest in the Z order. If the specified window is a top-level window, the handle identifies the top-level window that is highest in the Z order. If the specified window is a child window, the handle identifies the sibling window that is highest in the Z order.
//GW_HWNDLAST
//The retrieved handle identifies the window of the same type that is lowest in the Z order. If the specified window is a topmost window, the handle identifies the topmost window that is lowest in the Z order. If the specified window is a top-level window, the handle identifies the top-level window that is lowest in the Z order. If the specified window is a child window, the handle identifies the sibling window that is lowest in the Z order.
//GW_HWNDNEXT
//The retrieved handle identifies the window below the specified window in the Z order. If the specified window is a topmost window, the handle identifies the topmost window below the specified window. If the specified window is a top-level window, the handle identifies the top-level window below the specified window. If the specified window is a child window, the handle identifies the sibling window below the specified window.
//GW_HWNDPREV
//The retrieved handle identifies the window above the specified window in the Z order. If the specified window is a topmost window, the handle identifies the topmost window above the specified window. If the specified window is a top-level window, the handle identifies the top-level window above the specified window. If the specified window is a child window, the handle identifies the sibling window above the specified window.
//GW_OWNER
//The retrieved handle identifies the specified window's owner window, if any. For more information, see Owned Windows.
#endregion

///
/// Tells if the control is hidden by other in the z order
///

/// Control to be checked
/// if true, a control is returned even if it is only partially hidden, if false, the control must be completely hidden
/// If there is a control above, reference to the control
public static Control IsHidden(Control sender, bool partially)
{
// Check preconditions
if (sender == null) throw new ArgumentException("IsHidden recieved null as parameter");

// 'Found a control' flag
bool ret = false;

// Return value
Control topControl = null;

// Point to the display area rectangle
Rectangle senderRect = sender.DisplayRectangle; //new Rectangle(sRect.X, sRect.Y, sRect.Width, sRect.Height); senderRect.Location.Offset(
senderRect.Offset(sender.Left, sender.Top);

// Iterate through higher z-order controls to see if any above the sender control
IntPtr currentHandler = sender.Handle ;
Control currentControl = null;
do
{
// Get next control
currentHandler = GetWindow(currentHandler, GW_HWNDPREV);

if (currentHandler != IntPtr.Zero)
{
// Get associated rectangle
currentControl = Control.FromHandle(currentHandler);
Rectangle currentRectangle = currentControl.DisplayRectangle;
currentRectangle.Offset(currentControl.Left, currentControl.Top);

// Select if fully contained or partially
if (partially)
{
ret = currentControl.Visible && currentRectangle.IntersectsWith(senderRect);
}
else
{
ret = currentControl.Visible && currentRectangle.Contains(senderRect);
}
// Point to control if it matches the condition
if (ret)
{
topControl = currentControl;
}

}

} while (!ret && currentHandler != IntPtr.Zero);

return topControl;




}
}
}




Build a sample form with three buttons and add the following (or alike) code to test:

public partial class Form1 : Form
{



public Form1()
{
InitializeComponent();
}

private void Test(object sender)
{
Control top = ZOrder.IsHidden(sender as Control, true);
if (top != null)
{
MessageBox.Show("Handle of control above is : " + top.Handle.ToString());
}
else
{
MessageBox.Show("This control is the topmost");
}
}

private void button2_Click(object sender, EventArgs e)
{
Test(sender);
}

private void button1_Click(object sender, EventArgs e)
{
Test(sender);
}

private void button3_Click(object sender, EventArgs e)
{
Test(sender);
}

private void Form1_Load(object sender, EventArgs e)
{
button1.Text = button1.Handle.ToString();
button2.Text = button2.Handle.ToString();
button3.Text = button3.Handle.ToString();
}
}

Thursday, 22 March 2007

Visual studio 2005 T-SQL Debugging "Unable to bind SQL breakpoint at this time"

Did you found the "Unable to bind SQL breakpoint at this time" error when debugging pure T-SQL procedures (not CLR)?

Today I discovered a funny 'feature' of the Visual Studio related to T-SQL debugging (hopefully solved in the sp1...):

Accordingly to one of the developers of the Visual Studio Debugger Team, there is an issue with dead connections in Server explorer:

(see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=840978&SiteID=1&mode=1)

So to be able to step into the procedures from the visual studio, you need to:
a) Delete ALL the connections you may have created
b) Close the Visual Studio
c) Reopen Visual Studio
d) Recreate the connection to the database you want to debug. Make sure credentials you use to connect belong to the sysadmin group of the SQL Server

That's all folks... at least it worked for me.

Anyway... first option is to install SP1 ;o)

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);
}


}
}