Earlier today i read Code 39 barcodes in C# by Bart De Smet. The article shows the reader how to generate a Code 39 barcode. I thought it would be cool if you could actually recognize the barcode in the generated bitmaps so i last couple of hours i wrote a BarCode39 Reader
Feel free to get BarCode39Reader.zip sources now!(PS: I forgot to write this is still a very quick ‘n dirty hack. I don’t write bad code, just create a lot of opportunities to refactor
Monthly Archives: September 2006
Code 39 barcodes recognizer
Inconsistent Drag and Drop API
Every System.Windows.Forms.Control has the following events:
- public event DragEventHandler DragDrop
- public event DragEventHandler DragEnter
- public event EventHandler DragLeave
- public event DragEventHandler DragOver
This means, when the user drags something away of the control, you can’t access the data anymore (unless you cached it somewhere when the DragEnter or DragOver events occured). Here is an extract from Microsoft patterns and practives on Event Design:
If you define an event that takes an EventArgs instance instead of a derived class that you define, you cannot add data to the event in later versions. For that reason, it is preferable to create an empty derived class of EventArgs. This allows you add data to the event in later versions without introducing breaking changes.
DataGridView with EditOnEnter as EditMode
Earlier this week i found that when a DataGridView has it’s EditMode property set to EditOnEnter the user cannot select an entire row by clicking on the row header. This prevents the user from being able to delete a row. A couple of websearches later i found a bugreport but the proposed workarounds didn’t work for me
Here is a workaround that does work for me:
private void dataGridView1_MouseClick( object sender, MouseEventArgs e ) {
DataGridView.HitTestInfo hitInfo = this.dataGridView1.HitTest(e.X, e.Y);
if( hitInfo.Type == DataGridViewHitTestType.RowHeader ) {
this.dataGridView1.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
this.dataGridView1.EndEdit();
}
else
{
this.dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
}
}
Drag and Drop on a DataGridView
Here is a bit of sample code that allows you to drag and drop a cellvalue in a DataGridView (don’t forget to set the AllowDrop property of the DataGridView to true). Notice that you need to translate the X and Y properties of the DragEventArgs first (unlike the X and Y properties of the MouseEventArgs):
private void dataGridView1_MouseDown( object sender, MouseEventArgs e )
{
DataGridView.HitTestInfo info = this.dataGridView1.HitTest( e.X, e.Y );
if ( info.RowIndex != -1 && info.ColumnIndex != -1 )
{
Object value = this.dataGridView1.Rows[info.RowIndex].Cells[info.ColumnIndex].Value;
if ( value != null )
{
this.dataGridView1.Rows[info.RowIndex].Cells[info.ColumnIndex].Value = null;
this.DoDragDrop( value, DragDropEffects.Move );
}
}
}
private void dataGridView1_DragDrop( object sender, DragEventArgs e )
{
Point p = this.dataGridView1.PointToClient( new Point( e.X, e.Y ) );
DataGridView.HitTestInfo info = this.dataGridView1.HitTest( p.X, p.Y );
if ( info.RowIndex != -1 && info.ColumnIndex != -1 )
{
Object value = (Object)e.Data.GetData( typeof( string ) );
this.dataGridView1.Rows[info.RowIndex].Cells[info.ColumnIndex].Value = value;
}
}
private void dataGridView1_DragEnter( object sender, DragEventArgs e )
{
e.Effect = DragDropEffects.Move;
}
Anonymous methods
Suppose you add a couple of buttons to a panel as shown below. What do you think the message in the MessageBoxes will be?
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
for (int i = 0; i < 10; ++i) {
Button button = new Button();
button.Text = String.Format("{0:00}", i);
this.flowLayoutPanel1.Controls.Add( button );
button.Click += new EventHandler(delegate(Object sender, EventArgs e) {
MessageBox.Show(String.Format("You clicked button {0:00}", i));
});
}
}
}
In case you’re wondering why they all have the message “You clicked button 10″ i suggest you read the following articles:
Adding DataGridViewColumns (lots of them)
Last couple of days i’ve been trying to add a couple (750+) columns into a DataGridView. Initially i tried the following:
this.dataGridView1.ColumnCount = 750;
The code above results in the following error: Sum of the columns’ FillWeight values cannot exceed 65535. Then i tried the following:
DataGridViewColumn[] columns = new DataGridViewColumn[750];
for ( int i = 0; i < columns.Length; ++i )
{
DataGridViewColumn column = new DataGridViewColumn();
column.CellTemplate = new DataGridViewTextBoxCell();
column.FillWeight = 1;
columns[i] = column;
}
this.dataGridView1.Columns.AddRange( columns );
This results in the following error: At least one of the DataGridView control’s columns has no cell template. Thus i tried the following:
DataGridViewColumn[] columns = new DataGridViewColumn[750];
for ( int i = 0; i < columns.Length; ++i )
{
DataGridViewColumn column = new DataGridViewTextBoxColumn();
column.FillWeight = 1;
columns[i] = column;
}
this.dataGridView1.Columns.AddRange( columns );
This code works but the AddRange call took about 15 seconds to complete. With the aid of a collegue and Reflector i set the ColumnHeadersHeightSize to DisableResizing. This reduced the calltime to less than 0.5 seconds
Using DataGridViewComboBoxColumn with Custom Objects
Earlier today i was playing with the DataGridView control. I wanted to have a couple of DataGridViewComboBoxColumns in order to limit the available input options for the user. The documentation clearly mentions the following:
The DataGridViewComboBoxColumn will only work properly if there is a mapping between all its cell values that are populated by the DataGridView.DataSource property and the range of choices populated either by the DataSource property or the Items property. If this mapping doesn’t exist, the message “An Error happened Formatting, Display” will appear when the column is in view.
Here is sample of a custom object:
public class Slot {
private int id;
private DateTime dateTime;
public Slot( int id, DateTime dateTime ) {
this.id = id;
this.dateTime = dateTime;
}
public int Id {
get { return this.id; }
}
public DateTime DateTime {
get { return this.dateTime; }
}
}
And here is the workaround for a one to one mapping:
public partial class Form1 : Form {
// here we'll store the value the user selected in one of the comboboxcolumns
private Object selectedValue;
public Form1() {
InitializeComponent();
selectedValues = new Object[this.dataGridView1.Columns.Count];
// create a couple of slots an add them to the comboboxcolumns
for ( int i = 0; i < 10; ++i ) {
Slot slot = new Slot( i, DateTime.Now.AddDays( i ) );
this.Column1.Items.Add( slot );
this.Column2.Items.Add( slot );
}
this.Column1.DisplayMember = "DateTime";
this.Column2.DisplayMember = "Id";
}
private void dataGridView1_CellParsing( object sender, DataGridViewCellParsingEventArgs e ) {
// lookup the selected value
e.Value = this.selectedValue;
e.ParsingApplied = true;
}
private void dataGridView1_EditingControlShowing( object sender, DataGridViewEditingControlShowingEventArgs e ) {
ComboBox cb = e.Control as ComboBox;
if ( cb != null ) {
cb.SelectedIndexChanged -= cb_SelectedIndexChanged;
cb.SelectedIndexChanged += cb_SelectedIndexChanged;
}
}
void cb_SelectedIndexChanged(object sender, EventArgs e) {
ComboBox comboBox = sender as ComboBox;
this.selectedValue = comboBox.SelectedItem;
}
}
Implementing Master/Detail for Custom Objects with DataGridViewComboBoxColumns
Imagine you have the following two classes:
public class Parent {
private int id;
private string name;
public Parent( int id, string name ) {
this.id = id;
this.name = name;
}
public int Id {
get { return this.id; }
}
public string Name {
get { return this.name; }
}
}
public class Child : Parent {
private int parentId;
public Child( int id, int parentId, string name )
: base( id, name ) {
this.parentId = parentId;
}
public int ParentId {
get { return this.parentId; }
}
}
In the first ComboBoxColumn you display a list of possible Parents. In the second ComboBoxColumn you display Children, but only those that belong to the Chosen Parent. Here is how it goes:
public partial class Form1 : Form {
private Object selectedValue;
public Form1() {
InitializeComponent();
// Add a couple of Parents
for ( int i = 0; i < 3; ++i ) {
Parent parent = new Parent( i, String.Format( "Parent{0:00}", i ) );
this.Column1.Items.Add( parent );
// Add a couple of Children to each parent
for ( int j = 0; j < 5; ++j ) {
Child child = new Child( j, i, String.Format( "Child{0:00}", i * 10 + j ) );
this.Column2.Items.Add( child );
}
}
this.Column1.DisplayMember = "Name";
this.Column2.DisplayMember = "Name";
}
private void dataGridView1_CellParsing( object sender, DataGridViewCellParsingEventArgs e ) {
e.Value = this.selectedValue;
e.ParsingApplied = true;
}
private void dataGridView1_EditingControlShowing( object sender, DataGridViewEditingControlShowingEventArgs e ) {
ComboBox cb = e.Control as ComboBox;
if ( cb != null ) {
// remove all the children that do not belong to the choosen parent
int currentColumnIndex = this.dataGridView1.CurrentCell.ColumnIndex;
if ( currentColumnIndex == 1 ) {
cb.Items.Clear();
int currentRowIndex = this.dataGridView1.CurrentCell.RowIndex;
Object currentCellValue = this.dataGridView1.Rows[currentRowIndex].Cells[0].Value;
if ( currentCellValue != null ) {
int parentId = ( (Parent)currentCellValue ).Id;
foreach ( Child child in this.Column2.Items ) {
if ( child.ParentId == parentId ) {
cb.Items.Add( child );
}
}
}
}
cb.SelectedIndexChanged -= cb_SelectedIndexChanged;
cb.SelectedIndexChanged += cb_SelectedIndexChanged;
}
void cb_SelectedIndexChanged(object sender, EventArgs e) {
ComboBox comboBox = sender as ComboBox;
this.selectedValue = comboBox.SelectedItem;
}
}
Playing with VMWare Server
Earlier today i decided to add an image to my VMWare Server. The creation of the image went smooth but at the end of the process i recieved the following error: Unable to add virtual machine ‘E:\\…..’ to the inventory: Virtual machine is already in the inventory”. When i tried to open the machine via File > Open i recieved the error again. Here is a workaround for this problem: Choose ‘View’ in the menubar and click on Inventory F9. At the left of the screen you’ll see a list of machines that are in the Inventory. If you delete the entries (right click on the machines in the list and choose delete) you’ll be able to Open your newly created machine
List the month names
As i wrote in Generate a menu with month names it’s silly to hardcode month names. Here’s a C# sample using localization/globalization:
using System;
using System.Globalization;
using System.Threading;
namespace ConsoleApplication1 {
class Program {
static void ListMonths() {
for ( int i = 1; i < 13; ++i ) {
DateTime dateTime = new DateTime( DateTime.Now.Year, i, 1 );
Console.WriteLine( dateTime.ToString( "MMMM" ) );
}
Console.WriteLine();
}
static void Main( string[] args ) {
Thread.CurrentThread.CurrentCulture = new CultureInfo( "en-US", false );
ListMonths();
Thread.CurrentThread.CurrentCulture = new CultureInfo( "nl-BE", false );
ListMonths();
Console.Write( "{0}Press any key to continue...", Environment.NewLine );
Console.ReadKey();
}
}
}