Tuesday, September 13, 2005

BhashaIndia.com :: Multilingual Application Development

BhashaIndia.com :: Multilingual Application Development: "Adding multi-language capabilities other than English should be done from ground up. All the code that represents the language to the user (buttons, menus, prompts, help, text, dialog-boxes, etc.) must be found and altered to support language independence. This problem multiplies exponentially when you deal with applications that have hundreds of thousands lines of code, which have been created over the time and modified constantly by different developers. Hard-coding the language dependent text makes it extremely complex the process of adding multilingual capability for the program. One will have to develop individual application for every language he wants to support. Maintaining such a program is another headache."

.NET Data Access Architecture Guide :Drag-and-Drop Operations

Performing Drag-and-Drop Operations in Windows Forms
Drag-and-Drop Operations and Clipboard Support
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbcondragdropclipboardsupport.asp

Architecture Guide is available at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/daag.asp

Saturday, February 26, 2005

Customizing RedHat Linux Kickstart Installation Cdrom

Customizing RedHat Linux Kickstart Installation Cdrom: "The following document has been created because I've not been able to find a suitable alternative. Certainly there are numerous pages on how to install RedHat Linux using kickstart, but there are nuggets of information here that I still have been unable to find documented elsewhere about making custom boot CDs. "

Red Hat Linux 9 Customization Guide

Red Hat Linux 9: "Red Hat Linux Customization Guide"

YoLinux: Index of Linux Tutorials

YoLinux: Index of Linux Tutorials

Here is a link for Linux tutorials....!

Linux: Init Process and PC Boot Procedure

Linux: Init Process and PC Boot Procedure

Friday, February 25, 2005

Linux Networking Configuration


Adding a network interface card (NIC):
Manual method: This does not alter the permanent configuration and will only configure support until the next reboot.
cd /lib/modules/2.2.5-15/net/ - Use kernel version for your system. This example uses 2.2.5-15 Here you will find the modules supported by your system. It can be permanently added to /etc/modules.conf (or for older systems: /etc/conf.modules) Example: alias eth0 3c59x

/sbin/insmod -v 3c59x (For a 3Com ethernet card)
ifconfig ...
The easy way
: Red Hat versions 6.2 and later, ship with Kudzu, a device detection program which runs during system initialization. (/etc/rc.d/init.d/kudzu) This can detect a newly installed NIC and load the appropriate driver. Then use /usr/sbin/netconfig to configure the IP address and network settings. The configuration will be stored so that it will be utilized upon system boot.
Systems with two NIC cards: Typically two cards are used when connecting to two networks. In this case the device must be defined using one of three methods:
Use the GUI tool /usr/bin/netcfg
OR
Define network parameters in configuration files:
Define new device in file /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
BOOTPROTO=static
IPADDR=192.168.10.12
NETMASK=255.255.255.0
GATEWAY=XXX.XXX.XXX.XXX
HOSTNAME=node-name.name-of-domain.com
DOMAIN=name-of-domain.com
Special routing information may be specified, if necessary, in the file: /etc/sysconfig/static-routes Example:
eth1 net XXX.XXX.XXX.0 netmask 255.255.255.0 gw XXX.XXX.XXX.XXX
OR
Define network parameters using Unix command line interface:
Define IP address: ifconfig eth0 XXX.XXX.XXX.XXX netmask 255.255.255.0 broadcast XXX.XXX.XXX.255
ifconfig eth1 192.168.10.12 netmask 255.255.255.0 broadcast 192.168.10.255

If necessary, define route with with the route command: Examples: route add default gw XXX.XXX.XXX.XXX dev eth0
route add -net XXX.XXX.XXX.0 netmask 255.255.255.0 gw XXX.XXX.XXX.XXX dev eth0
Where XXX.XXX.XXX.XXX is the gateway to the internet as defined by your ISP or network operator.
If a mistake is made just repeat the route command substituting "del" in place of "add".
Configuration files:


File: /etc/resolv.conf - resolver configuration file
search name-of-domain.com - Name of your domain or ISP's domain if using their name server
nameserver XXX.XXX.XXX.XXX - IP address of primary name server
nameserver XXX.XXX.XXX.XXX - IP address of secondary name server
This configures Linux so that it knows which DNS server will be resolving domain names into IP addresses. If using DHCP, this will automatically be sent to you by the ISP and loaded into this file as part of the DHCP protocol. If using a static IP address, ask the ISP or check another machine on your network.

File: /etc/hosts - locally resolve node names to IP addresses
127.0.0.1 your-node-name.your-domain.com localhost.localdomain localhost
XXX.XXX.XXX.XXX node-name
Note when adding hosts to this file, place the fully qualified name first. (It helps sendmail identify your server correctly) i.e.: XXX.XXX.XXX.XXX superserver.yolinux.com superserver
This informs Linux of local systems on the network which are not handled by the DNS server. (or for all systems in your LAN if you are not using DNS or NIS)

/etc/sysconfig/network
Red Hat network configuration file used by the system during the boot process.
/etc/nsswitch In the past this file has had the following names: /etc/nsswitch.conf, /etc/svc.conf, /etc/netsvc.conf, ... depending on the distribution.


File: /etc/nsswitch.conf - System Databases and Name Service Switch configuration file
hosts: files dns nisplus nis
This example tells Linux to first resolve a host name by looking at the local hosts file(/etc/hosts), then if the name is not found look to your DNS server as defined by /etc/resolv.conf and if not found there look to your NIS server.
File: /etc/sysconfig/network-scripts/ifcfg-eth0 Configuration settings for your first ethernet port (0). Your second port is eth1.


File: /etc/modules.conf (or for older systems: /etc/conf.modules)

Example statement for Intel ethernet card:
alias eth0 eepro100
Modules for other devices on the system will also be listed. This tells the kernel which device driver to use if configured as a loadable module. (default for Red Hat)


Network GUI Configuration Tools:
/usr/bin/redhat-config-network (/usr/bin/neat) (RH 7.2+)
/usr/bin/redhat-config-network-tui (text console RH 9.0)
/usr/sbin/netconfig (console. First interface only - eth0)
/usr/bin/netcfg (GUI) (no longer available with RH 7.2)
/usr/bin/redhat-control-network (Start/Stop network interfaces RH 9.0)
redhat-config-network-druid (Configure CIPE,Ethernet,modem,token Ring,Wireless or DSL)
/usr/bin/gnome-network-preferences Gnome Desktop Network Configuration (RH 9.0) - proxy configuration

The Red Hat configuration tools store the configuration information in the file /etc/sysconfig/network. They will also allow one to configure routing information.
File: /etc/sysconfig/network
Static IP address Configuration: (Configure gateway address)
NETWORKING=yes
HOSTNAME=my-hostname - Hostname is defined here and by command hostname
FORWARD_IPV4=true - True for NAT firewall gateways and linux routers. False for everyone else - desktops and servers.
GATEWAY="XXX.XXX.XXX.YYY" - Used if your network is connected to another network or the internet.
Static IP configuration. Gateway not defined here for DHCP.
OR for DHCP configuration:
NETWORKING=yes
HOSTNAME=my-hostname - Hostname is defined here and by command hostname
(Gateway is assigned by DHCP.)
File: /etc/sysconfig/network-scripts/ifcfg-eth0
Static IP address configuration:

DEVICE=eth0
BOOTPROTO=static
BROADCAST=XXX.XXX.XXX.255
IPADDR=XXX.XXX.XXX.XXX
NETMASK=255.255.255.0
NETWORK=XXX.XXX.XXX.0
ONBOOT=yes
OR for DHCP configuration:
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=dhcp
(Used by script /etc/sysconfig/network-scripts/ifup to bring the various network interfaces on-line) To disable DHCP change BOOTPROTO=dhcp to BOOTPROTO=none
In order for updated information in any of these files to take effect, one must issue the command: service network restart

Changing the host name:
This is a three step process:
Issue the command: hostname new-host-name
Change network configuration file: /etc/sysconfig/network Edit entry: HOSTNAME=new-host-name
Restart systems which relied on the hostname (or reboot):
Restart network services: service network restart
Restart desktop:
Bring down system to console mode: init 3
Bring up X-Windows: init 5 One may also want to check the file /etc/hosts for an entry using the system name which allows the system to be self aware.


Usefull Linux networking commands:
/etc/rc.d/init.d/network start - command to start, restart or stop the network
netstat - Display connections, routing tables, stats etc
List externally connected processes: netstat -punta
List all connected processes: netstat -nap
Show network statistics: netstat -s
Kernel interface table info: netstat -a -i eth0
ping - send ICMP ECHO_REQUEST packets to network hosts. Use Cntl-C to stop ping.
traceroute - print the route packets take to network host
mtr - a network diagnostic tool - Like traceroute except it gives more network quality and network diagnostic info.
whois - Lookup a domain name in the internic whois database.
finger - Display information on a system user. i.e. finger user@host Uses $HOME/.plan and $HOME/.project user files. Often used by game developers. See http://finger.planetquake.com/
ipchains - IP firewall administration
tcpdump - dump traffic on a network
iptraf - Interactive Colorful IP LAN Monitor
socklist - Display list of open sockets, type, port, process id and the name of the process. Kill with fuser or kill.
nslookup - Give a host name and the command will return IP address. Also see Testing your DNS (YoLinux Tutorial) Note that nslookup does not use the /etc/hosts file.
host - Give a host name and the command will return IP address. Unlike nslookup, the host command will use both /etc/hosts as well as DNS.
nmap - Network exploration tool and security scanner
List pingable nodes on network: nmap -sP 192.168.0.0/24 Scans network for IP addresses 192.168.0.0 to 192.168.0.255 using ping.

Thursday, February 24, 2005

Using Grep - the Ultimate Filtering Tool

This page contains a description of how to filter text files and/or text output to the terminal screen.
Command Syntax
grep [options] PATTERN [file....]
Grep searches the specified input FILE (or standard input is no files are specified, such as if you pipe something to grep) for lines that contain a match to the pattern. By default, the matches are printed to the screen. You can also encapsulate the pattern in quotes ("PATTERN").
Options

-a, --text : This option allows you to process a binary file as if it was a normal text file. This is equivalent to the --binary-files=text option.
-c, --count : Suppress the normal output. Instead print the number of matches found.
-h, --no-filename : Suppress the prefixing of filenames on output when multiple files are searched.
-i, --ignore-case : ignore case in the pattern and file names
-l, --files-with-matches : Suppress normal output; instead print the name of each input file from which output would normally have been printed. The scanning will stop on the first match.
Examples
Example: In this example we search the file called README.txt for lines that contain the word 'help'
grep "help" README.txt
Example: In this case, no file is specified. We will search the directory /etc for all files that contain the word "cron" in the filename.
ls /etc grep cron
Basically, what we just did is perform a directory listing, only displaying lines that contain "cron". Using the "" (pipe), the output from the ls command becomes the input for grep.
Advanced Grep Usage

Grep can also be piped multiple times. For example, the output of one grep command can be the input for the second one, and so on.
For example, we want to check the mail log for all emails that were sent from ernie@company.com. First we "cat" the maillog, which would output every line in the file to the screen. This is more information than we want, so we pass the output along to our first grep command. We search for all lines that contain "ernie". The -i option is used to ignore the case. So far, the syntax would be:

cat /var/log/maillog grep -i ernie

We are partially there. The result from this command outputs every line containing "ernie". But this also includes all places where ernie was the recipient as well as the sender. Next we filter out only the places where he was the sender:

cat /var/log/maillog grep -i ernie grep from

The output from this command lists all places in the mail log pertaining to ernie sending an email.

Josh's Example:
cat maillog grep "Dec 11" egrep "TO:<\w+@domain.com" -c
First I filtered out the maillog for entries of a certain day, then I filtered for entries:TO:ANYUSER@company.com
the -c command will only display the number of occurrences.

Linux Run-Levels

Linux has 6 different run levels (or operating modes):

rc1.d - Single User Mode
rc2.d - Single User Mode with Networking
rc3.d - Multi-User Mode - boot up in text mode
rc4.d - Not yet Defined
rc5.d - Multi-User Mode - boot up in X Windows
rc6.d - Shutdown

Each mode has it's own list of settings for what services to start and what services to shutdown. Not only does this list contain what is supposed to be running, but also what order each service should be started in.
chkconfig - will list all the registered services and display thier status at each different run level.
Most of the time when your server boots up it will start in runlevel 3 or 5 depending on what it is set to. This setting can be found in the file /etc/inittab
If you ever want to change what mode your server defaults to on bootup, just edit that file, but NEVER change it to runlevel 6 or else you will set your server to shutdown on bootup.
RC.D Directory Explanation
In the folder: /etc/rc.d are all the run level folders starting from rc1.d to rc6.d and including init.d
It is in each of these rc#.d folders where the service run settings are kept. If you change directory into /etc/rc.d/rc3.d all the files that start with a capital S are the services that will start at this runlevel. All the files that start with a K are the services that will be killed at that runlevel.
The reason why services that are not supposed to run are still listed is because it is popular to switch a server from one run level to another instead of just booting into the needed run level. That way you can take your server offline (single user mode) and then apply a patch or do a file restore.
Let's take a closer look at these files that are inside the rc#.d folders. Notice that they are symbolic links to the script file located in /etc/rc.d/init.d This makes things very easy. All the commands to start, stop, or reload a service are handled by these scripts. So when a server is to go into a specific run level it can find the correct script and knows whether to start it based on its name and link path.
Switching Run Levels
init

Run levels are configured in /etc/inittab by lines like the following:

l2:2:wait:/etc/init.d/rc 2
The first field is an arbitrary label, the second one means that this applies for run level 2. The third field means that init should run the command in the fourth field once, when the run level is entered, and that init should wait for it to complete. The /etc/init.d/rc command runs whatever commands are necessary to start and stop services to enter run level 2.
The command in the fourth field does all the hard work of setting up a run level. It starts services that aren't already running, and stops services that shouldn't be running in the new run level any more. Exactly what the command is, and how run levels are configured, depends on the Linux distribution.

When init starts, it looks for a line in /etc/inittab that specifies the default run level:
id:2:initdefault:
You can ask init to go to a non-default run level at startup by giving the kernel a command line argument of single or emergency. Kernel command line arguments can be given via LILO, for example. This allows you to choose the single user mode (run level 1).
While the system is running, the telinit command can change the run level. When the run level is changed, init runs the relevant command from /etc/inittab.


The kernel, once it is loaded, finds init in sbin and executes it.
When init starts, it becomes the parent or grandparent of all of the processes that start up automatically on your Linux system. The first thing init does, is reading its initialization file, /etc/inittab. This instructs init to read an initial configuration script for the environment, which sets the path, starts swapping, checks the file systems, and so on. Basically, this step takes care of everything that your system needs to have done at system initialization: setting the clock, initializing serial ports and so forth.
Then init continues to read the /etc/inittab file, which describes how the system should be set up in each run level and sets the default run level. A run level is a configuration of processes. All UNIX-like systems can be run in different process configurations, such as the single user mode, which is referred to as run level 1 or run level S (or s). In this mode, only the system administrator can connect to the system. It is used to perform maintenance tasks without risks of damaging the system or user data. Naturally, in this configuration we don't need to offer user services, so they will all be disabled. Another run level is the reboot run level, or run level 6, which shuts down all running services according to the appropriate procedures and then restarts the system

Commonly, run level 3 is configured to be text mode on a Linux machine, and run level 5 initializes the graphical login and environment.

After having determined the default run level for your system, init starts all of the background processes necessary for the system to run by looking in the appropriate rc directory for that run level. init runs each of the kill scripts (their file names start with a K) with a stop parameter. It then runs all of the start scripts (their file names start with an S) in the appropriate run level directory so that all services and applications are started correctly. In fact, you can execute these same scripts manually after the system is finished booting with a command like /etc/rc.d/init.d/httpd stop or service httpd stop logged in as root, in this case stopping the web server.
None of the scripts that actually start and stop the services are located in /etc/rc.d/rc.d. Rather, all of the files in /etc/rc.d/rc.d are symbolic links that point to the actual scripts located in /etc/rc.d/init.d. A symbolic link is nothing more than a file that points to another file, and is used in this case because it can be created and deleted without affecting the actual scripts that kill or start the services. The symbolic links to the various scripts are numbered in a particular order so that they start in that order. You can change the order in which the services start up or are killed by changing the name of the symbolic link that refers to the script that actually controls the service. You can use the same number multiple times if you want a particular service started or stopped right before or after another service.

Below a sample of /etc/rc5.d content, which lists the services started up in run level 5:
[jean@blub /etc/rc5.d] ls
K15httpd@ K45named@ S08ipchains@ S25netfs@ S85gpm@
K16rarpd@ K46radvd@ S08iptables@ S26apmd@ S90crond@
K20nfs@ K61ldap@ S09isdn@ S28autofs@ S90xfs@
K20rstatd@ K65identd@ S10network@ S30nscd@ S95anacron@
K20rusersd@ K74ntpd@ S12syslog@ S55sshd@ S95atd@
K20rwalld@ K74ypserv@ S13portmap@ S56rawdevices@ S97rhnsd@
K20rwhod@ K74ypxfrd@ S14nfslock@ S56xinetd@ S99local@
K25squid@ K89bcm5820@ S17keytable@ S60lpd@
K34yppasswdd@ S05kudzu@ S20random@ S80sendmail@

After init has progressed through the run levels to get to the default run level, the /etc/inittab script forks a getty process for each virtual console (login prompt in text mode). getty opens tty lines, sets their modes, prints the login prompt, gets the user's name, and then initiates a login process for that user. This allows users to authenticate themselves to the system and use it. By default, most systems offer 6 virtual consoles, but as you can see from the inittab file, this is configurable.
/etc/inittab can also tell init how it should handle a user pressing Ctrl+Alt+Delete at the console. As the system should be properly shut down and restarted rather than immediately power-cycled, init is told to execute the command /sbin/shutdown -t3 -r now, for instance, when a user hits those keys. In addition, /etc/inittab states what init should do in case of power failures, if your system has a UPS unit attached to it.
In run level 5, /etc/inittab runs a script called /etc/X11/prefdm. The prefdm script runs the preferred X display manager, based on the contents of the /etc/sysconfig/desktop directory. This is typically gdm if you run GNOME or kdm if you run KDE, but they can be mixed, and there's also the xdm that comes with a standard X installation.
The /etc/sysconfig directory contains entries for a range of functions and services, these are all read at boot time. This last part, however, might be somewhat different depending on your Linux distribution.
Besides the graphical user environment, a lot of other services may be started as well. But if all goes well, you should be looking at a login prompt or login screen when the boot process has finished.

Monday, February 14, 2005

Disable the close button on a Windows Form

“How do I disable the close button on a Windows Form, but still have the system menu, minimize and maximize available?”.

CloseButtonRemove.vb

Option Explicit On
Option Strict On

Imports System.Runtime.InteropServices

Public Class CloseButtonRemove

Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Integer, ByVal revert As Integer) As Integer
Private Declare Function GetMenuItemCount Lib "user32" (ByVal menu As Integer) As Integer
Private Declare Function RemoveMenu Lib "user32" (ByVal menu As Integer, ByVal position As Integer, ByVal flags As Integer) As Integer
Private Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Integer) As Integer
Private Declare Function FormatMessageA Lib "kernel32" (ByVal flags As Integer, ByRef source As Object, ByVal messageID As Integer, ByVal languageID As Integer, ByVal buffer As String, ByVal size As Integer, ByRef arguments As Integer) As Integer


Private Const MF_BYPOSITION As Integer = &H400
Private Const MF_DISABLED As Integer = &H2

Private Sub New()
End Sub

Public Shared Sub Disable(ByVal form As System.Windows.Forms.Form)
' Get handle to system menu for the form provided.
Dim menu As Integer = GetSystemMenu(form.Handle.ToInt32, 0)
' Get number of items in this system menu.
Dim count As Integer = GetMenuItemCount(menu)
' Remove last item from system menu (last item should be ’Close’).
If RemoveMenu(menu, count - 1, MF_DISABLED Or MF_BYPOSITION) = 0 Then
Throw New Exception(FormatMessage(Err.LastDllError))
Else
' On success, force a redraw of the system menu.
If DrawMenuBar(form.Handle.ToInt32) = 0 Then
Throw New Exception(FormatMessage(Err.LastDllError))
End If
End If
End Sub

Public Shared Function FormatMessage(ByVal [error] As Integer) As String
Const FORMAT_MESSAGE_FROM_SYSTEM As Short = &H1000
Const LANG_NEUTRAL As Short = &H0
Dim buffer As String = Space(999)
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, [error], LANG_NEUTRAL, buffer, 999, 0)
buffer = Replace(Replace(buffer, Chr(13), ""), Chr(10), "")
Return buffer.Substring(0, buffer.IndexOf(Chr(0)))
End Function
End

Class
I've attempted to document the code fully so that it's easier to see what is going on. Also, bear in mind that most of the sample code floating around on the net doesn't take into account possible errors occuring doing this task in more of a brute force method. Based on the SDK documentation, I've added error checking where appropriate. Also, take note of the FormatMessage function that is included in this class. It may come in handy when your working with other Win32 Interop functions as it will (usually) return a string version of a numeric error value.

So, you might ask, what's the problem with this code? It does what was asked. Actually, it doesn't. The question was “How do I ***disable*** the close button?”; which would mean disabling the 'close' menu item as well... not deleting it from the menu.

The following code shows how to disable the close button according to the SDK documentation.

CloseButton.vb

Option Explicit On
Option Strict On

Public

Class CloseButton

Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Integer, ByVal revert As Integer) As Integer
Private Declare Function EnableMenuItem Lib "user32" (ByVal menu As Integer, ByVal ideEnableItem As Integer, ByVal enable As Integer) As Integer

Private Const SC_CLOSE As Integer = &HF060
Private Const MF_BYCOMMAND As Integer = &H0
Private Const MF_GRAYED As Integer = &H1
Private Const MF_ENABLED As Integer = &H0

Private Sub New()
End Sub

Public Shared Sub Disable(ByVal form As System.Windows.Forms.Form)
' The return value specifies the previous state of the menu item (it is either
' MF_ENABLED or MF_GRAYED). 0xFFFFFFFF indicates that the menu item does not exist.
Select Case EnableMenuItem(GetSystemMenu(form.Handle.ToInt32, 0), SC_CLOSE, MF_BYCOMMAND Or MF_GRAYED)
Case MF_ENABLED
Case MF_GRAYED
Case &HFFFFFFFF
Throw New Exception("The Close menu item does not exist.")
Case Else
End Select
End Sub
End

Class
As you can see, we now only require two API declarations. Also, there are not LastDllError checks necessary in order to determine the error since our single call returns one of three values as described by the SDK documentation.

To use this code within your Windows Form application, just import one of the classes into your code and add the CloseButton.Disable(Me) line to your Form1_Load (as an example). For example:



Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
CloseButton.Disable(Me)
End Sub

You will also need to add the following code to your form if you will be allowing the form to minimize or maximize:

Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
CloseButton.Disable(Me)
End Sub

.NET Streams Explained

.NET Streams Explained by Wei-Meng Lee -- On of the common challenges facing beginning .NET programmers is the large number of classes in the .NET Class Library. Looking for the correct class to use for file handling can be confusing. There are many classes in the System.IO namespace, each looking not much different from the others. Wei-Meng Lee explains the differences between the Stream classes.

Friday, February 11, 2005

NASA Brain Bites - Munchies For Your Mind

NASA Brain Bites - Munchies For Your Mind: "Have you ever wondered what space is REALLY like, how astronauts overcome gravity to train for weightlessness or how you'd turn a bolt in space? NASA Brain Bites will give your mind something to munch on! "

Thursday, February 10, 2005

Windows Forms FAQ

Windows Forms FAQ

Here you can get all FAQs Regading Windows Forms, Controls and lot more stuff..Very useful contents for Developers

Wednesday, February 09, 2005

DataGrid: Tailor Your DataGrid Apps Using Table Style and Custom Column Style Objects -- MSDN Magazine, August 2003

DataGrid: Tailor Your DataGrid Apps Using Table Style and Custom Column Style Objects -- MSDN Magazine, August 2003: " The Windows Forms DataGrid control gives developers a powerful and flexible tool to meet this challenge. This article explains its basic operations and shows how to extend the DataGrid to display columns of data in an application-appropriate manner."