Sunday, March 27, 2005

Hierarchical TreeView control with data binding enabled

Some time ago my task was to write something like virtual file system. Of course I decide to use typed DataSets because I am already wrote framework to work and update them easily. With this technology there are very easy to display content of the folder. Relation DataTables are very great tool for this. That’s all right but when I have seen the result - I had died! This is not look and feel that I mentioned for my client! So I am open google and start searching for TreeView with data binding enabled. Of course I found something: this (Data Binding TreeView in C#) is pretty but this is not hierarchy in my understanding; this (How to fill hierarchical data into a TreeView using base classes and data providers) is pretty too but I am not understood why author do not like standard binding. Both are not for me! And as real Ukrainian man I decide try to write my own!

See article on "Code Project".

Tuesday, March 15, 2005

Custom drawing cursors


Some time ago I have decided to try to make something like a "Photoshop" in C#. The first problem has appeared was custom cursors. If you are familiar with "Photoshop" you know that when you change brush size the cursor size also changes. This is solution to make this possible in C# . Note: hare will described only black and white cursors. It is possible that I will upgrade this article or create new for 32-bit cursors.

Standard realization of class Cursor practically completely repeats Win API for the cursor except for method CreateCursor . This method is necessary for drawing custom cursor. So we import it from library user32.dll.

private static extern IntPtr CreateCursor(
    IntPtr hInst,
    int xHotSpot,
    int yHotSpot,
    int nWidth,
    int nHeight,
    byte[] pvANDPlane,
    byte[] pvXORPlane

The method needs handle to the current window, coordinates of the cursor's hot spot, the size and mask of the image.

The sizes of the cursor :

Win API contains method GetSystemMetrics which returns the possible size of cursor, but it always return 32/32 for standard video devices or 64/64 for hi-resolution devices. In the experimental way I have find out that the sizes of the cursor should be multiple 16 px. So we will not use GetSystemMetrics . Attempt of use of the non-standard size leads to wrong display of the image. As greater accuracy is necessary for us than 16 px we shall take the size of the cursor with a stock. All that remains will be in visible so this is for us!

Masks of the cursor:

The image of the cursor is set by means of two masks (AND and XOR masks). Masks consist from array of byte where every b i t designates one pixel. Here results of a combination of two pixels:

AND mask XOR mask Result
0 0 Black
0 1 White
1 0 Screen
1 1 Reverse screen

Result code:

Using CreateCursor function this is simple to create new cursor from masks:

byte[] andMaskCursor = new byte[]
    0xFF, 0xFC, 0x3F, 0xFF,   // line 1 
    0xFF, 0xC0, 0x1F, 0xFF,   // line 2 
    0xFF, 0x00, 0x3F, 0xFF,   // line 3             
    0xFF, 0xC3, 0xFF, 0xFF,   // line 31 
    0xFF, 0xFF, 0xFF, 0xFF    // line 32 

byte[] xorMaskCursor = new byte[]
    0x00, 0x00, 0x00, 0x00,   // line 1 
    0x00, 0x03, 0xC0, 0x00,   // line 2 
    0x00, 0x3F, 0x00, 0x00,   // line 3             
    0x00, 0x00, 0x00, 0x00,   // line 31 
    0x00, 0x00, 0x00, 0x00    // line 32 

IntPtr cursorHandle = CreateCursor(
    handle,             // app. instance 
    radius,             // hot spot horiz pos
    radius,             // hot spot vert pos 
    sideLength,         // cursor width 
    sideLength,         // cursor height 
    andMaskCursor,      // AND mask 
    xorMaskCursor       // XOR mask 

Cursor.Current = new Cursor(cursorHandle);

Huuh.. now we can create cursors programmatically . Masks now are hard coded that is not applicable for us! Lets create mask with circle cursor:

int sideLength = radius*2;          
if (sideLength%16 != 15)
    sideLength = sideLength + 16-sideLength%16;
int length = sideLength*sideLength/8;
byte[] andMaskCursor = new byte[length];
byte[] xorMaskCursor = new byte[length];
for (int i = 0; i < sideLength; i++ )
    for (int j = 0; j < sideLength; j++ )
        double x = i - radius;
        double y = j - radius;
        double pRadius = Math.Pow(
            Math.Pow(x, 2) + Math.Pow(y, 2), 0.5
        if ((int)pRadius != radius)
            int ii = (i*sideLength)+j;
            andMaskCursor[ii/8] =
                (byte)Math.Pow(2, 7-ii%8);

That's all! Enjoy.

Tuesday, March 08, 2005

Beep, MessageBeep

Here is three very simple methods to Beep.

  • Most simple way.
  • Second one is WinAPI MessageBeep function using PInvoke.
    using System;
    using System.Runtime.InteropServices;
    public class _Main
        #region Win32
        static extern void MessageBeep(uint uType); 
        const uint MB_OK                = 0x00000000;
        const uint MB_ICONHAND          = 0x00000010;
        const uint MB_ICONQUESTION      = 0x00000020;
        const uint MB_ICONEXCLAMATION   = 0x00000030;
        const uint MB_ICONASTERISK      = 0x00000040;
        public static void Main()
  • Third is PInvoke too. This is WinAPI Beep function.
    using System;
    using System.Runtime.InteropServices;
    public class _Main
        #region Win32
    static extern bool Beep( uint dwFreq, uint dwDuration ); #endregion public static void Main() { Beep(150, 150); } }


MessageBeep function

The MessageBeep function plays a waveform sound. The waveform sound for each sound type is identified by an entry in the registry.


uType:Sound type, as identified by an entry in the registry.

Beep function

The Beep function generates simple tones on the speaker. The function is synchronous; it does not return control to its caller until the sound finishes.


dwFreq:Frequency of the sound, in hertz. This parameter must be in the range 37 through 32,767 (0x25 through 0x7FFF). dwDuration:Duration of the sound, in milliseconds.

Return Values:

If the function succeeds, the return value is true. If the function fails, the return value is false.