This blog will describe some of the learning experiences that I have with .NET, some personal projects that I'm working on, and whatever other topics tickle my fancy.
My Blog Links
Wednesday, March 31, 2004
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
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... :)
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?
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
Mike Rowe is donating his legal defense fund to a hospice for terminally ill children.
Good for him!
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.
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:
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:
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.
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:
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.
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.
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:
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:
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
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.
Tuesday, March 23, 2004
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
You gotta love this guy...
Not too bright, and he definitely got what he deserved.
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
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...
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:
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
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.
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
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.
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.
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
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.
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
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
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:
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:
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.
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
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):
Then, I override OnPaint to draw in what I want. In OnPaint, I broke up the painting into different steps:
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.
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:
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).
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.
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.
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.
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
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.
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.
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.
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.
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
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.
Well, at least now it's officially recognized by the financial instutions anyway... ;)
Here's a funny Dilbert for today.
Sunday, March 07, 2004
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
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... :)
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:
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
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."
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
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:
Monday, March 01, 2004
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.