Sunday, March 16, 2008
My wife and I were talking by phone while I was at a store when she mentioned that she wanted an MP3 player for use around the house. Easy in-car use was also a requirement. I went to the music player display case to see what was offered. A full 80% of the boxes were various flavors of iPods. The rest were Zunes with a couple of Samsung units.
Since I have a bad opinion of Apple DRM and iTunes lock-in, I bought her a 4GB pink Zune and brought it home. My wife loved opening the box and playing with all the bits. We both agreed that the out-of-the-box experience was great. She went off to do some grocery shopping while I handled the task of installing the software and getting the thing running.
The great hardware happiness died an ugly death during the software installation. The process was agonizingly slow. The software prepared the computer for installation three times. The Zune software appeared to get installed three or four times, although I knew those were small support programs, then ... reboot! What? What does a simple music player need to restart the entire computer for? While writing this blog post, the software also automatically ran Microsoft Update without my permission and contrary to my Microsoft Update system settings. Very rude!
The total length of the install seemed like I was installing Microsoft Office and Visual Studio at the same time. It took nearly a half an hour on a computer with nothing else running and no other load on the broadband modem. I can only assume the 33MB of dedicated files are supported by numerous other changes -- and all this on a fully updated Windows XP system. I actually ended up with multiple restarts for multiple updates. I was disappointed the install process was so miserable, but grateful it went forward with no errors.
Happily, my wife saw none of this and was soon listening to a few of our CDs I had ripped previously. She dutifully listened to the music that came with the player, then spent some time learning the Zune software deleting it all. She judged it all to be crap. The only artist she recognized of the bunch was Bare Naked Ladies and that was only because they sang a song for Disney's movie Chicken Little.
She loves the size, feel, and interface of the Zune. That part of the experience was great. The car kit just worked with no problems. She is happy, and that was a primary goal of the purchase.
Saturday, March 08, 2008
IE8 Beta 1 now available here.
My initial experience with this version was not good. As soon as I viewed my Favorites and started to mouse over the list, the browser crashed. Every time. After the fifth or sixth time, I uninstalled it and returned to IE7.
After downloading a new copy, I reinstalled. It still crashed. Then I started the browser without add-ons. (Right click the Desktop icon and choose "Start Without Add-ons.") No crashes when viewing Favorites.
Ah hah. I went in and pruned down the list of add-ons. I was surprised at how many things had crept into the list. A couple of companies got turned off entirely: InstallShield (2), Symantec (6), Groove Alliance (1). I also disabled a couple of duplicates and some strange things I never used. In the end, I had stuff from Adobe, Apple, Sun, Microsoft, and a few others. After restarting in normal mode, I had no more crashes.
I am not sure of the precise culprit, although Eric Law hints on the IE Blog that it may be the Sonic Solutions Drive Letter Access (part of the Roxio CD/DVD burning software which I got from Dell) and disabled in the browser. I wondered why InstallShield and Symantec needed such a browser presence. I will see what complains as I work and if things seem faster.
Tuesday, December 25, 2007
... to everyone at GeeksWithBlogs and those elsewhere reading. Enjoy the season with your own traditions and religion, whether you celebrate the birth of Christ or not. Enjoy!
Sunday, December 23, 2007
Technorati Tags:
Christmas As I noted previously, I have now retired from military active duty. I am taking the Christmas season off to relax before going back to work for my new career out there in the Real World©®. That means I have gotten the opportunity to spend much more time with the wife and kids.
Thursday morning, while the girls were in school and the little one was napping, my wife and I took the time to open and assemble all the kids toys for Christmas. We did this early to avoid a late night on Christmas Eve, or the crisis rush on Christmas morning. It took us much longer than we anticipated due to the nature of this year's toy packaging adventures.
We intentionally avoided the infamous clamshell death plastic this year. The closest we came were with a pair of nicely packaged GE headsets
for use with our cordless phone system. They had a paperboard back that was easy to cut open -- no plastic cutting required. The toy boxes took much longer than anticipated to open since the manufacturers seem to have substituted complex folded cardboard secured with miles of cellophane tape to replace the hated plastic clamshells.
The full-view packaging designers win the raspberry award this year for their use of wire twist ties suitable for towing. These things were so thick that our Fiskars scissors, normally a match for any consumer packaging, failed miserably. I had to resort to my Craftsman wire cutters, though I wondered if I might need something bigger. The stiff, machine-tightened wires were impossible to undo by hand even when we had full access to them behind their nefariously complex packaging. Be ready with your own heavy-duty implements of destruction in the coming days.
We have been buying stuff over the last 6-8 months, as items became available. We have found many of the things we chose are now not available, or available on Amazon or EBay at astronomical markups over what we paid just months ago (a $40 toy was selling for $175).
Our son emerged from his nap and demanded to do some preliminary play testing of things his sisters would be getting. He gave a double thumbs up to a few items, but his clear favorite was Pat Pat Rocket
. Since he is just on the verge of walking, we feel he will not spill the beans before Tuesday.
Post-Christmas Update
The Pat Pat Rocket
was a hit with all the kids, but we found that little hands could not pry open the top to play with the figures inside. I remedied that with a sticky tape tab they could grip, but a better design would be appreciated by parents. It also falls into the category of toys which are too loud.
GeoTrax
Rail and Road System Grand Central Station
by Fisher-Price: Recommended; another big hit. We left this one in the box and assembled it on a bedroom floor while the girls were busy elsewhere. It is a big hit on available floor space, so we had to be careful where we put it. The assembly was pretty easy, but I see the engine will be chewing through lots of AAA batteries.
vtech ABC Food Fun: Not recommended. It is too loud, comes on randomly even after it is supposedly powered off automatically, the door is hard to open, it is too easy to accidentally hit a mode button, and the magnet pieces are hard for kids to align correctly to make it work. Other than that, it briefly held the attention of a three-year-old.
Sunday, November 11, 2007
As I come up on 26 years of active duty service in the Navy, I have been reliably informed by my wife that it is in fact time for me to complete my Navy service and transition to some other line of work. When the 5-star Admiral at home speaks, you listen. Next Veterans Day will likely be a good time for me to look back on my Navy career and reminisce. For this year, I join others who offer their thoughts to those in uniform who have risked and lost much more than I.
Thursday, October 25, 2007
Reminder: All posts in this series can be found here.
The Toolbox and the DesignSurface work together to allow a developer to add new components and controls to the design surface. There are two interfaces that implement this functionality: IToolboxService and IToolboxUser.
IToolboxService defines an interface that provides methods and properties to manage and query the Toolbox capabilities in a development environment. All forms designer systems must implement the IToolboxService in their Toolbox in order to provide the expected Windows Forms UI functionality. This service is added to the ServiceContainer and can be accessed at any time. There are 15 methods and two properties in the interface which I will not cover here in detail. Many of those methods deal with adding, deleting and selecting Toolbox items.
IToolboxUser defines an interface for setting the currently selected toolbox item and indicating whether a designer supports a particular toolbox item. A forms designer will implement an IToolboxUser interface that consists of only two methods: GetToolSupported and ToolPicked. You use GetToolSupported to filter the items that can be added onto the designer.
Implementation: There are several steps you have to take to build a Toolbox for a Forms Designer system.
- Create a user interface that displays the tools to the user. This is typically implemented as a classic TreeView, or as sliding tree control that behaves much like Windows Explorer, but without grid or connection lines. Building the desired functionality can take quite a bit of time. There is a nice Code Project article that has a mostly-complete solution with multi-level tabs. There are several design decisions you will have to make before you start coding. Besides the visual representation already noted, you must decide how you will discover, categorize, and load the tools. One of the MSDN Magazine articles describes three methods: hard coding (not recommended), configuration file, and attribute decorated classes loaded from a known location and checked using reflection.
- Implement the IToolboxService. The three main members of this interface are GetSelectedToolboxItem, SerializeToolboxItem, and DeserializeToolboxItem. Other useful members include GetCursor, IsSupported, IsToolboxItem, and SelectedToolboxItemUsed.
- Plug the IToolboxService implementation into the design-time environment.
- Create a keyboard interface.
- Handle Toolbox events, such as tool selection, keyboard, mouse clicks and drag and drop.
Usage: There are four ways a developer can add a component or control to a forms designer DesignSurface; one uses the keyboard, while the other three use the mouse. The Visual Studio Toolbox functions are described here. I will talk about the changes required to the sample application to implement the Visual Studio functionality in Part 2 of this post.
- Keyboard: The Visual Studio Toolbox keyboard interface is described here. Essentially, you Ctrl+Tab/cursor to the Toolbox (or press Ctrl+Alt+X), cursor to the desired control and hit Enter. The control will be added to the DesignSurface. Ctrl+Up Arrow, Ctrl+Down Arrow, Left Arrow, and Right Arrow also have control functions within the Toolbox.
- Mouse: Double-Click a Toolbox icon. The control will be added to the DesignSurface. This has the same effect as using the keyboard method.
- Mouse: Single Click a Toolbox icon. Move the cursor over the DesignSurface, were it will turn to a Crosshair ready for you to click and drag the desired control location and size. Visual Studio customizes the Crosshair cursor with color overlay of the selected tool icon until the mouse has been clicked and released. This operation can be canceled with the Esc key.
- Mouse: Drag and Drop a Toolbox icon. Click a control icon, drag it onto the DesignSurface, and drop it where you want it. This operation can be canceled with the Esc key.
To allow the icons from the Toolbox to be added to the DesignSurface using the mouse or keyboard, the Toolbox in the sample code hooks onto the KeyDown and MouseDown events. IToolboxUser.ToolPicked is called for the Enter key or a mouse double-click. The sample code shows how to serialize the ToolboxItem into a DataObject and to DoDragDrop for when a mouse single-click event occurs. IToolboxService.SerializeToolboxItem will be called on mouse up and the item will be added to the designer.
When a new control or component is added to the DesignSurface from the Toolbox, the NameCreationService is used to give it a unique name. My discussion of this service and my modifications to the sample code are described here.
Monday, October 22, 2007
Reminder: All posts in this series can be found here.
Simply, it is a container for services. Services are classes that have a well-known interface, that have instantiations which be stored in service containers, can be obtained from a service provider, and are addressable by type. The ServiceContainer class is a service provider and implements IServiceProvider. You obtain a service by supplying the Type of the service you want to the service provider's GetService method. The ServiceContainer.GetService method is declared like this.
public virtual Object GetService(Type serviceType);
This is how you obtain a a reference to a service in a class derived from DesignSurfaceManager or DesignSurface. The cast is required because GetService returns an Object.
INameCreationService serviceNameCreation = (INameCreationService)(this.GetService(typeof(INameCreationService)));
Both DesignSurfaceManager and DesignSurface implement ServiceContainer. This may appear to lead to confusion, but you do not have to keep track of which container holds what service. One of the nice features of using the DesignSurfaceManager class is that it automatically merges services between it and the DesignSurfaces.
There is no documentation of how to use these multiple implementations, but it makes sense to put general, non-DesignSurface services such as IHelpService, IDesignerEventService, and INameCreationService in the central DesignSurfaceManager ServiceContainer. DesignSurface-specific services such as ISelectionService would logically be with each DesignSurface.ServiceContainer.
MSDN states the following about the DesignSurfaceManager.ServiceContainer Property: The DesignSurfaceManager class provides several design-time services automatically. You can override each of these services by replacing them in the protected ServiceContainer property. To replace a service, override the constructor, call base, and make any changes through the protected ServiceContainer property. All services added to the service container that implement the IDisposable interface are disposed when the design surface manager is disposed. The DesignSurfaceManager class provides the IDesignerEventService interface as the default service. IDesignerEventService provides a global eventing mechanism for designer events. With this mechanism, an application is informed when a designer becomes active. The service provides a collection of designers and a single place where global objects, such as the Properties window, can monitor selection change events.
What is interesting here is that MSDN itself does not state what the default design-time services are. A MSDN Magazine designer hosting article provides a table of the default DesignSurface design-time services. An important part of that table is listing which services are not replaceable due to their interrelationships. I wrote some code to see what services were running in the two containers for the sample application and found the following.
HostSurfaceManager.ServiceContainer Services
--------------------------------------------
IDesignerEventService
INameCreationService
IToolboxService
HostSurface.ServiceContainer Services
--------------------------------------------
IComponentChangeService
IDesignerEventService
IDesignerHost
IExtenderListService
IExtenderProviderService
IInheritanceService
IMenuCommandService
INameCreationService
IReferenceService
ISelectionService
IToolboxService
ITypeDescriptorFilterService
This list matches almost perfectly with the default design-time service table noted above. It was not surprising to see the three HostSurfaceManager services were also visible from the HostSurface because the System.ComponentModel.Design.DesignSurfaceManager has a small bit of code in its MergedServiceProvider GetService method that looks like the following:
Object service = this._primaryProvider.GetService(serviceType);
if (service == null)
{
service = this._secondaryProvider.GetService(serviceType);
}
return service;
The primary provider for the HostSurface is its own ServiceContainer. The secondary provider for the HostSurface is the HostSurfaceManager.ServiceContainer. As shown in the code snippet, any HostSurface service is preferred over an identical one in the HostSurfaceManager. Any service unavailable in the HostSurface but present in the HostSurfaceManager is accessible as well.
Thursday, October 18, 2007
Reminder: All posts in this series can be found here.
The first code I will look at is from one of the simplest services supporting the Windows Forms designer. The Name Creation Service is called each time you drop a control/component onto the visual design surface. Your job is to return a unique name for the new control that also means something.
The standard process, per convention, is to take the control/component Type name, camel case it (lower case the first letter) since it will be a member variable, and then append a digit suffix to make it unique from all other controls/components on the form. The number ensures it remains unique for languages which are not case sensitive.
The sample code implements only the INameCreationService.CreateName method. I modeled my implementation off of the sample code for INameCreationService here. I found the sample CreateName code to be excessively complex:
string INameCreationService.CreateName(IContainer container, Type type)
{
ComponentCollection cc = container.Components;
int min = Int32.MaxValue;
int max = Int32.MinValue;
int count = 0;
for (int i = 0; i < cc.Count; i++)
{
Component comp = cc[i] as Component;
if (comp.GetType() == type)
{
count++;
string name = comp.Site.Name;
if (name.StartsWith(type.Name))
{
try
{
int value = Int32.Parse(name.Substring(type.Name.Length));
if (value < min)
min = value;
if (value > max)
max = value;
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
}
}
}
if (count == 0)
return type.Name + "1";
else if (min > 1)
{
int j = min - 1;
return type.Name + j.ToString();
}
else
{
int j = max + 1;
return type.Name + j.ToString();
}
}
My implementation is simpler:
String INameCreationService.CreateName(IContainer container, Type dataType)
{
if (dataType == null)
{
throw new ArgumentNullException("dataType");
}
// The base component name is the Type name with the first character lower case
String name = Char.ToLower(dataType.Name[0]) + dataType.Name.Substring(1);
int number = 1;
// If the container parameter is null, no container search is needed
if (container != null)
{
// Increment counter until we find a name that is not in use
while (container.Components[name + number.ToString()] != null)
{
++number;
}
}
return (name + number.ToString());
}
I also added implementations of INameCreationService.IsValidName and INameCreationService.ValidateName. The IsValidName method returns a Boolean and does not throw any exceptions. The ValidateName method can throw an exception to return the exact reason why the candidate name fails. Generally, the code structure of the two methods should be similar, differing primarily in how they communicate an error back to the developer.
bool INameCreationService.IsValidName(String name)
{
// Cannot be null or empty
if (String.IsNullOrEmpty(name))
{
return false;
}
// Cannot start with a number
UnicodeCategory uc = Char.GetUnicodeCategory(name, 0);
if (uc == UnicodeCategory.DecimalDigitNumber)
{
return false;
}
// Cannot have anything other than letters and numbers
foreach (Char c in name)
{
uc = Char.GetUnicodeCategory(c);
switch (uc)
{
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.DecimalDigitNumber:
break;
default:
return false;
}
}
return true;
}
void INameCreationService.ValidateName(String name)
{
// Cannot be null
if (name == null)
{
throw new ArgumentNullException("name");
}
// Cannot be empty
if (name.Trim().Length == 0)
{
throw new ArgumentException("name cannot be of zero length");
}
// Cannot start with a number
UnicodeCategory uc = Char.GetUnicodeCategory(name, 0);
if (uc == UnicodeCategory.DecimalDigitNumber)
{
throw new ArgumentException("name cannot start with a number");
}
// Cannot have anything other than letters and numbers
foreach (Char c in name)
{
uc = Char.GetUnicodeCategory(c);
switch (uc)
{
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.DecimalDigitNumber:
break;
default:
throw new ArgumentException("name must contain only letters and numbers");
}
}
return;
}
While there is a little more refactoring possible, that will be good enough for now.