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


}
}

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

}
}