<$BlogRSDURL$>
My Blog Links
Site Links
Currently Reading
Blogroll
Archives
Tools
  • This page is powered by Blogger. Isn't yours?
  • Weblog Commenting and Trackback by HaloScan.com
  • 2RSS.com :: RSS directory
  • Listed on BlogShares
  • Blog Directory, Find A Blog, Submit A Blog, Search For The Best Blogs

Wednesday, March 31, 2004

Hey Don't Have a Google, Man. 

Saw this some time last week, but didn't get a chance to post it. It's great though...



On a related note, check out my profile on GotDotNet.

Monday, March 29, 2004

The Power of Blogs 

Well, I've been struggling with getting GotDotNet workspaces WinForm source client to work. For the last two months, I've had no luck getting it running even with forum exchanges with folks from the GotDotNet team. Just the other day, I posted a blog entry about this problem linked to the entry that Craig made about leaving GotDotNet for SourceForge.

And, lo and behold, Andy Oakley, from the GotDotNet team, pointed me at a workspace that has a stand-alone version of the WinForms client. I installed it; it worked perfectly; and I even joined the workspace to help out how ever I can to keep this tool around.

It's amazing how a blog entry tied to the right topic can get you the answer you need... :)

XNA For Developing Games 

A new platform, called XNA, was recently announced for developing Games for the XBox, Windows platform, and Windows devices. Its some libraries for common controls and Live support and services for games. I can't wait to see what this looks like -- should be useful to get more games developed for XBox faster. And, hopefully games that are easily developed cross platform for Windows and the XBox.

I love my XBox and play it a lot. But, is it just me, or does it seem like fewer computer games are being developed than console games?

Driving Blog Traffic 

Here's an insightful article about how to increase the reach of your blog by Dave Pollard. Most of the items on his top 10 were really good and some were even things I've been trying to do.

Also, this gives me the opportunity to link to his article and see if one of his theories for improving blog traffic works... :)

Friday, March 26, 2004

Teen who battled Microsoft donates defense fund to charity 

Mike Rowe is donating his legal defense fund to a hospice for terminally ill children.
Good for him!

ArrayList with Events (Part 3) 

Now, let's put it all together and see how we handle events from EventedArrayList and EventedListItems in a sample application.

The sample test application that I created contains a ListView which is updated only in response to events from EventedArrayList and EventedListItem classes. It also has several buttons that directly operate on the ArrayList (things like add an item, remove an item, clear the whole list, and update item data).

Let's have a look at the EventedListItem-derived class for my test application. This contains the data and functionality specific for my application -- granted that it's rather simple in this scenario.

private class MyListViewItemData : EventedListItem
{
private string name = "";
private int count = 0;

#region public properties
public string Name
{
get { return name; }
set
{
string oldValue = name;
name = value;
FireAllChangedEvents(oldValue, value);
}
}

public int Count
{
get { return count; }
set
{
int oldValue = count;
count = value;
FireAllChangedEvents(oldValue, value);
}
}
#endregion

public MyListViewItemData(string name, int count)
{
this.name = name;
this.count = count;
}

public MyListViewItemData()
{
}
}

 


Again, pretty simple. It just provides 2 properties that I use to display in the ListView. Each property's setter calls FireAllChangedEvents to fire the appropriate event through any EventedArrayList that this item is associated with.

This could be made more complex: to only fire one event for all the changes, or even one event for changes across multiple items (if you had some sort of transacted system). But, for our purposes, just the basic implementation.

Now, let's look at how we register for the events that we're interested in:

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

itemsList.ArrayItemsAdded +=
new EventedArrayList.arrayItemsAdded(this.itemsList_Added);
itemsList.ArrayItemsRemoved +=
new EventedArrayList.arrayItemsRemoved(this.itemsList_Removed);
itemsList.ArrayItemsChanged +=
new EventedArrayList.arrayItemsChanged(this.itemsList_Changed);
}

 


It works the same way as any other event registration that you're familiar with in .NET: just create an instance of your handler with the method in your class, and add it to the event handler list.

Here's the code that handles these events for the Form:

private void itemsList_Added(object sender, ArrayListEventArgs e)
{
foreach(MyListViewItemData item in e.Items)
{
MappedListViewItem newItem =
new MappedListViewItem(
new string[] {item.Name, item.Count.ToString()});
newItem.MappedDataObject = item;
this.listViewEntries.Items.Add(newItem);
}
}

private void itemsList_Removed(object sender, ArrayListEventArgs e)
{
foreach(MyListViewItemData dataObj in e.Items)
{
ListViewItem itemFound = FindListViewItem(dataObj);
if (itemFound != null)
{
// only try to remove the item if its still in the listview.
this.listViewEntries.Items.Remove(itemFound);
}
}
}

private void itemsList_Changed(object sender, ArrayListEventArgs e)
{
foreach(MyListViewItemData dataObj in e.Items)
{
ListViewItem itemFound = FindListViewItem(dataObj);
if (itemFound != null)
{
// only try to update the item if its still in the listview.
itemFound.Text = dataObj.Name;
itemFound.SubItems[1].Text = dataObj.Count.ToString();
}
}
}

 


Pretty straight-forward stuff: just go through the list of items in the EventArgs, and depending on which event you're handling, either add, remove, or update the corresponding item in the ListView.

You'll notice a MappedListViewItem class being used. I just created this class to map a ListView item with its backing data object. It derives from ListViewItem and just adds a MappedDataObject property. This allows you to cache away which object is associated with that particular ListView item, which will usually be needed in event handling for the ListView. I won't go over that code here, but you can look at it in the sample download if you're interested in what it does.

So, that's it -- an ArrayList that fires events, items in that list which also notify observers when they've changed, and sample code of user interface that responds to those events and updates appropriately.

You can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.

ArrayList with Events (Part 2) 

Now that the ArrayList fires events, what about when the data within one of its contained components changes. Any UI you have (or any other listener for that matter) would need to be updated. So, we need events fired when that happens too. The problem is that the ArrayList doesn't know when you've changed the data object itself because lots of time, you call it like this:
         myArrayList[index].Name = "new name";
All the ArrayList knows is that the object has been accessed, but not why. So, the object itself needs to be able to fire the change event. To do this, we exposed a public ArrayItemsChanged event and OnArrayItemsChanged method, so that it can be called from the object not just within the EventedArrayList.

Note: I tried using a static ArrayItemsChanged event on the EventedListItem class itself so that any instance of it could fire this event. But, then the listeners list would be static too, so any item change would fire the event to all listeners regardless of what instance of the ArrayList is was in. That wasn't exactly what I wanted because it could cause a lot of "noise" events to be fired, and the handlers would need more intelligent code to figure out whether or not it cared about that particular object's event. I decided to let the ArrayList manage the listener list because then you'd only have to connect to the right instance of the ArrayList rather than each individual instance of an object that you wanted to listen to.


So, now the problem was for an object to fire the event, it needs a pointer back to the appropriate EventedArrayList that it belongs to, so I created the following interface and class to handle that:

///
/// Interface required for data objects that want to fire events whenever their
/// data changes.
///

public interface IEventedListItem
{
// used to associate an ArrayList with this item.
void AssociateWith(EventedArrayList parent);

// checks if the specified ArrayList is associated with this item.
bool IsAssociatedWith(EventedArrayList parent);

// used to disassociate the specified ArrayList with this item.
void DisassociateFrom(EventedArrayList parent);

// used as helper method to fire events to all associated ArrayLists.
void FireAllChangedEvents(object oldValue, object newValue);
}

///
/// EventedListItem are data objects that use the default implementation of
/// IEventedListItem to fire events whenever their data changes.
///

public abstract class EventedListItem : IEventedListItem
{
private ArrayList parents = new ArrayList();

public EventedListItem()
{
}

public void AssociateWith(EventedArrayList parent)
{
if (parent != null)
{
// only add the parent if it's not null, and it's not already
// associated with this item.
if (this.IsAssociatedWith(parent) == false)
{
this.parents.Add(parent);
}
}
}

public bool IsAssociatedWith(EventedArrayList parent)
{
return this.parents.Contains(parent);
}

public void DisassociateFrom(EventedArrayList parent)
{
if (parent != null)
{
// only remove the parent if it's not null, and it is already
// associated with this item.
if (this.IsAssociatedWith(parent) == true)
{
this.parents.Remove(parent);
}
}
}

public void FireAllChangedEvents(object oldValue, object newValue)
{
if (oldValue != newValue)
{
ArrayListEventArgs e = new ArrayListEventArgs(this);
foreach (EventedArrayList parent in this.parents)
{
// then, use parents to fire an event with each ArrayList
// that we're associated with.
parent.OnArrayItemsChanged(e);
}
}
}
}

 


The EventedListItem keeps track of its parent EventedArrayList, so that it knows which ones to use to fire events through. It supports a list of parents that this item has been added to, that way you can put this item in multiple EventedArrayLists. If it hasn't been associated to an ArrayList yet, then it doesn't fire any events.

Also, any item that wants this functionality needs to either implement the IEventedListItem interface or derive from EventedListItem (which contains the default implementation for that interface). I implemented it through an interface, in case you have issues where you need to derive from a specific base class. And, provided the base implementation for the cases where you can derive from it (in this case, no additional coding needed on your end).

Note: If multiple class derivation is an issue for your class, then you must implement IEventedListItem on your class rather than deriving directly from EventedListItem (you can copy its code for your implementation, if you want the same functionality though).


Now that we have these two classes (EventedArrayList and EventedListItem), we need to associate them. I did this in the EventedArrayList class. The OnArrayItemsAdded method associates any newly added items with this EventedArrayList. If the item does not implement IEventedListItem then it's not associated.

protected virtual void OnArrayItemsAdded(ArrayListEventArgs e)
{
arrayItemsAdded temp = ArrayItemsAdded;
if (temp != null)
{
// make sure all items are associated with this array before firing event.
AssociateItemsWithArrayList(e.Items);
ArrayItemsAdded(this, e);
}
}

protected virtual void OnArrayItemsRemoved(ArrayListEventArgs e)
{
arrayItemsRemoved temp = ArrayItemsRemoved;
if (temp != null)
{
// make sure anything that was associated with this array is now disassociated.
DisassociateItemsWithArrayList(e.Items);
ArrayItemsRemoved(this, e);
}
}

private void AssociateItemsWithArrayList(ICollection items)
{
foreach (object obj in items)
{
// hook into items that are evented, so they can fire
// their change events through this ArrayList.
if (obj is IEventedListItem)
{
IEventedListItem temp = obj as IEventedListItem;
if (temp != null)
temp.AssociateWith(this);
}
}
}

private void DisassociateItemsWithArrayList(ICollection items)
{
foreach (object obj in items)
{
// hook into items that are evented, so they can disassociate
//objects that are associated with this ArrayList.
if (obj is IEventedListItem)
{
IEventedListItem temp = obj as IEventedListItem;
if (temp != null)
temp.DisassociateFrom(this);
}
}
}

 


As you can see, we also have a corresponding DisassociateItemsWithArrayList that's called from OnArrayItemsRemoved to get rid of any existing associations.

Now, on to how we handle these new events...

You can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.

ArrayList with Events (Part 1) 

I recently was involved in a thread on GotDotNet about responding to ArrayList changes in the UI. DalanGalma wanted to populate a ListView with data from an ArrayList and keep it in synch with the associated data objects. Having an ArrayList that fired events (this could also be done for other collections as well) when things changed could be generally useful, not just for keeping the ListView up to date. So, I decided to create one (after a few searches for something like that on the web).

So, first thing to do was figure out what events to send. I decided on having several:

  • ArrayItemsAdded - called when new items are added to the list either by Add or Insert methods.
  • ArrayItemsRemoved - called when items are removed from the list through Remove methods or Clear.
  • ArrayItemsChanged - called when the actual data for an item has changed (depends on object implementor to fire event appropriately).
  • ArrayItemsSorted - called whenever the Arraylist is sorted.

The interesting thing about ArrayItemsChanged is that data objects need to fire it, and it doesn't get fired when you set an item into an array position.
          myArray[index] = newItem;
Instead, I decided to fire an ArrayItemsRemoved followed by ArrayItemsAdded (rather than ArrayItemsChanged) because the item itself isn't actually changing, but the item that was at that position in the ArrayList is being replaced by the new item. It's debatable as to which is better, but if you feel that ArrayItemsChanged should be fired instead, feel free to update the code in the sample to your requirements.

Here's some of the code for the EventedArrayList class:

///
/// EventedArrayList overrides the default ArrayList class to fire events when
/// interesting things happen to the array, like when items are added, removed,
/// and changed.
///

public class EventedArrayList : ArrayList
{
#region Properties
public override object this[int index]
{
get
{
return base[index];
}
set
{
object oldValue = base[index];
if (oldValue != value)
{
base[index] = value;

// when the indexer is set, then we're really getting rid of the old
// item and setting the new one.
if (oldValue != null)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(oldValue));
}

if (value != null)
{
this.OnArrayItemsAdded(new ArrayListEventArgs(value));
}
}
}
}
#endregion

#region ArrayList override methods
public override int Add(object value)
{
int index = base.Add(value);
if (index >= 0)
{
this.OnArrayItemsAdded(new ArrayListEventArgs(value));
}
return index;
}

public override void Insert(int index, object value)
{
base.Insert(index, value);
this.OnArrayItemsAdded(new ArrayListEventArgs(value));
}

public override void InsertRange(int index, ICollection c)
{
base.InsertRange(index, c);
if (c.Count > 0)
{
this.OnArrayItemsAdded(new ArrayListEventArgs(c));
}
}

public override void RemoveAt(int index)
{
object obj = this[index];
base.RemoveAt(index);

if (obj != null)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(obj));
}
}

public override void RemoveRange(int index, int count)
{
object[] obj = new object[count];
this.CopyTo(index, obj, 0, count);

base.RemoveRange(index, count);

if (obj.Length > 0)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(obj));
}
}

public override void Clear()
{
object[] obj = new object[this.Count];
this.CopyTo(obj, 0);

base.Clear();

if (obj.Length > 0)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(obj));
}
}

public override void Sort()
{
base.Sort();
this.OnArrayItemsSorted(new EventArgs());
}

public override void Sort(int index, int count, IComparer comparer)
{
base.Sort(index, count, comparer);
this.OnArrayItemsSorted(new EventArgs());
}

public override void Sort(IComparer comparer)
{
base.Sort(comparer);
this.OnArrayItemsSorted(new EventArgs());
}
#endregion
}

 


You'll notice that I didn't override AddRange or Remove because they call through to other methods (InsertRange and RemoveAt respectively) that already fire events, so you would get multiple events fired in those cases. Counter-intuitively, you do need fire an event for Clear because it doesn't call into RemoveRange as I thought it might... gotta love consistency. :)

Note: This class does not contain an overloaded constructor that takes an ICollection similar to the one ArrayList has. No events are fired during the class's creation because no one could be listening for them yet anyway, so you would not get notification that these items were added in the constructor. Therefore, I decided to leave that method out.


Finally, the ArrayListEventArgs class is used by all of the events. This class simply keeps a list of items that have been manipulated by an EventedArrayList operation. This is passed into the event handlers, so that they can use that information to respond accordingly.

In my next blog entry, I'll discuss the data object that fires change events...

You can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.

Thursday, March 25, 2004

GotDotNet got a Dear John Letter 

Craig Andera recently posted a "dear John" letter to GotDotNet, moving his project from GotDotNet Workspaces to SourceForge. I can appreciate some of Craig's problems with Workspaces. It's been slow and unreliable at times, but I've been trying to stick it out with a project of my own hosted there.

Hopefully Craig, GotDotNet will listen and can get some of these issues straightened out quickly -- for those of us who remain there. Who knows, if they do, they may even be able to win you back... :)

My own biggest complaint is that I can't get anything but their web UI to work when trying to access source control. There's also a WinForms and VS plug-in that's supposed to allow you to access files checked into the source control system. But, they don't work on my machine. I've traded email and forum messages with GotDotNet folks but have never been able to make it work. And, let me tell you, using their web UI, to upload and check files in and out, is VERY painful.

All About Blogs and RSS 

Here's a good intro article to Blogs and RSS from MSDN Magazine written by Aaron Skonnard.

Tuesday, March 23, 2004

Know When to Hold'em, ... Know When to Run. 

If you've ever been on projects that don't know how to make the hard choices of which features to implement and which to cut, then you'll appreciate Brad's blog entry entitled: Software Engineering 101: Cutting a feature sooner is better. He makes a lot a good points about project management in that entry.

Monday, March 22, 2004

Man arrested for allegedly extorting Google 

You gotta love this guy...

Not too bright, and he definitely got what he deserved.

Beam Me Up Scotty... 

So, I'm standing in line at a local grocery store - Larry's Market in Redmond - yesterday afternoon. And, who gets in line behind me? This guy:



Yep, James Doohan aka Scotty from the original Star Trek series. I gotta say, he's looking his age (about 84) these days. It took a couple of seconds for me to even recognize who it was...

Friday, March 19, 2004

Neil Gaiman: other than books 

Brijesh read my post on Neil's blog and also talks about Neil's work on the Sandman graphic novels (high class comic books :).

Although I'm a big fan of his books, I've never read or even seen the Sandman series, but maybe I need to read those as well...

My Blog is the Borg 

When I started my blog, it was not only an outlet for me to express my thoughts; it was also an experiment -- an attempt to create a fully-featured blog using nothing but free software, sites, and services. I began assimilating services I found on the web and at other people's blogs. And, now only about a month into it, I have a pretty feature-rich blog. So, I wanted to write about my experiences doing this, in case other people out there are trying to put together a good blog too. Plus, it gives me a chance to highlight the people that are letting me put up my blog (besides the links to their sites in my blog's "Tools" section aren't even in the RSS feeds anyway).

Here is a list of all of the free stuff I'm using to produce my blog, and some other alternatives that I tried:

  • First was finding a blogging engine and hosting site. There are a plethora of blogging engines and quite a few are free. Of those free blogging engines, many of them needed components to be installed on the web server, so that the blogs could be published. However, the combination of blogging engine and free hosting are what attracted me to Blogger and BlogSpot.
  • Second, nearly all blogs support syndication of some sort. Blogger just produces Atom; however, many of the news aggregators out there only support RSS feeds, so I found a few services on the web that convert Atom feeds into RSS feeds. I settled on the one from 2Rss, but also tried Rssify and Feedster's converters. I liked 2Rss better because it actually uses the title of my blog entries as the title in the RSS item. The other two converters use the first 20 characters of the entry as the title; otherwise, they worked well too. I know it's minor, but it looks so much better to me in the news aggregators.
  • Then, you can't have a fully featured blog without comments. People love to leave their thoughts about blog entries, so I had to add that. Blogger doesn't natively support it, but there are other services out there that integrate in their functionality. I blogged about them earlier, but decided to go with HaloScan because they were free, ran as a service so I didn't need to install anything on a web server (not an option since I'm using BlogSpot), were still accepting users (there were a couple that were full?!?!), allow me to manage the comments on my site, and also provided Trackback functionality.
  • Trackback (also provided by HaloScan) is kind of cool. It allows others to link their blog entries to yours if they're talking about the same topic or responding to you. This is great because I can follow it back to other bloggers who are talking about something that I'm interested in. Many times after returning via Trackback a few times to the same blog, I add it to my news aggregator because obviously they talk about things that I'm interested in.
  • And, I wanted to find some measure of how many people were coming to my blog. I found a page counter service provided by Sparklit, which counts my visitors. It works very well, lets me pick the look of my counter, and create multiple counters. Of course, this doesn't count people that read my blog through a news aggregator, but I thought it would be too annoying to add the page counter in the RSS item...
  • Although Guestbooks aren't common on most blogs, I found one on Sparklit too, so decided to add it to my blog. We'll see if anyone enters their information in it.
  • Finally, my news aggregator is RSS Bandit. It's a free, .NET based, Windows desktop aggregator. It lets me keep track of feeds and blogs that I'm interested in, categorize feeds, and flag entries that I need to read more about, follow-up on, or respond to. Overall, pretty useful. And, it's still under development by a community of developers, so they're good about accepting feature requests and bugs.

The main thing I still need to find is a good place to host my images. The ones I posted earlier are on a workspace on GotDotNet, but folks can't see them without accounts there. I've thought about moving them to a public MSN community, but haven't done it yet. Do you know of any good, free spots to save images? Let me know.

So, are there any other features that my blog is missing?

Obviously, the experiment is just beginning, but so far so good...

Wednesday, March 17, 2004

Neil Gaiman blog 

Oh, I'm so excited I can hardly contain myself. I just found Neil Gaiman's blog. Neil is a New York Times bestselling author in the sci-fi/fantasy genre. The first book of his I read was American Gods. It was unique, quirky, and I couldn't put it down. Since then, I've read several of his other novels and haven't been disappointed.

If you haven't read any of his books, check some of these out:

He's also written quite a few short stories, but I haven't read any of those yet.

Reviewing Patent Filing 

I'm in the middle of reviewing a patent filing that's been processed into legalese. I'd rather go to the dentist to have some teeth pulled. Or, maybe a nice root canal... :)

Tuesday, March 16, 2004

Vanguard: Saga of Heroes Game 

Ooooo, another MMORG to waste my (spare) time on. It looks like this one's going to be published by Microsoft and will be done by the designer and co-creator of EverQuest.

I never played EverQuest, but I hear it's good. My addiction came a bit earlier with the first version of Ultima Online.

Read/Write XML files, Config files, INI files, or the Registry 

This is a very useful C# library on Code Project. It provides classes to read/write for different sources, but provides a single interface between the 4 provider types. This allows you to easily change where your application data is saved (ini files, registry, XML) depending on your needs, or use different types for different projects. And, it has a pretty handy sample UI too.

Where in the World is DarthPedro? 

Found this mapping site via Simon Guest's blog. Needless to say, there's a lot of countries left for me to visit, but I'm doing pretty well in covering US states.




create your own visited country map
or write about it on the open travel guide



create your own personalized map of the USA
or write about it on the open travel guide


Monday, March 15, 2004

World's Best RSS Feed... 

Here it is the world's best RSS feed. You can now get Dilbert daily on your RSS Aggregator. Thanks to the brijwhiz blog for pointing it out.

They also provide feeds for other comics as well.

Gibson's "Pattern Recognition" 

Just started reading this book over the weekend (I know it's been out for a while, but I hadn't gotten to it yet). I've powered through about half of it, and it's very intriguing. Gibson is concentrating more on the present, but has done a good job of adding technology to everyday life.

I really liked the concepts of a "cool" hunter, and the "Deep Niche" advertising (using good-looking men and women at bars to mention products to members of the opposite sex, brilliant!). And, of course, the bits of video being released on the web and causing a sensation of various web forums.

I can't wait to finish it.

Saturday, March 13, 2004

In the Beginning... Was the Command Line. 

I'm in the middle of reading "Quicksilver" by Neal Stephenson. It's a good book, but very long and a little slow for my liking. So, I had to take a break half way through it and read another one of his books -- "In the Beginning... Was the Command Line".

Command Line is a great book. It's about technical subject matter (the rise of the GUI and how it still relates to the command line interface), but he does it in a light tone that's approachable by the non-tech savvy too. Stephenson goes into the battles between Mac and Windows and newer OSes like Linux and BeOS. It's a little dated because it was written in 1999 but still very entertaining.

Now, if there were any writer that I expected to have a blog, it would be Stephenson, but it doesn't look like he has one. The other one was William Gibson, but he hasn't blogged in a while, so that he could write his next book. I miss reading your posts though...

Friday, March 12, 2004

FlatComboBox Sample 

After working on the FlatTabControl, I decided to look at making the WinForms ComboBox flat (it doesn't support a flat look either) using the same trick of setting the ControlStyles.UserPaint style to true, and drawing in the borders myself.



Overall, it worked pretty well, and I followed the same pattern with the OnPaint, breaking it up into different, overridable steps:

  • DrawControlBorder
  • DrawDropDownButton

This painted in the border and button correctly. However, the edit portion of the ComboBox wouldn't draw correctly. For a ComboBox in DropDownList style, I just had the selected item draw in the top portion as well. That worked, but for the DropDrown and Simple styles, the edit control which exists within the ComboBox would try to paint itself. And, it would paint itself with the wrong font and colors.

It took awhile, but I found a work-around for painting the edit area correctly too. The first part was setting that control's font correctly, which I did in an override of OnHandleCreated:

protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);

// Find the edit control within the ComboBox.
if (DropDownStyle != ComboBoxStyle.DropDownList)
{
IntPtr hEditControl = Win32Methods.GetDlgItem(Handle, editControlId);
Debug.Assert(hEditControl != IntPtr.Zero,
"Failed to get ComboBox's edit control");

// make sure the edit control in the combo box has the right font setting.
Win32Methods.SendMessage(hEditControl, Win32Methods.WM_SETFONT,
Font.ToHfont(), true);
}
...
}

In that code, we find the handle to the EditControl within a ComboBox and set its font the old fashioned way -- through a Win32 API call for SendMessage for WM_SETFONT.

Well, that worked like a charm, so I tried some similar Win32 magic for the colors. I overrode the WndProc and intercepted the WM_CTLCOLOREDIT and WM_CTLCOLORLISTBOX messages. These are sent when a Win32 control is about to paint, so that you can change the colors at that time. So, I created a virtual OnCtlColor method to handle those window messages in my FlatComboBox control.

protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
// when the combo box edit and list controls ask for what colors they
// need, then call OnCtlColor for our FlatComboBox.
case Win32Methods.WM_CTLCOLOREDIT:
case Win32Methods.WM_CTLCOLORLISTBOX:
m.Result = OnCtlColor(m.WParam, m.Msg);
break;
default:
base.WndProc (ref m);
break;
}
}
#endregion

protected virtual IntPtr OnCtlColor(IntPtr hDC, int msg)
{
// Set the appropriate text and background color in the painting DC for
// the edit control. This BkColor is just used for the background right
// around the text.

// *** Note: this is when it's helpful to have a background in Win32 programming
// even when working with .NET, because these are the message that you need to
// handle and the GDI calls you need to make in Win32 to get a combo box control
// to paint in custom colors.
Win32Methods.SetTextColor(hDC, ColorTranslator.ToWin32(ForeColor));
Win32Methods.SetBkColor(hDC, ColorTranslator.ToWin32(BackColor));

// return an HBRUSH to be used to paint the background of the control.
// this is for the portion of the control that may not have text in it.
return BackgroundBrush;
}

This code goes and sets the text and background colors to the ones specified for the control, so that the edit portion paints exactly the same as the rest of the control.

Note: As you see in the picture above, the border for the list portion of the Simple ComboBox is still paint with the default border. There is probably a way to paint that as well by taking over the painting of the list; however, it's not something I needed for my use, so I left it as an exercise for the reader... :)

The code for this sample makes several calls to Win32 APIs which I call through PInvoke interoperability, which I encapsulated into a Win32Methods class. You can get the full source code for those methods and constants from the sample download on GotDotNet or look at it online and report bugs at the workspace.

Thursday, March 11, 2004

FlatTabControl Sample 

Overall, the WinForms TabControl gives you a lot of customization through its owner draw mechanism. You can paint the tab buttons basically in whatever way you want. However, the background color behind the page tabs cannot be changed, and neither can the 3D button effect around the control and the page tab buttons. So, for an application that I'm working on, I wanted to be able to fully customize the drawing of the background and have a flat rather than 3D control

So another control that I have in my Control library on GotDotNet is a FlatTabControl. This control allows the user to specify what the ControlBackColor and the TextColor. These colors are then used during the control's painting. Also, it allows for a BorderDarkColor and BorderLightColor which are used to paint both the border around the control and the borders for the tab buttons.

Here's a look at what I mean. The control on the right is the standard WinForms TabControl, but it's customized as much as possible with a different color scheme. As you can see, portions of it still have basic control look and feel. On the left is my FlatTabControl. It paints everything in the appropriate color scheme, so that you can fully customize your user interface.



The interesting part to get the painting to be overridden was in the constructor to tell the underlying control that I wanted to handle all of the painting (including the borders and background):

public FlatTabControl()
{
// Initialize the control styles for painting.
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);
}

Then, I override OnPaint to draw in what I want. In OnPaint, I broke up the painting into different steps:

  • DrawControlBorder
  • DrawTabButtonFull
  • DrawButtonBorder
  • DrawTabButton
  • FixupSelectedBorder
  • PerformDrawItem

These methods are all exposed as virtual, so that anyone can derive from the FlatTabControl and customize any of the specific drawing steps that they're interested in. Want to just change how the button borders are drawn, then just override the DrawButtonBorder method.

Also, to continue to support the owner draw mechanism and allow customization in that way, the PerformDrawItem method fires the DrawItem event, if the DrawMode property is set to OwnerDraw.

Note: I turned off being able to set the TabAlignment to Left or Right because for my project, all I needed was the Top and Bottom. If you need others, it shouldn't be too hard to add. I may even do it in the future, if I ever need it.

Also, you can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.

ImageListBox Sample 

Well, I added to my ImageComboBox sample by extracting some shared drawing and ImageItem collection code, so that it could be used for a ListBox item as well. This allowed me to easily create an ImageListBox.

As I was doing this, I wanted to have the Items collections for these controls be editable at design time, so that users could add an item with an image index at the same time. At first, I tried replacing the Items collection with my own ImageItemList:

class ImageItemList : CollectionBase
{
public ImageItemList()
{
}

public new override ImageItem this[int index]
{
get
{
return base[index] as ImageItem;
}
set
{
base[index] = value;
}
}

public override int Add(ImageItem item)
{
return base.Add(item);
}
}

For completeness, the ImageItem class looks like the following. It derives from Control so that it can be used in the collection editor. Otherwise, I would need to support a type converter for my class (I may do that in a later sample).

public class ImageItem : Component
{
private string itemData = "New Item";
private int imageIndex = -1;

public string ItemData
{
get { return itemData; }
set { itemData = value; }
}

public int ImageIndex
{
get { return imageIndex; }
set { imageIndex = value; }
}

public ImageItem(string itemData, int imageIndex)
{
this.itemData = itemData;
this.imageIndex = imageIndex;
}

public ImageItem()
{
}
}

This automatically gave me the collections editor that I wanted during design time with property grid items for ItemData and ImageIndex. However, filling those in didn't actually display the new items. And, worse at run time the control didn't think there were any items in the ListBox. That's because the ListBox.Items property is not virtual and cannot be overridden, so I could only create a new property of the type I wanted. However, the control is still using the underlying Items list and knows nothing about the one I just added.

So, I had to go back to the drawing board and came up with a different collection (called BaseImageItemList). This one actually wrapped the ListBox.Items collection and implemented all of the IList methods. The implementation passes through all of the calls to the innerCollection. Then, I added ImageItemList which derived from BaseImageItemList to provide type specific versions of some methods. This allowed me to create a type-specific Items property and still have it work with the items that the base ListBox control was expecting.

public abstract class BaseImageItemList : IList
{
IList innerCollection = null;

public BaseImageItemList(IList innerCollection)
{
this.innerCollection = innerCollection;
}

#region IList properties
public object this[int index]
{
get
{
return innerCollection[index];
}
set
{
innerCollection[index] = value;
}
}

public int Count
{
get { return innerCollection.Count; }
}

public bool IsSynchronized
{
get { return ((IList)innerCollection).IsSynchronized; }
}

public object SyncRoot
{
get { return ((IList)innerCollection).SyncRoot; }
}

public bool IsFixedSize
{
get { return ((IList)innerCollection).IsFixedSize; }
}

public bool IsReadOnly
{
get { return innerCollection.IsReadOnly; }
}
#endregion

#region IList methods
public virtual int Add(object item)
{
return innerCollection.Add(item);
}

public virtual void Clear()
{
innerCollection.Clear();
}

public virtual bool Contains(object item)
{
return innerCollection.Contains(item);
}

public virtual void CopyTo(Array items, int index)
{
innerCollection.CopyTo((object[])items, index);
}

public virtual IEnumerator GetEnumerator()
{
return innerCollection.GetEnumerator();
}

public virtual int IndexOf(object item)
{
return innerCollection.IndexOf(item);
}

public virtual void Insert(int index, object item)
{
innerCollection.Insert(index, item);
}

public virtual void Remove(object item)
{
innerCollection.Remove(item);
}

public virtual void RemoveAt(int index)
{
innerCollection.RemoveAt(index);
}
#endregion
}

This class is actually pretty generic and can be used to wrap any collection with another typed one around it, if you ever find another place where you might need to use that sort of thing. Boy, I can't wait for Generics support in the .NET Framwork!

After making those changes, everything worked as expected and I could display items with images that were either added at design or run time. You can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.

Wil Wheaton's book 

I had not learned until recently that Wil Wheaton wrote a book. From the reviews I've seen on it, I'm going to have to pick it up soon and have a read. I definitely like reading Wil's blog, so I expect the book to be quite enjoyable too.

For those geeks who don't know, Wil played Wesley Crusher on Star Trek: Next Generation.

Big Fish 

I saw the movie "Big Fish" a while ago -- actually very soon after it came out. But, just recently read this review about it, so decided to give my thoughts as well.

I thought Big Fish was an excellent movie. Maybe its being a 30-something male that helped me really identify with the son, or some fear in the back of my mind that I don't really "know" my dad. But, whatever it was, this movie really struck a cord with me and I loved it -- I don't really say that about a movie very often. The scenes between the father and son were moving and in some cases heartrending, until the end. And, the stories the father told were entertaining.

If you haven't seen it, you really should try to catch it before it completely leaves the theaters (or on DVD if it's out yet).

Wednesday, March 10, 2004

Printing Blank Page After a Print Preview 

I just answered a post on GotDotNet about PrintDocument.Print only printing a blank page after showing the print preview dialog. This has happened to me a few times and seems to be something that others get tripped up on as well, so I decided to put up an entry about it.

The Symptom:
Your PrintDocument works fine when the user just prints it first. However, if the user does a print preview followed by a print, the result is a blank page rather than what it should be.

Typical Reason:
The PrintDialog uses the same instance of the PrintDocument as the PrintPreviewDialog, so you need to be sure to reset any important data during PrintDocument.OnBeginPrint. So, if you have anything that may be traversing lists or tables or keeping track of page numbers or whatever, they get set during the first run in the PrintPreviewDialog. So, when the PrintDialog is actually run, your PrintDocument may think it's already printed everything it needed to.

If this is the problem, you should be able to simulate it by calling the PrintPreviewDialog twice in a row with the same PrintDocument.

Here is a very good article on MSDN about .NET WinForms printing.

Debugging Trouble on VS.NET? 

Here's a very useful article on how to trouble shoot problems with the VS.NET debugger. If you've ever had trouble debugging one of your programs, this information is most helpful. This came via duncanma's blog.

Atom vs RSS on CNet 

Wow, looks like the news is picking up on Dave's RSS message too:

"Blog format truce proposed" on C|Net by Paul Festa.

Tuesday, March 09, 2004

Compatible Syndication Formats 

I just read Dave Winer's call to consolidate RSS with other syndication formats. Being a Blogger/BlogSpot user, I think it would be great to have compatible syndication formats, so that my blog feed can be used by all of the desktop aggregators. And, a lot more of those (especially the free ones) support RSS over Atom.

Currently, to get this to work, I use Rssify to convert the Atom feeds into RSS (I have also tried 2Rss). That sort of works, but not very well. The conversion isn't great and what you get in the final RSS is sometimes different than what you see from sites that have native RSS support. Having a compatible/merged format would make life a whole lot easier.

Sun Microsystems Debt Cut to 'Junk' 

Well, at least now it's officially recognized by the financial instutions anyway... ;)

Dilbert on Marriage 

Here's a funny Dilbert for today.

Sunday, March 07, 2004

Adding Comments Service to Blogs 

Yesterday, I decided to look for a Comments service that I could add to my blog (Blogger/BlogSpot doesn't support it). After a few Google searches, I found a couple of pages that had useful lists of such services. Here they are in case anyone else is interested:

I decided to go with Haloscan. It was pretty simple to implement, just a couple of html and script tags in the appropriate spots. And, they provide wizards to create specific code for your blogging engine (like Blogger/BlogSpot). Also, they allow you to customize the comments UI with additional templates, and provide Trackback functionality too. All in all, it took me about 30 minutes to get it all up and working -- not bad. Actually, it took longer to do the search on Google and get to the right information.

And, the best part -- this was all free using: Blogger (posting), BlogSpot (hosting), Haloscan (comments/trackback), and Rssify (RSS feed for Blogger atoms). This is really what stitching together web services into applications is all about. Now, we just need more of these types of reuseable services...

Friday, March 05, 2004

Funny MSDN Headline 

Wow! Who in the world is writing headlines for MSDN these days:

Fall in Love with ASP.NET Again (or for the Very First Time)
Download the ASP.NET Resource Kit for free and get essential tools, tutorials, samples, over $1000 worth of .NET controls and components, and more. (March 1, Product Information)


... And, what would you pay for all this??? But, wait, if you order now, you also get a full set of steak knives ...

I blame this guy... :)

ImageComboBox Sample 

My ImageComboBox sample was just posted on GotDotNet.

The ImageComboBox allows you to associate an ImageList with the control and select which images to attach to a particular item in the list. It also has design-time support, so that you can add items with specific images at that time, as well as being able to do it dynamically at runtime. It supports the different styles of ComboBoxes -- DropDown, DropDownList, and SimpleList.

Items of interest:
  • DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) lets you hide properties that you don't want to support in your control, even if they're in your base class.
  • DesignerSerializationVisibility(DesignerSerializationVisibility.Content) lets you save the contents (a list of items in my case) rather than just the object itself.
  • By creating a type Collection that wraps the ComboBox.ObjectCollection, we can use the CollectionEditor to update the Items list (with specific text and image information) during design time.
  • Here's a link to an earlier blog entry I made about drawing the image with the background color transparent.

Surviving Email Overload 

Scoble talks about organizing you life through better inbox management. I never took the David Allen course he mentions, but I did take a different one.

I took a time management course several years ago (5+) that talked about the same thing: keeping your inbox clean. And, I've been doing it ever since. Typically, I have 5-10 email items in my inbox at any time. The rest are processed or filtered to appropriate folders (which I usually look through at the beginning and end of the day). Being on several high traffic mailing lists, it's impossible to look at every message right away so automated filters are a must for a clean inbox. However, I still try to keep most messages coming through my Inbox, so I can see the information and make a decision on what to do with it. And, I only look at new messages in my inbox every couple of hours.

Basically, as messages come in, I read them, then decide what type of message it is. Informational ones get placed into appropriate folders. If the response is quick, those messages are returned right away. If it's important and takes longer investigation but can still be done today, then it stays in the inbox (as a constant reminder). Things that take longer to respond to are made into Outlook tasks or appointments given specific completion dates and reminders. Everything else gets deleted.

That's usually the controversial part: deleting stuff. Lots of people worry about deleting stuff they may someday need. But, once you actually start doing that, you'll notice that you didn't really need it. And, if you saved everything but could never find it anyway, is it really even still on your system... :) Over the years that I've been practicing this, there have been a couple of times that I deleted a message I needed, but you can always ask the person to resend it (they're probably hoarding all of the emails anyway).

Now, what I need is a good system for organizing the RSS feeds that I'm watching and sifting through the important and the useless stuff that comes through...

Thursday, March 04, 2004

Did A Computer Virus Bring Down The Soviet Union? 

Conspiracy theorists unite! This is great fun to speculate on:

"President Ronald Reagan approved a CIA plan to sabotage the economy of the Soviet Union through covert transfers of technology that contained hidden malfunctions."


I'm Sorry Dave, Your Speeding 

Ok, this is just wrong (via Dave Winer's blog):

"At the Melbourne Motor Show last week, Toyota unveiled a controversial concept car that would very closely monitor, and in some cases restrict, the actions of its driver -- including refusing to turn on."


Tuesday, March 02, 2004

Drawing Composite Image... 

You know some things are just harder than they should be. I was trying to draw an Image so that it would paint with the selection color, as needed. That way, if the item was selected, its background would composite draw with the selection. You've probably seen this type of functionality in all sorts of WinForms controls. Since a picture is worth a 1000 words, here goes:



My control had an ImageList attached. On the ImageList, the TransparentColor was set to white. So, I thought if I used ImageList.Draw, it would paint it correctly given the TransparentColor. That way when an item was selected, the image would paint with the selection color for the bits of the image that had the transparent color.

Well, needless to say, that didn't work. I had to call the right incarnation of Graphics.DrawImage (mainly one that took an ImageAttributes as a parameter). By setting the right ColorKey in the ImageAttributes, it finally painted correctly. Here's the code:


Color lowerColor = Color.FromArgb(250,250,250);
Color upperColor = Color.FromArgb(255,255,255);
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorKey(lowerColor, upperColor, ColorAdjustType.Default);

g.DrawImage(imageList.Images[imageIndex], rect, 0, 0,
imageList.ImageSize.Width, imageList.ImageSize.Height,
GraphicsUnit.Pixel, imageAttr );


Monday, March 01, 2004

Lord of the Oscars 

The Lord of the Rings finally won its Oscars:

"I'm so honored and relieved that the academy and the members of the academy that have supported us have seen past the trolls and the wizards and the hobbits in recognizing fantasy this year," Jackson said in accepting the best picture Oscar.

I don't think that 'The Return of the King' was the best movie of the trilogy, but it looks like they won for the whole set. Hopefully, this will spark some new fantasy films -- the way Star Wars did for sci-fi.