Main

February 6, 2008

More fun with Value Converters

I've blogged about IValueConverters and WPF many times in the past:

I've got another. This new one may seem a bit obscure, but it has proven useful. Here's what I wanted to do: conditionally, based on the enabled state of a button, swap between two different images, without special code, and without needing to use a style or a template; ideally the solution would not require an events or triggers.

So, I created the ConditionalStringConverter:

 [ValueConversion(typeof(Boolean), typeof(string))]
  public class ConditionalStringConverter :IValueConverter
  {
    /// <summary>
    /// Converts a bound boolean value to a conditional string value.
    /// </summary>
    /// <param name="value">The boolean value (as a Boolean)</param>
    /// <param name="targetType">(ignored)</param>
    /// <param name="parameter">A string of the following format:
    ///    {"value" Matches against this Boolean Value: True or False}?
{Return This Value if matches}:{Return this value if no match}
/// For example: True?BackBtn.png:BackBtnDisabled.png /// If the bound boolean property is True, this converter returns
BackBtn.png. If False, it returns BackBtnDisabled.png
/// </param> /// <param name="culture">ignored</param> /// <returns></returns> public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture) { if (string.IsNullOrEmpty((string) parameter)) { throw new ArgumentNullException(
"Converter Parameter must be specified when using
the ConditionalStringConverter."
); }

string[] condition = ((string)parameter).Split('?');
if (condition.Length != 2) {
throw new ArgumentOutOfRangeException(
"Converter Parameter improperly specified in ConditionalStringConverter."); }

string[] values = condition[1].Split(':');
if (values.Length != 2) {
throw new ArgumentOutOfRangeException(
"Converter Parameter improperly specified in ConditionalStringConverter."); }

if (value is Boolean) {
bool compareValue = Boolean.Parse(condition[0]);

if ((bool)value == compareValue) {
return values[0];
}
else {
return values[1];
}
}
else
{
throw new ArgumentException(
"ConditionalStringConverter converts only booleans", "value");
}
}

public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}


Before I walk through my simple solution, here it is in use:


<Button
    x:Name="btnBack"
    DockPanel.Dock="Top"
    IsEnabled="{Binding Path=IsNotAtRoot}"
    Click="btnBack_Click" Style="{DynamicResource ImageButtonStyle}">
   <StackPanel
        Orientation="Horizontal">
       <Image
            Source="{Binding Path=IsEnabled,
Converter
={StaticResource conditionalConverter},
ConverterParameter
=True?BackBtn.png:BackBtnDisabled.png,
RelativeSource
={RelativeSource AncestorType={x:Type ButtonBase},
Mode
=FindAncestor}}"
            Height="24"
            Width="24" />
       <TextBlock
            VerticalAlignment="Center"><Run Text="Back"/></TextBlock>
   </StackPanel>
</
Button>

The important part of the example that uses the new value converter is right here:
ConverterParameter=True?BackBtn.png:BackBtnDisabled.png,

Based on the boolean binding, the value converter compares the value of the binding to the boolean value specified as the converter parameter. If the values match, the value converter returns the first value. If it does not, it returns the second value. If you're familiar with the C# conditional operator (?:), this should be familiar syntax.To make the image swap, I resorted to using a RelativeSource binding with an AncestorType of ButtonBase.

image


What's nice about this solution is that the Image's Source parameter accepts the String file name and happily converts it as necessary to the image (which in my application is a resource).And no, I'm not done with the button yet -- so it's a bit ugly.


image

February 3, 2008

Remember the Power of the Path, young WPF'er

Binding's in WPF can make quick work of getting values from object properties.

I've created a class with a property:

    public XmlElement SelectedVideo
    {
      get { return _selectedVideo; }
    }

This Property is  of Type XmlElement. The XmlElement it binds to typically is structured like this:

<Video title="Anonymous Methods" source="Data\anonmethods.wmv" 
timecode="00:01:00" />

With a tiny bit of binding magic, I can easily fetch the title from the element and display it within a textblock:

<TextBlock Text="{Binding Path=SelectedVideo.Attributes[title].Value, Mode=OneWay}"

Since this is a read-only XmlElement, I've set the Mode to be OneWay (as it can't be updated via the TextBlock). Since the element may change, I need to use OneWay binding. OneTime wouldn't work as the SelectedVideo element may change.

The slick part is that the binding syntax allows me to use the indexer on the Attributes object. The only odd thing is that it doesn't need any quotation marks or delimiters.

I'm not using XPath bindings here because I'm not binding directly to an Xml Data Provider in my application (not always at least).

January 27, 2008

Politician Bobble Heads using Silverlight

I have no idea what inspired this.

image

See for yourself here. You'll need Silverlight 1.0 installed.

This is in no way any indication of any political affiliation -- or any indication of anyone I may vote for in the primary or general election. I had actually hoped to get more "heads" done this afternoon, but I ran out of time after these two.

The Silverlight code was very simple in this first edition. I added a few Mouse handlers:

rootElement.addEventListener("MouseMove", 
Silverlight.createDelegate(this, this.handleMouseMove));

The handler above was used to move the hand around:

handleMouseMove: function(sender, eventArgs) 
{    
  var hand = this.control.content.findName("hand");
  var pt = eventArgs.GetPosition(null);
  
  hand["Canvas.Left"] = pt.X - hand["Width"] / 2 ;
  hand["Canvas.Top"] = pt.Y - hand["Height"] / 2;
}

The mouse down was even simpler:

handleMouseDown: function(sender, eventArgs) 
{    
  this.control.content.findName(sender.Name + "Bobble").Begin();
  this.control.content.findName("WhackHand").Begin();
},

January 24, 2008

A more enjoyable way to read about Project Management...

I read this book 6 or 7 years ago after being given a copy by a new development manager while working at Microsoft, and something today reminded me of it -- so I thought I'd recommend it to others (I couldn't remember the author!). I rarely enjoy reading books that are about project management, time management, ... they're so dry that after the first 10 pages, I've lost interest.image

Tom DeMarco's, "The Deadline: A Novel About Project Management" is one of my favorite project management books I've read -- and one of the very few that I've read cover to cover. Even better is that I actually felt like I learned something!

The book kept my interest as it's written as an interesting novel. From the book's "home page":

With his trademark wit set free in the novel format, DeMarco centers the plot around the development of six software products. Mr. Tompkins, a manager downsized from a giant telecommunications company, divides the huge staff of developers at his disposal into eighteen teams -- three for each of the products. The teams are of different sizes and use different methods, and they compete against each other . . . and against an impossible deadline.
With these teams, and with the help of numerous consultants who come to his aid, Mr. Tompkins tests the project management principles he has gathered over a lifetime. Key chapters end with journal entries that form the core of the eye-opening approaches to management illustrated in this entertaining novel.

I lost my copy a few years ago in one of my many moves and always wished I hadn't as I'd enjoy reading this book again.

Definitely recommended. It might just open your eyes to some better ways to manage a (software) project.

January 23, 2008

Is this a joke? "Linux For The Masses? Bet On This Winner."

Like a wild animal in the headlights of a car, I read this "interview" posted on LXer.com. I couldn't stop reading no matter how hard I tried to stop.

With top-of-the-line journalism and fact gathering like:

With Linux on the desktop going from a slow crawl to verging on an explosion, many have toiled with the question: How do we make this happen faster? A well-known Austin-based Linux Advocate thinks he has the answer.

Emphasis mine. 

Where is the explosion? People frustrated by Linux ready to explode? People tired of being flamed by others when they ask a simple question in a forum? People confused as to why there are 2 web browsers, 3 e-mail programs, 5 text editors, etc. installed? Users annoyed by the need to COMPILE an application before installation?

But there's more:

Here's the deal. Ken has come to the correct conclusion that the majority of people who use a computer haven't a clue that they have a choice in how it's operated. Enter the GNU/Linux Operating System. During his time as a level 3 tech support specialist, Ken heard hundreds of horror stories first-hand and he knows what problems Windows Users face. Ken began a company called HeliOS Solutions to meet the need of long-suffering market.

Did Ken not get the memo that the vast majority of people don't care about choice in a computer operating system? Make it work. Make it work like their friends computers so they can use the same applications, open documents, share files, etc.

The company that Ken runs (I guess -- I'm a bit confused about the company after reading the article), helps install Linux for enterprises and home users.

He [a coworker] gets the specs on the customer's computer and matches the distro to that machine so we don't have any ugly surprises when we get there. The last thing you want to see is a shiny new Lexmark printer sitting beside the computer when you walk in the door.

That makes for easy support - when your customers are all running different distributions of Linux. Supposedly, Lexmark doesn't make Linux compatible drivers. The horror! How dare they cater to their target market of Windows and the Mac OS X. Almost makes me want to go out and buy a Lexmark printer!

Since we offer two weeks free phone support, we want to make sure the distro out-matches the user's ability to screw it up. We've received phone calls less than one hour later because someone has tried a new driver or inadvertently messed up the bootloader.

I love that about Linux. I've downloaded a lot of Linux distributions over the years and installed them on stand-alone PCs and in virtual machines as experiments. On several occasions the install became so horribly messed up that I decided it was faster to reinstall than it was to try to fix the problem. But the attitude of "user's ability to screw it up" speaks to the design of the OS, not to the hidden talents of the user. It's easy to do poorly with poorly designed software.

We are arranging for a small town in California to agree to go "Microsoft free" for a period of time. Maybe a month, maybe only a week. The idea is to draw massive media attention to the effort and therefore introduce America in particular to Linux. We are going to gather some priceless information from this project.

Oh my freaking Linux. You've got to be kidding me. That's one of the stupidest wastes of people time that I've heard of recently. Seriously -- who really wins in this "project"?

I've spent three years banging on this issue. We hold the key of Freedom for tens of millions of people and that freedom is Linux.

Dude. Seriously. GET A LIFE.

Look, my counterpart in California spelled it out best. You don't call a chevrolet a chevrolet every time you say it. Here in the states, it is most often abbreviated to "chevy". Same with Ford. You don't always refer to a ford as a Ford/Lincoln/Mercury do you? It's the same with Linux.

Eh? Ford is Ford. Lincoln and Mercury are different companies owned by Ford. Just because you see them together at a car dealer doesn't mean the company name is Ford/Lincoln/Mercury.

Richie Chapman, a good friend of Lobby4Linux and K4K stated it well .... He said that he installs Linux on others computers for free not because he's such a nice guy, which he actually is. He does it out of fear that if enough people aren't using Linux when Microsoft makes their "big move", then we will be just that easy to sweep away with the next DMCA-type midnight bill passage. People need to wake up and get involved. If not in my effort, than in someones. It's getting to the point where Microsoft is going to have to do something drastic to get rid of Linux as a competitor. Large numbers of users who are not afraid to stand up to a giant would be a good place to start.

What?! Is this a war? What "big move"? Should I be stockpiling RAM chips? Or getting ready to burn Linux DVDs? Microsoft? My chip-implant is ready to do your bidding now ....

I'm not going to give any additional support to their website by linking to it, but they have a wonderful tag-line:

We promise...90% of your computer problems are going to disappear forever.

Making it easier to deploy a web site or application on IIS6 or IIS7

Have you struggled to deploy a Microsoft hosted ASP.NET web site from one machine to another? Or have you wanted to easily archive the settings/files/etc. so that you can roll back changes later if needed (could be very useful for testing an update for example)?

Check out the new Microsoft Web Deployment Team announcement here on their new blog.

They just announced a new deployment tool that supports moving configuration, content, SSL certificates and the other types of data that is associated with a web server. It's command-line driven for now (and with the output being very configurable, including XML), it would be easy enough to put a GUI front-end on the tool if one were so inclined.

It's a tech preview right now and a handful of walkthroughs are available for download.

January 20, 2008

Ouch! That hurt!

During an attempted online chat session with a web host (yes, this seems to be an ongoing problem with me ... but from now on, I guess I'll need to be a lot more careful!)

 

image

January 19, 2008

WPF Object Browser Demonstration

With no particular goal, except to write some interesting WPF code, I've created the "Wired Prairie Object Browser." The project, while somewhat useful in its current state, is primarily designed to showcase WPF.

You can see an enlarged screen shot by clicking on most of the images.

image

If you're familiar with the Macintosh OS X Finder view, you'll see the similarity between the user interface of this application. The core aspect of this user experience is a horizontal scrolling area that expands to the right with the children of the currently selected item to the left.

Best explained by a walk-through.

The application starts like this:

image

I clicked on the first node:

image

And so on:

image image

Clicking around changes the various lists:

image

Support for individual per-column filtering is included:

image

image

The application also supports dragging new assemblies onto the application -- which adds them to the list of objects and namespaces. The application always defaults to opening mscorlib.dll by default (home of System.String). (To add a new assembly, just drag the file name from an Explorer window onto this application).

Private and protected members are highlighted with different images:

image

The application makes heavy use of DataTemplates in WPF to do it's magic.

For example, there are 3 types of lists in the application: the horizontal list which holds the various namespaces and classes lists; the list of namespaces and classes; and the list of properties and methods and events.

<DataTemplate
    DataType="{x:Type local:ContainerNodeCollection}">
    <Border
        BorderThickness="6"
        BorderBrush="Silver">
        <DockPanel
            LastChildFill="True">
            <TextBox
                Name="FilterTextBox"
                TextChanged="FilterTextBox_TextChanged"
                DockPanel.Dock="Top"
                Margin="2,2,2,2"
                Style="{DynamicResource FilterTextBoxStyle}"></TextBox>
            <ListBox
                Name="CollectionListBox"
                ItemsSource="{Binding Mode=OneTime}"
                SelectionChanged="ListBox_SelectionChanged"
                Loaded="ListBox_Loaded"
                MinWidth="300"
                MaxWidth="300"
                MinHeight="30" />
        </DockPanel>
    </Border>
</DataTemplate>

Above is the DataTemplate for the list which shows namespaces and classes.

Here's a snippet from the template that shows information about the specific details of a class:

<DataTemplate
    DataType="{x:Type local:ComboCollection}">
    <DataTemplate.Resources>
        <!-- these are privately used within the combo collection -->
        <DataTemplate
            DataType="{x:Type sysreflect:PropertyInfo}">
            <DockPanel
                LastChildFill="True">
                <Canvas
                    Height="16"
                    Width="16">
                    <Image
                        Height="16"

This one is particularly interesting from a WPF perspective as it's redefining a resource locally within the DataTemplate. Depending on the use of the PropertyInfo object, it displays very differently.When it's shown in this list, it's just a small icon, with a title. When it's shown as detail of a property, it shows with more detail:

image

On the left is the template contained as a resource within a DataTemplate (the example snippet above), and the right is the other. I could have used a complex set of DataTriggers, but this seemed like the simpler, more maintainable solution rather than relying on a single DataTemplate that had to have knowledge of all the places it could be used.

The code treats the protected or private state of a method as a sneaky image overlay using a DataTrigger:

 

<DataTemplate
    DataType="{x:Type sysreflect:MethodInfo}">
    <DockPanel
        LastChildFill="True">
        <Canvas
            Height="16"
            Width="16">
            <Image
                Height="16"
                Width="16"
                Source="Method.png" />
            <Image
                Visibility="Collapsed"
                Height="16"
                Width="16"
                Source="Key.png"
                Name="ProtectedImage" />
            <Image
                Visibility="Collapsed"
                Height="16"
                Width="16"
                Source="private.png"
                Name="PrivateImage" />
        </Canvas>
        <StackPanel
            Orientation="Vertical"
            HorizontalAlignment="Stretch">
            <TextBlock
                Text="{Binding Path=Name, Mode=OneTime, FallbackValue=Method}"
                FontWeight="Bold"
                VerticalAlignment="Center" />
      </StackPanel>
    </DockPanel>
    <DataTemplate.Triggers>
        <DataTrigger
            Binding="{Binding Path=IsFamily, Mode=OneTime}"
            Value="True">
            <Setter
                Property="Visibility"
                Value="Visible"
                TargetName="ProtectedImage" />
        </DataTrigger>
        <DataTrigger
            Binding="{Binding Path=IsPrivate, Mode=OneTime}"
            Value="True">
            <Setter
                Property="Visibility"
                Value="Visible"
                TargetName="PrivateImage" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

 

The DataTemplate directly maps to the MethodInfo data type. If the IsPrivate property of the MethodInfo class is True, the Private image is shown. The private image is just a small icon that overlays the other image rather than having multiple combinations of images necessary to represent all of the different states.

Some of the challenges were managing the horizontal list so that it always reflected all of the highlighted elements.

The main UI for the application is extremely simple:

<ListBox
    Grid.Row="1"
    Margin="8"
    x:Name="lbClasses"
    ItemsSource="{Binding Path=Data}"
    VerticalContentAlignment="Stretch"
    Style="{DynamicResource ContainerListBoxStyle}"
    ScrollViewer.VerticalScrollBarVisibility="Disabled">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel
                Orientation="Horizontal"
                IsItemsHost="True"
                CanHorizontallyScroll="True"
                CanVerticallyScroll="False" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

A list box that is bound to a property called data. Everything else is created using Styles and DataTemplates! By placing the ListBox in a DockPanel and setting the CanVerticallyScroll property to False, the contained list boxes (with the details of the assemblies and classes) scroll independently as each is the height of the main window (rather than each ListBox being a unique height creating a jagged user interface).

The application code has to do a bit of hunting when an item is clicked. The tough part is finding the containing ListBox and removing unnecessary data columns. If you have a ListBoxItem and want to find the parent ListBox, you can use the ItemsControl.ItemsControlFromItemContainer static method.

 

private void ComboCollectionListBox_SelectionChanged(object sender, 
SelectionChangedEventArgs e) { foreach (object o in e.AddedItems) { FrameworkElement fe = e.Source as FrameworkElement; if (fe != null) {

DependencyObject d = (DependencyObject)fe;
while (d != null && d.GetType() != typeof(ListBoxItem ))
{
d = VisualTreeHelper.GetParent(d);
}
ListBoxItem lbi = d as ListBoxItem;
ListBox parentListBox = ItemsControl.ItemsControlFromItemContainer(lbi)
as ListBox;
int depth = parentListBox.Items.IndexOf(lbi.DataContext);

for (int i = _data.Count - 1; i > depth; i--)
{
_data.RemoveAt(i);
}

_data.Add(o);
}
}
}


The filtering right now is simple:

CollectionView cv = (CollectionView)
CollectionViewSource.GetDefaultView(lb.DataContext);
if (cv != null)
{
if (cv.CanFilter)
{
cv.Filter = delegate(object o)
{
if (o != null)
{
string title = o.ToString();
if (!string.IsNullOrEmpty(title) &&
title.IndexOf(((TextBox) sender).Text,
StringComparison.InvariantCultureIgnoreCase) >= 0)
{
return true;
}
}
return false;
};
}
}

Once the listbox has been located (lb), it retrieves the CollectionView wrapping the data (which is automatically added by WPF), and then sets the filter. The filter just checks whether the title of each element contains the filter text specified by the user in the text box as each character is typed.


By no means is this a complete object browser. There's many details you'd want to add to make it generally useful.


The source code is available here. I used Visual Studio 2008 -- but no 3.5 features (if you're using an older version of VS, you'll need to recreate the project. All the necessary files are included).

January 17, 2008

When to switch to WPF?

Microsoft has provided little in the way of guidance as to when a product should adopt or switch to WPF. Recently, Microsoft published a whitepaper with a small amount of guidance here, entitled "Deciding When to Adopt Windows Presentation Foundation." The whitepaper describes mostly technology choices and integration options. If you're not familiar with WPF and it's integration options at a high level, the whitepaper is worth a read. I'd suggest it might be good for a technical manager who's new to WPF. If you've gotten your hands dirty in WPF though -- it won't be very enlightening.

If you're just interested in the final conclusion, I've snipped that section and put it here:

In conclusion:

  • If you have an existing Windows Forms application or are building a traditional forms-based application and are looking for a mature technology to use with mature tools and component support then Windows Forms is a good fit.
  • If you have an existing Windows Forms (or MFC/Win32) application that could benefit from some of the advanced presentation features of WPF, you can add WPF to your existing project.
  • If you’re wanting to create a new experience for your users that is rich, interactive, sophisticated, or highly custom or branded, WPF is Microsoft’s next-generation platform for your project today.
  • If you’re targeting the web, Silverlight shares the same development model as WPF but is optimized for a lightweight, cross-platform runtime. Investing in either WPF or Silverlight nets you the skills, tools, and assets for both platforms.

With all of these choices, you can be assured that all of these platforms will be supported for years to come.

The MFC/Win32 application that isn't using managed code today has the most at risk -- as the application would require a large additional framework installation.

January 16, 2008

.NET 3.5 source code is available officially ...

You'll need to install a hot fix first ... once you sign in using your Windows Live account, (and possibly updating your profile like I had to), you'll run the install.

Don't bother with the File Transfer Manager. It's definitely overkill for this 1.17MB file. Just click the Download link I've highlighted (it took 10-15 seconds before the standard download dialog appeared).

image

It unfortunately defaults to unpacking files in the root directory of your primary drive so be sure to put it somewhere else (or remember to delete the files when you're done):

image

It unpacks the hotfix file into the directory you specified:

VS90-KB944899.exe

Run that.

image

image

Wait for it. Wait for it. Wait!

It took my machine about 5 minutes to run the patch for some reason.

image

I don't understand how a 1.17 MB patch could take so long ...?

image

Then you can follow the instructions on Shawn Burke's blog to finish.

The only problem I encountered was that until I stepped into the framework code once, I wasn't able to just double-click on a call stack entry to view the source. VS would prompt me for the location for the source. Once I did stepped in, accepted the EULA, it worked like a charm!

image

January 12, 2008

Chumby Photo Viewer Source code

I mentioned I created some software for the Chumby recently. I'm releasing portions of it for public consumption here. I'm not including the automatic e-mail uploader as that requires a custom component I'm not able to distribute. The php code just expects the images to be stored in a particular directory. It doesn't matter how they get there.

image

Chumby rule #1 though is that you must use non-progressive JPEG images. If you use progressive images, expect that the Chumby won't show them.

Chumby rule #2 is to keep the images small. I reduce the images to 640x480 (although the screen is 320x240) and compress them to 60% quality and am very happy with the images. You can play with the settings -- but you'll want to keep the images small so they download quickly. One of the settings (hard-coded in the Flash ActionScript) is the length of time between photos. Adjust that to suit your needs. It's eight seconds in the code I released.

The zipped package is here.

There's a key that is sent with every request to the server. The Flash ActionScript is hard-coded to the key ... just change it to whatever you want:

var apikey:String = escape("websitepassword"); // your web site password (must match what the web server expects)

The PHP has the same key and they must match.

If you don't want people to browse your photos directly, make sure you set up folder/directory permissions to prevent direct web browsing (through htaccess on an Apache web site for example).

Some snippets of the code I used follow.

var getImagesXMLComplete:Boolean = false; 

function getImages() {
getImagesXMLComplete = false;

txtStatus.text = "retrieving image list...";
this.getImagesXML = new XML();
this.getImagesXML.target = this;
this.getImagesXML.ignoreWhite = true;

this.getImagesXML.onLoad = function(success:Boolean) {
if (success) {
txtStatus.text = "success: " + this.firstChild.toString();
processImages(this);
}
getImagesXMLComplete = true;
}
this.getImagesXML.load(baseurl + "/getImages.php?" +
"startatfilename=" + lastfilename);
}



I parse the Xml this way (by grabbing the key data from the Xml file returned.)


function processImages(xml:XML) { 
var images:Array = XPathAPI.selectNodeList(xml.firstChild,
"/images/image");
if (images == null) { return; }

for(var i:Number = images.length - 1; i >= 0; i--) {
var photo:Object = new Object();
try {
photo.imageUrl = images[i].attributes.id;
photo.imageIndex = images[i].attributes.index;
trace("image: " + photo.imageUrl);
lastfilename = photo.imageUrl;
} catch(ex) {
txtStatus.text = ex.toString();
}
_photos.push(photo);
}
// trim if we've got too many photos collected
if (_photos.length > 200) {
_photos = _photos.slice(0, 200);
// reset if we've gone too far!
if (index > _photos.length) { index = 0; }
}

randomizePhotoList();
}



I added a debug mode to my chumby application:


var isDebugMode:Boolean = false; 
clipReminder.onMouseDown = function()
{
isDebugMode = true;
txtStatus._visible = true;
clipReminder._visible = false;
}

If the user touches the screen while the initialization is occurring, it puts the widget into a debug mode. The debug mode greatly helped me figure out what was going on as the emulator often worked when the Chumby did not. The debug mode shrinks the image to a small size and puts tracing text that I've scattered throughout the application on the Chumby's screen.


I randomize the list in place to keep memory use as low as possible:


function randomizePhotoList():Void { 
var i:Number = 0;
trace("randomizing");
for(i = _photos.length; i > 0 ; i--)
{
var rnd:Number = Math.round(Math.random() * i);
rnd = (rnd > i ? i : rnd);
var url:Object = _photos[i];
trace("swapping " + i.toString() + " with " +
rnd.toString());
_photos[i] = _photos[rnd];
_photos[rnd] = url;
}
}

If you have questions, feel free to email or post a comment.

January 7, 2008

Don't put your product prices in the URL!

This really happened and I am not making this up.

I thought I found a new web host, so I was going through their ordering process and clicked "order".

 

I noticed the URL had an odd number at the end:

https://mydomainhost.domain/autosignup.asp?AdminAccNum=RSWEB2&ReturnURL=
http://mydomainhost.domain/v3/custom.asp&totalplan=52.75

I've changed the name of the host to protected the innocent.

So, I changed the number, at first to something slightly smaller. Then, to something negative:

image

Yes. It says free.

I'm far too honest to take advantage of this clearly amateur web mistake, but ... come on!

Download a free e-book copy of Introducing Microsoft LINQ

If you're interested in learning LINQ, Microsoft is providing an e-book download of the book Introducting Microsoft LINQ for free. The only catch is that you agree to signup for a newsletter.

Get it here.

January 5, 2008

Third Party Component Web Site Turn offs ...

You're interested in a product ..., some searching,

"AH HA! This sounds good ..."

Click on the link ... and you're greeted by the product's web site. In this case, I'm talking about 3rd party components or products that you might purchase to aid in the development of an software application. But this can be easily generalized for nearly any product.

My list of things I look out for ...

  • Grammar or spelling mistakes. I'll definitely forgive mistakes in a blog post, but anywhere else on your site .... grrrr.
  • Lack of product or screen shots (almost every component or 3rd party product can be shown in some way, either directly or through a demonstration application).
  • Confusing pricing or licensing terms
  • Non royalty free licensing terms
  • Click-through agreements for end-users (end users of the product using the 3rd party widget)
  • A support forum with only a few posts
  • A support forum where people keep wondering where the support is ... (this is usually the kiss of death for me)
  • No plans for future development
  • No clear release date for the product (how old is the current version, etc.)
  • Non professional web sites (eh, you know what I mean)
  • No contact information (like an e-mail address, phone number, address, something).
  • Web site not current. If your web site isn't current, I wonder if you're actually working on the product...
  • A blog that isn't updated. Sure sign of lack of interest on the vendor's part.
  • No trial edition.

What would you add?

If it's a component I'm interested in, I won't even bother downloading a trial if I don't feel like the list above has been satisfactorily met by the vendor.

When do you buy a third party component product rather than build?

I was just reading a recent post on the VistaDB blog titled Looking ahead to 2008, and back at 2007. (What's funny is that I have no idea who is writing the blog post because they don't seem to post their name though they're trying to be 'transparent' about their business and plans!).

In any case, this was part that made me think:

Where we are lacking is in sales, marketing and business partners. The website and store have to be overhauled, and the training of new users to ADO.NET and VistaDB needs serious work. This is not just documentation; it is also samples, case studies and tutorials. We do have a few strong partners, but we need to do much more to get the word out about VistaDB. Not just about the engine, but about the philosophy of managed code, and why we are doing all this in the first place (the business case).

As a hobbyist developer at home, my "buy a widget" budget is extremely limited. It's really hard to convince the CFO (wife), that I need some widget or tool for an application I'm writing for home use (I've been successful a total of one time in 10 years in buying a widget). So, I'll spend hours scouring the Internet for other options, or writing it from scratch (or deciding I don't need as badly as I originally thought).

At work, we can buy components of course that fit various needs. I'm sure this is true of many development shops, from internal IT tools, to departmental applications, to commercial applications that are being sold.

But, where do you draw the line (both personally and for work)? How much effort are you willing to put into a 'widget' before you decide that it's worth buying instead? In this example, the product they're selling is VistaDB 3.3:

... is the world's first  100% fully managed and typesafe embedded SQL database designed specifically for Microsoft .NET Framework, Compact Framework, 64-bit .NET and Mono. Build fully managed and typesafe ASP.NET and WinForms applications using C#, VB.NET and other CLR-compliant languages.

At $169 currently, it's not a very compelling product if I'm being frugal about money spent (raw dollars, ignoring time here). Looking around at free options that are immediately available from Microsoft as competitors, I could use an Access database or SQL Server Express database, or even a SQL Server Compact database. All are decent options -- and available for no charge from Microsoft. They each have limitations on the number of connected users and the size of the database, but for many applications (especially home users), these limitations aren't an issue. The compact edition is especially nice as it doesn't require any special installation.

If my needs were relatively simple, I'd go with one of the free options -- no questions asked. If my needs scaled, I'd likely go with SQL Server as it has the best growth and management opportunities (well known, etc.).

So, where does that leave a product like VistaDB? If you believed that having a 100% fully managed embedded SQL database is important, then it may be a good option and worthy of potentially purchasing (after a thorough test). But, if you're not as interested or don't believe that it's that big of a deal, then there are many database options that you could employ in an application.

Sometimes it makes sense to build yourself. However, writing a decent database should rarely be on the "build it yourself" lists (I don't consider an XML file to be a database). Clearly, either buying a database or taking advantage of a free option is really the only choice when considering a database.

Although I've been talking about mostly databases in this post, I'm more interested in hearing the answer to what types of things you'd be more likely to buy rather than build? Is there some price point where you decide that the 3rd party product costs too much, so you'll look elsewhere?

 

What should a company like VistaDB do for hobbyist developers? Should they offer free versions for home use? Or, provide free versions to developers who link back to their site (and provide a review / feedback)? Or ...

There's no way for example, I'd buy VistaDB for home use, which means I'm not likely to talk about successes, provide feedback, etc., on my blog, which would amount to "free" advertising. It's a tough call for any vendor.

January 3, 2008

XamlPad is (mostly) dead. Long live Kaxaml!

If you've used XamlPad from Microsoft ... you probably have a love-hate relationship with it. You love it because it's reasonably quick at doing a proof of concept or test of a WPF feature. You love it because it's simple. You hate it because it has a bunch of odd quirks with the text editor, the fact that it really has only a few useful features ...

Kaxaml is a better XamlPad in many respects. It's got a bunch of nice little features including:

  • Intellisense!
  • A color picker (that is better than the one in Expression!)
  • a scrubber:
  • image
  • You can paste images into the editor!
  • It can have multiple files open at one time
  • Did I mention intellisense (and a color picker!)

 

The full announcement is here.

 

Pay attention to the default install -- it may change your default file handlers for Xaml files in a way that isn't to your liking:

image 

A definite recommended add to your WPF/Xaml editing toolbox.

January 1, 2008

No more programming books from Charles Petzold?

Charles announced he isn't likely to write many more programming books in the future due to dwindling book royalties. I suspect the availability of information on Internet has had something to do with that (and of course the rapid change of development technologies). (Although from looking at my purchases at Amazon in 2007, you wouldn't see much of a change in my technology focused book purchasing pattern when compared to previous years).

It's definitely a sad day for Windows users and programmers. Many Windows programs were completed with higher quality and more rapidly as developers had a better understanding of the Windows API thanks to Charles' books. Developers were able to spend time on the functionality rather than the plumbing of a Windows application. (And in some cases, I wish more developers had read the books a bit more carefully!).

I know I owe a lot of my early knowledge of the Windows API to Charles' now classic Programming Windows books.

Thanks Charles!

Are companion applications practical in the enterprise?

Brad Abrams provided a few predictions for IT-Technology in 2008. I started posting a comment, but it was getting a bit long. So, here are my comments with some snippets from Brad's post.

#1) User Experience Reaches the Enterprise

In 2008 we will see several major enterprises start efforts to build UX centric applications that increase worker productivity, reduced transaction costs and increase pull through as the UX meme of the consumer facing world leaks into the enterprise. The days of the battleship gray, forms of data application as the king of the enterprise are numbered because of an imperative towards richer visualization of complex and interconnected data. ....

Battleship gray forms are cheap and easy for an enterprise developer. The tools make them easy. Even with a toolbox full of IDEs from Microsoft, doing something outside of a standard data entry form is complex. Sure, I can make a quick gradient -- but that's just eye candy, not visualization.(Though some days even picking the colors can be a challenge!)

<RANT>It's still hard to do a nice WPF data entry form that can take advantage of WPF features without resorting to grids in grids in grids</RANT>

Data visualization is a tough nut to crack -- it's hard enough some days to get a simple table to layout correctly the way you want in WPF/HTML/Silverlight/Flash/etc., but actually deciding there's a better way to "visualize" the data is a real art and skill that many developers lack. It's not their fault either -- I call it art for a reason. It's a real skill, not everyone is an artist, a musician, a chef, a leader, or even a computer programmer. We each have our unique combination of talents that make us, well, unique.

Although there are definitely exceptions to this, 'scientists' are often not the best artists. Look at the history of computer software -- look at how many UGLY applications you've used (I'm ignoring usability entirely as that's another skill, but one that is more trainable).

The art is much more difficult than the battleship gray form. Plus, it takes a lot more time.

Rather than trying to change the way data is visualized, the first step in most enterprises is to simplify and make the data more accessible through better use of colors and fonts (usually font weight, not different fonts like this). Once they've nailed that aspect of an application or module, they might step back and see if there's better ways to actually present the data (graphs, 3D, etc.). In far too many cases, the expense of doing that development work will exceed the end user benefit. (And sometimes, a graph may require more user time than just displaying the data formatted nicely).

3. The Companion Applications Become Practical. While RIA and AJAX application categories continue to grow, many consumer facing web applications and enterprise applications developers realize there is a need for desktop exploitive applications as well ...

IT shops don't like to manage workstations. They don't like to install upgrades. They want to move users to web versions of applications to simplify the management of their complex IT needs. Sure, there will be companion applications that are occasionally necessary as the web is not yet capable of delivering a complete 'desktop' experience (think hardware integration, real time feeds, etc.).

What meaningful application wouldn’t benefit from a pairing like that of Outlook and Outlook Web Access? In the past it has been prohibitively expensive to build these applications, but with the circa 2008 technology such as .NET Framework 3.5 and Silverlight, it is finally becoming practical to have a single codebase that fully exploits the desktop and offers a rich web experience.

Really? It's really complex to build an application that can run on the desktop and run on the web. Some aspects can be shared, but the way you would write applications is often very different on the web when compared to an installed application. His example, Outlook is perfect. Outlook 2007 uses a local cache and from my experience about 100MB of RAM when running (Outlook 2007 is using 91MB right now on my home machine). It would be completely impractical if a web version of Outlook used 100MB of RAM for every user who connects. So bad it would be nearly criminal!

The user interface is fundamentally different (one has threads, the other doesn't, the display needs are completely different, sending mail is different, etc.) and although you could extract some shared code again, in the end I don't believe it would pay to do so. It's cheaper to make two distinct clients of the Exchange server, sharing minimal code and mostly protocols (web services?) and techniques only.

More likely though is that a development shop would decide on one version as it would cost too much to maintain two. Microsoft (and Outlook) is unique with the army of developers that are available. Corporate IT and enterprise developers have no such luxuries of time and resources. They can't cross train in multiple languages, tools, ... it just takes too much time and energy.

If an enterprise was developing it's own email front end, they'd do it once and use the technologies that make sense. I could see using Silverlight if they were using ASP.NET, mixed with HTML to produce an effective, modern email client.

I'd change #3: the move to the web and away from the desktop will continue to grow (which may improve the UX of some applications). Developers will start to use Silverlight, Flash, Flex, to enhance the usability of their web applications.

December 31, 2007

Sometimes you just need a good GC.Collect ...

From the VistaDB blog: To Collect or not to Collect.

One of the first things it seems most people do when they get the code to VistaDB is remove the GC.Collect() call we have in the MinimizeMemory code for the Tree class.  This class specifically is used to hold portions of the database, indexes, etc in RAM.  Periodically it is combed to attempt and minimize the number of nodes kept in RAM.

They've done a bit of testing with 3.5 and found the performance with and without the call to be much more comparable than previous versions.

Does this mean you should use GC.Collect? Generally, the answer is still no. I've noticed a few times that a GC.Collect with Marshal.FinalRealseComObject can improve reliability in certain types of managed COM Interop application scenarios, but I haven't generally found too many scenarios where a GC.Collect was useful.

Their testing:

2.0 (no SP installed):

Dot Net 2 Runtime Results
Without Collect
Runtime: 259 Seconds
Peak Memory: 289,742 Kb
Ending Memory: 165,756 Kb

With Collect
Runtime: 290 Seconds
Peak Memory: 68,788 Kb
Ending Memory: 58,088 Kb

And with 3.5 installed:

Without Collect Dot Net 3.5 Runtime
Runtime: 233 Seconds
Peak Memory: 62,880 Kb
Ending Memory:  70,824 Kb

With Collect Dot Net 3.5 Runtime
Runtime: 278 Seconds
Peak Memory: 53,992 Kb
Ending Memory: 56,300 Kb

December 30, 2007

A Windows Service, and configuration editor, all in one ...

I just finished writing a Windows Service for a utility I needed (for a Christmas gift actually!). Even though the first version is only used on one server in my house, I wanted to have a passable configuration editor for it, rather than relying on XML files, the registry editor, etc.

image

Additionally, I wanted to be able to run the service as a stand-alone EXE for the purposes of debugging (rather than attaching to the service with the debugger). There are a number of solutions to this problem, but here's the approach I took.

This code is from a file, Program.cs in my project:

namespace UploadingService
{
  static class Program
  {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main(string[] args)
    {
      if (args.Length > 0)
      {
        if (args[0].Equals("I", 
StringComparison.InvariantCultureIgnoreCase)) { BasicSettingsDialog dlg = new BasicSettingsDialog(); System.Windows.Forms.Application.Run(dlg);

return;
}
}
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Uploader()
};
ServiceBase.Run(ServicesToRun);
}
}
}


The important part of the code above is that I added the string array args to the static Main method, and then compared that to a command line flag, the letter "I". If that flag is present on the command line, I create a settings dialog (BasicSettingsDialog), and start the Windows message-pump loop by calling Application.Run with the dialog instance. When that returns, the process exits.


If the flag is not present, the service starts normally. I built my installer to include a shortcut to the service and also include the command line flag (as an argument below).


image


In the user interface code, I added a button to 'interactively' start the service (for debugging):

_uploader = new Uploader();
_uploader.InteractiveStart(null);

InteractiveStart is an internal method that calls the overridden ServiceBase method OnStart:

    internal void InteractiveStart(string[] args)
{
OnStart(args);
}

That, after a little work, calls into a static method in another class in my code:

    internal static void Start()
{
EventLog.WriteEntry("WiredPrairie Uploading Service", "WiredPrairie Uploading Service Started.");
s_emailThread = new Thread(new ParameterizedThreadStart(s_Instance.Run));
s_emailThread.IsBackground = true;
s_emailThread.Start(null);
}

All it does is kick off a new thread with a singleton instance of my worker class.


I communicate to the thread (and the actual service) via the Registry, which is polled every minute for up-to-date settings with code like this:

using (RegistryKey software = Registry.LocalMachine.OpenSubKey("SOFTWARE"))
{
using (RegistryKey wp = software.OpenSubKey("WiredPrairie"))
{
using (RegistryKey ups = wp.OpenSubKey("UploadingService"))
{
emailAccountName = (string)ups.GetValue(@"AccountName",
"", RegistryValueOptions.None);
emailPassword = (string)ups.GetValue(@"AccountPassword",
"", RegistryValueOptions.None);
emailPop3Server = (string)ups.GetValue(@"Pop3Server",
"", RegistryValueOptions.None);
}
}
}

Saving settings is easy:

using (RegistryKey software = Registry.LocalMachine.CreateSubKey("SOFTWARE"))
{
using (RegistryKey wp = software.CreateSubKey("WiredPrairie"))
{
using (RegistryKey ups = wp.CreateSubKey("UploadingService"))
{
ups.SetValue(@"AccountName", txtEmailAccountName.Text,
RegistryValueKind.String);
ups.SetValue(@"AccountPassword", txtEmailPassword.Text,
RegistryValueKind.String);
ups.SetValue(@"Pop3Server", txtEmailPop3Server.Text,
RegistryValueKind.String);
}
}
}

By using the registry as a cheap and reliable communication channel, I didn't need to worry about interprocess communication challenges (named pipes, WCF, etc.), creating a custom file format (or XML), file permissions, etc... Yes, there is a concern about registry permissions, but since these settings are intended for use by a service, setting the expectation that my users are administrators isn't out of the question (and right now, the only user is ME!).


If you have questions, post a comment.

December 28, 2007

Memory Leak with Databinding in WPF

If you're using WPF and doing any databinding, make sure you read KB article at Microsoft carefully:

A memory leak may occur when you use data binding in Windows Presentation Foundation

A snippet from the KB article:

This issue occurs if the following conditions are true:

    • A data-binding path refers to property P of object X.
    • Object X contains a direct reference or an indirect reference to the target of the data-binding operation.
    • Property P is accessed through a PropertyDescriptor object instead of a DependencyProperty object or a PropertyInfo object.

 

In the following code example, the conditions for the leak are created.

<Label Name="MyLabel">
   <Stack Panel Name="MyStackPanel">
      <TextBlock Text="{Binding ElementName=MyStackPanel, 
Path=Children.Count}" /> </StackPanel> </Label>

 

Yes, it's really that simple! There are some work around methods, IF you happen to own the object you're binding to ... if not, you may need to create a facade/shim around the object.

November 4, 2007

Flipping images in 3D!

Ever wanted to flip some photos in 3D in WPF?

Well, I did. (honestly, I wanted to do with cover art and ITunes, but ITunes was throwing far too many "CATASTROPHIC" errors on it's COM interface that I gave up!)

Here's my sample project demonstrating flipping images in 3D using WPF. 12 x 9 photo thumbnails are loaded, and then semi-randomly, each image is flipped and replaced with a new thumbnail -- over and over.

image

The source code to this WPF 3.0 application is available for download here.

As coded, it scans your "Photos" directory in XP/Vista  and then slowly starts flipping each of the small thumbnails through your images. You can move around the viewport using "S" for Forward and "X" for backward and the cursor keys adjust the direction you're looking (up, down, left, or right).

image

 

image

The main XAML for the Window is simple:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="ImageFlip.Window1"
    x:Name="Window"
    Title="ImageFlip in 3D!"
    Width="640" Height="480" Foreground="#FFF4F4F4">

<Window.Background>
<
LinearGradientBrush EndPoint="0.001,0.044" StartPoint="0.001,0.5">
<
GradientStop Color="#FF000000" Offset="0"/>
<
GradientStop Color="#FF1C1C1C" Offset="1"/>
</
LinearGradientBrush>
</
Window.Background>

<Grid x:Name="LayoutRoot">
<
Viewport3D x:Name="vp3D">
<
Viewport3D.Resources>
</
Viewport3D.Resources>
<
Viewport3D.Camera>
<
PerspectiveCamera x:Name="cam"
FarPlaneDistance="45"
LookDirection="0,0,-1"
UpDirection="0,1,0"
NearPlaneDistance="1"
Position="0,0,10"
FieldOfView="45" />
</
Viewport3D.Camera>
</
Viewport3D>
</
Grid>
</
Window>


Nothing more really than a place to hold the 3D viewport and the camera.


I create each image as a pure rectangle (rather than trying to make images that fit, they're stretched a bit for the purposes of this code):

MeshGeometry3D mesh3d = new MeshGeometry3D();
mesh3d.Positions.Add(new Point3D(-0.5, 0.5, 0));
mesh3d.Positions.Add(new Point3D(0.5, 0.5, 0));
mesh3d.Positions.Add(new Point3D(-0.5, -0.5, 0));
mesh3d.Positions.Add(new Point3D(0.5, -0.5, 0));

mesh3d.TriangleIndices.Add(0);
mesh3d.TriangleIndices.Add(2);
mesh3d.TriangleIndices.Add(1);
mesh3d.TriangleIndices.Add(1);
mesh3d.TriangleIndices.Add(2);
mesh3d.TriangleIndices.Add(3);

mesh3d.TextureCoordinates.Add(new Point(0, 0));
mesh3d.TextureCoordinates.Add(new Point(1, 0));
mesh3d.TextureCoordinates.Add(new Point(0, 1));
mesh3d.TextureCoordinates.Add(new Point(1, 1));

image 

I set up some Rotation:

Transform3DGroup transformGroup = new Transform3DGroup();

// gotta set where we want to rotate around. We don't want to rotate around the world axis, which would be the default ...
RotateTransform3D rotateTransform = new RotateTransform3D();
rotateTransform.CenterX = xOffset;
rotateTransform.CenterZ = zOffset;
rotateTransform.CenterY = yOffset;
// we'll need a default axis angle so that we can animate it later ...
rotateTransform.Rotation = new AxisAngleRotation3D(new Vector3D(0, 0, 0), 0);
// move the object into the proper location in 3d space ...
transformGroup.Children.Add(new TranslateTransform3D(xOffset, yOffset, zOffset));
// add the rotation
transformGroup.Children.Add(rotateTransform);
// throw them together and put them into the group
gm3d.Transform = transformGroup;

_model3dGroup.Children.Add(gm3d);

AxisAngleRotation3D axis = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 180);
AxisAngleRotation3D axisFrom = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
Random rnd = new Random();
double rotateTime = rnd.NextDouble() * 5 + 2;

Rotation3DAnimationPlus rotate3d = new
Rotation3DAnimationPlus(gm3d,
rotateTransform,
axis, axisFrom,
new Duration(TimeSpan.FromSeconds(rotateTime)),
FillBehavior.HoldEnd, false);
rotateTransform.BeginAnimation(RotateTransform3D.RotationProperty, rotate3d);


The key about this is that the RotateTransform3D Center points (CenterX, etc.) are all based on where I've actually placed the image. By default, the images are all created on the origin of the Viewport (0, 0, 0). Because of that, I move them to a new location (the TranslateTransform3D object), then set their rotate based on that point. If the code didn't set the rotate transform based on their actual locations, they'd rotate around the origin, which although neat, wasn't the look I wanted.


I've created a new Rotation class (Rotation3DAnimationPlus), to handle swapping in new images automatically when the animation completes.


I've tried to do as much of the work in background threads, but WPF requires that some image work be done in the main UI thread unfortunately, so the animation isn't as smooth as I'd like (at least in my experience when loading images over the network). The code tries to grab thumbnails or create thumbnails rather than using the full size images. Thus, if you zoom, you'll see that the images are pixelated. You can disable that easily enough in code, but the memory requirements for the code shoot skyward!


I haven't tested the code thoroughly by any means, so it may not be suitable for anything other than a demo like this.


image

October 13, 2007

RIA: Rich INTERNET or INTERACTIVE Application?

There's a small debate going on right now ... what should the acronym RIA actually represent? There are 2 proposals:

  • Rich Internet Application
  • Rich Interactive Application

Adobe employees, such as Ryan Stewart, push Rich Internet Application, as that's what it was originally for. But Scott Barnes is declaring that it really should mean Interactive.

Scott's (Microsoft) point:

Internet for me isn't the right word, it's semantically incorrect and out of touch with today's actual "RIA" (Rich Interactive Applications) solutions. Typically, hardware vendors are looking at Rich Interactive Applications with new found respect, and monitoring both Microsoft and Adobe movements in this space.

When you couple hardware vendors along side most internal facing solutions, the word Internet starts to have a confusing message/meaning, as well.... Internet isn't really being used?

Ryan (Adobe) counters:

For some reason nothing gets under my skin more than this whole rich interactive application versus rich Internet application fight (and this bugged me before I joined Adobe). I just think it’s kind of dumb and petty. RIA stands for rich internet application. Everyone calls it that, everyone knows what it means (well as much as anyone can define RIAs) and that’s that.

I agree with Ryan (although it doesn't get under my skin as much). I think Scott is missing the target here. "Internet" may not always be the right word given the delivery may be over the "intranet", but Interactive is hardly an improvement. Interactive is so vague (is it a installed application, a smart-client application -- oh don't get me started on how much I hate that term -- is it a web application with cool DHTML, does it use Flash or Silverlight?). Microsoft Money is a Rich Interactive Application, and for that matter, so is Word 2007.

Rich implies to me fancy -- and the 99% of applications that matter to end users are interactive. Those that aren't interactive are not typically rich (even widgets on the desktop of your favorite operating system are interactive to some degree). How many Rich applications are there that you don't interact with in at least some way?

So, my suggestion would be to Scott since he's a bit frustrated by "Internet" is to drop the "I".

Rich Application.

But then, I think that's missing the whole point of the delivery mechanism being the key to why this acronym exists at all.

And if the whole point of this acronym really is to identify the source of the rich application, then it seems like it should stay. Unfortunately, few acronyms that might identify more precisely the network don't roll off the tongue so well. (Rich Network-Delivered Application?)

Of course, Scott is entitled to call it rich interactive application of course, but Rich Internet Application works just as well for a large percentage of applications that are delivered over the Internet, so that should stay as well. I love overloaded acronyms!

October 9, 2007

Can Adobe "kill" Microsoft?

From Ryan Stewart, "CNet on Adobe as a Microsoft Killer"...

The key word there is application. We're building a platform with Flex that is meant to compete head on with robust client side applications. You can do so much in the browser now and Flex lets to build applications that are feature-wise generally the same as any other app. The kicker is AIR. We understand the benefits of the desktop and if that's what you need to leverage, we want you to be able to do so. But we're coming at it from a web background, from the browser world. I think that allows us to strike a unique balance between browser and desktop that no one else can do as well.

(emphasis his).

Read one of the blog posts from CNet here that inspired Ryan's post.

 

Adobe has something very interesting on their hands. Flash, although steeped in a long history of performance problems and an animation background was completely rewritten for Flash 9, which powers the latest Flex platform. ActionScript, once a mockery of developers (and designers) everywhere, is now a robust, object-oriented, developer friendly, and JITed language. I've experimented a little lately with it, and it feels enough like C/C++/Java/C# that I'm easily at home. The latest beta of Flex Builder 3 is quite solid and provides for a very decent editing and debugging experience. It may not be up to the latest of Visual Studio 2008 in some respects, but it's not far away (and they're making rapid improvements in what seem to be a much faster time table than Microsoft).

MXML, the markup language for the UI of Flex, is a sibling (or first cousin) to WPF's XAML representation. I found myself quickly learning it and looking for the similarities (and finding many) between MXML and WPF in features and behaviors (and there are many).

Why might I consider Flex over WPF? Deployment, hardware requirements, platform availability, runtime adoption (Flash), developer experience, and platform maturity. Sure, I know that Silverlight 1.1 will have many of the same features of Flex, yet, if you look at the feature lists, many things will be left on the cutting room floor of Microsoft, for a 'future' version.

Flex also can be ported to the AIR platform -- which really has no equivalent in Silverlight. WPF is similar, but it's tied to a Windows Operating system, and installation on a PC with plenty of horsepower. (AIR being a technology that allows a Flash/Flex application to run stand alone, outside of the browser).

I think there's a bright future for Flex right now. The browser delivered application (with zero install), is the next big thing. Web 2.0 is all about using HTML/DHTML and more Javascript than you'd ever thought possible to make some applications web friendly (enough Javascript to give you heartburn!). The web is ready for something new. Macromedia is said to have introduced the world to "Rich Internet Applications" (RIA) in 2002. I'd suggest though that we're just about there Adobe (and Microsoft).

RIA is no longer in beta, it's RIA 1.0.

RIA 1.0: It's cross platform; cross browser; ditching the woes of HTML and CSS incompatibilities; crushing the browser; extending our experiences; and empowering users with the full gamut of useful applications without the boundaries of HTML/DHTML/AJAX.

Welcome to the future. It will be arriving any day now. :)  I'm looking forward to it. How about you?

October 3, 2007

Finally, No more Reflector'ing [sic]

Microsoft has finally seen the light! They announced today that the source code for .NET 3.5 for most of the framework would be available for download. That's awesome! It should make debugging so much easier! The list included WPF and all of the typical base class libraries. (WPF debugging still will be FREAKY though as there's so much there, but it might help in the real tough cases).

September 2, 2007

Silverlight 1.0 Particle Experiment

Playing around with Silverlight 1.0 the last week:

image

(Click image to run demo in a new window). It's fun to watch.

Managing the lines turned out to be slightly tricky and I never came up with a solution I liked a lot. (I didn't spend too much time on it ... there's probably something better). There's no "paint" or draw line command in Silverlight 1.0. So, the code needed to manage all of the lines and properly update them with each frame. Flash makes it easy with a DrawLine function. To reduce flicker, I also wanted to maintain the same line from 'frame' to 'frame' in the Silverlight demo. So, I had to create some arrays and toggle visibility appropriately.

The effect shown is called, "spring." When the distance between 2 particles falls below 100, they begin to act like a spring. That's when the line shows up -- and the intensity (or opacity) of the line indicates the inverse of the distance between the two particles.

For extra fun, press the left mouse button and drag it around. It's a pointless fading airbrush effect.

image

Since I haven't read anything about how Silverlight manages completed Storyboards and animations, I created a simple 'garbage-collector.' When the number of active animations exceeds 200, I remove the old brush spots and animations.

  animateParticle : function(sender, element)
  {
    var host = sender.getHost();
      
    try
    {        
    var xaml = '<Storyboard Name="' + ++this.dynmID + '">' + 
    '<DoubleAnimation ' +
      'Storyboard.TargetName="' + element.name + '" ' +
      'Storyboard.TargetProperty="Opacity" ' +
      'From="1.0" To="0.0" Duration="0:0:2" />' + 
    '</Storyboard>';

var e = host.content.createFromXaml(xaml);
sender.findName("Page").resources.add(e);

e.begin();
return e;
}
catch(ex)
{
alert(ex);
}
},

    handleMouseMove: function(sender, eventArgs)
{
if (this.isMouseDown == true) {
var e = this.createEllipse2(sender, eventArgs);
this.particles.push(e);
e = this.animateParticle(sender, e);
this.animparticles.push(e);

if (this.particles.length > 200) {
var anim = this.animparticles.shift();
anim.stop();
sender.findName("Page").resources.remove(anim);
var particle = this.particles.shift();
sender.children.remove(particle);
}

}
}

August 14, 2007

Petzold's 3D Book - as Hot as a Wii?

You'd think so, given the run around from Amazon that I've experienced thus far. I pre-ordered the book in May. So, when it was finally shipping, I expected to receive a copy a few days later. No emergencies.

However, like a few others, I got an e-mail from Amazon that my order had been back-ordered till October! Figuring that it was a mistake, I waited about a week before following up. Still back-ordered when I checked yesterday, yet the web page for the book lists it as, in stock, with immediate shipment!

Frustrated, I wrote Amazon to check on the status.

Response:

First, please allow me to extend my most sincere apologies for any frustration this matter has caused.

Sometimes unexpected fluctuations in supply can add time to our original availability estimate. We have learned that "3D Programming for Windows (Pro - Developer)" is now back-ordered, and our supplier has not been able to let us know exactly when they expect to have more in stock.

Please understand that the availability and shipping time frames listed on our web site are estimates, not guarantees. While we do our best to adhere to these estimates, our inventory is constantly changing based on information we receive directly from our suppliers.

If all of the items in your order are listed online as "In Stock," this indicates that we have at least one of each item on hand.

Depending on which shipping method you choose, your order could be packaged, prepared and shipped out in as little as a few hours.

What? (Wow, they could work on the helpfulness their canned responses!)

My response to that e-mail was a bit more terse, asking for something a bit more 'precise' and helpful.

Response:

I have reviewed our previous correspondence with you, and I offer my sincere apologies for any misunderstanding thus far.

"3D Programming for Windows (Pro - Developer)" has proven to be a very popular item.

We have many customer orders for this item, and we've just begun receiving copies from our suppliers. We are filling orders as quickly as we can on a first-come, first-served basis.

Further, I have checked your order and can confirm that we are currently preparing it for shipment.

I have investigated your order and determined that the estimated shipment and delivery dates displayed in Your Account was incorrect. Please accept our sincere apologies for this error.

It appears that we will be able to ship your package earlier than the date estimated on our web site, and we expect your order will arrive within two business days of the ship date.

What have I learned now? That I should expect my order as my book is being boxed right now. Also, good news for Charles is that the book has proven to be a very popular item.

Amazon's web site for my order this morning:

image

Yay! Delivery in a month and a half! Progress. [slap].

At this point I hope it's just a database update error, because I'd hate to know that the helpful customer service person lied to me.

By the way, BookPool currently has the book listed as In-Stock for $2 less than Amazon.

(And I know that others have had luck canceling and reordering. I prefer not to at this point as my price for the book is $3 less than what they're selling it for now.)

August 7, 2007

WPF Application Performance

If you were worried about the performance of a new application you were creating, would you pick WPF? What if it was critical that the application be fast? Would you pick WPF? I think Microsoft is overselling the performance of WPF still. I was hoping that WPF in .NET 3.5 would contain some nice perf enhancements, but nothing much seems to be listed (and I'd expect that they would).

I don't want to buy a new machine to make a marginally "better" application faster. That's true of nearly everyone, especially businesses/enterprises.

Pablo suggests WPF for an application that requires: "Fastest possible 2d grid control."

I personally think that's the wrong way to go. WPF isn't ready for super fast applications. The reality of most developer's days is that they don't have time to "evaluate" technology options much; maybe time to decide should they use a flat button or a 3D button, but they just need to get their work done. Microsoft absolutely needs to provide better prescriptive guidance in these cases that aren't self serving to a particular product/team whenever possible. (In this case, the developer was already committed to C# it seemed, so they had already picked a Microsoft platform).

What do you think? Comment here, or on Pablo's site.

July 23, 2007

WPF - The Ghost Cursor

I wanted a cursor that would look like some Visuals from a WPF project I was working on a while back. The challenge though was that I wanted the cursor to be visually appealing inside and outside of my WPF application. When I dragged an image from my application to another application, I didn't want the boring Drag cursor that is the default in Windows. I wanted thumbnails of what I was dragging.

I never quite completed the project or the code, but I decided to post it hoping that it might save someone else some time -- as it is close. Very close.

Here's the effect I was looking for (the grid is under the cursor):

image

Here are the originals I dragged from my application:

image

So, you can see how the mini images with a transparent gradient where created from the images in my application. The code I'm about to present is not for the faint of heart. It uses Win32 Interop. Big time. So, if you don't understand what that means, it's likely that you won't be able to make any improvements to the code without some assistance.

The code is here.

Here's how I used it:

        private void StartDrag()
        {
            WrapPanel panel = new WrapPanel();
            IList selectedItems = listPhotos.SelectedItems;
         
            foreach (object o in selectedItems)
            {
                Photo p = o as Photo;
                if (p != null)
                {
                    if (p.IsThumbnailLoaded)
                    {
                        Border b = new Border();
                        b.Margin = new Thickness(6);
                        b.Padding = new Thickness(2);
                        b.Background = new ImageBrush(p.Thumbnail);
                        b.BorderBrush = Brushes.Black;
                        b.BorderThickness = new Thickness(3);
                        b.Height = 60;
                        b.Width = 80;

panel.Children.Add(b);
}
}
}

panel.Visibility = Visibility.Visible;
canvasOffscreen.SetValue(Grid.MarginProperty, new Thickness(this.ActualWidth, this.ActualHeight, -panel.DesiredSize.Width, -panel.DesiredSize.Height));
canvasOffscreen.Children.Add(panel);
canvasOffscreen.UpdateLayout();
panel.Measure(new Size(425, 1000));
panel.Arrange(new Rect(new Size(425, 425)));

Mouse.OverrideCursor = new GhostCursor(canvasOffscreen).Cursor;

canvasOffscreen.Children.Remove(panel);
panel.Visibility = Visibility.Collapsed;

DragDrop.DoDragDrop(listPhotos, new DataObject("test"), DragDropEffects.Link);
Mouse.OverrideCursor = null;

_isMouseDown = false;
}


I had a list of Photos, each which may have had a Thumbnail. If one was present, I added a new Border containing an ImageBrush of the Thumbnail to a WrapPanel. Then, I made the WrapPanel visible. I have a canvas that is always Visible, yet off-screen, creatively called canvasOffscreen. This allows me to place the WrapPanel full of images on the screen -- force a render, and snap the resulting visuals. Without that, I couldn't get the Visuals to render.


Next, I pass the canvasOffScreen (as a Visual) to a new instance of the GhostCursor class which returns a Cursor object that can be used by WPF. Finally, I remove all of the borders (as I've grabbed the Visual and made the cursor by this point) and hide the panel. In the code above, it does a fake DragDrop.


This all was started by this code:

        void listPhotos_MouseMove(object sender, MouseEventArgs e)
{
if (_isMouseDown && IsDragGesture(e.GetPosition(GetTopContainer())))
{
StartDrag();
}
}


private bool IsDragGesture(Point point)
{
bool hGesture = Math.Abs(point.X - _dragStartPoint.X) > SystemParameters.MinimumHorizontalDragDistance;
bool vGesture = Math.Abs(point.Y - _dragStartPoint.Y) > SystemParameters.MinimumVerticalDragDistance;

return (hGesture | vGesture);
}


This just starts a drag action if the mouse has moved sufficiently far while the mouse button was pressed. There's a bit more code necessary to wire it all up, but you get the idea.


There are quite a few hard-coded sizes in the GhostCursor class that you'll probably want to make into properties or parameters. Feel free.


 

        private BitmapSource CaptureScreen(Visual target)
{
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);

RenderTargetBitmap renderBitmap = new RenderTargetBitmap(800, 600, 96, 96, PixelFormats.Pbgra32);

DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(target);
LinearGradientBrush opacityMask = new LinearGradientBrush(Color.FromArgb(255, 1, 1, 1), Color.FromArgb(0, 1, 1, 1), 30);
ctx.PushOpacityMask(opacityMask);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
ctx.Pop();
}
renderBitmap.Render(dv);

return renderBitmap;
}


The above code shows how to grab a visual as a Brush, and apply a linear gradient brush to the Visual and then render it to a BitmapSource.


I had to use the CursorInteropHelper class to do a little magic (I'm very glad this class existed!):

        private void CreateCursor(int width, int height, BitmapHandle dibSectionHandle)
{
BitmapHandle monoBitmapHandle = null;
try
{
monoBitmapHandle = new BitmapHandle(CreateBitmap(width, height, 1, 1, IntPtr.Zero));

ICONINFO icon = new ICONINFO();
icon.IsIcon = false;
icon.xHotspot = 0;
icon.yHotspot = 0;
icon.ColorBitmap = dibSectionHandle;
icon.MaskBitmap = monoBitmapHandle;

_iconHandle = CreateIconIndirect(ref icon);
if (!_iconHandle.IsInvalid)
{
_ghostCursor = CursorInteropHelper.Create(_iconHandle);
}

}
finally
{
// destroy the temporary mono bitmap now ...
if (monoBitmapHandle != null)
{
monoBitmapHandle.Dispose();
}
}
}


That's all for now. Enjoy.

July 4, 2007

Distract your WPF application users with a splash screen!

If you've written any WPF applications -- I bet you've had these feelings:

  • Worry: How patient will your users be? The startup time for your application is much longer than you'd like. (Do you hate the start up time for a WPF application as much as me? )
  • Pain: Annoyed by the long PAUSE that happens the first time you start/launch your WPF application.
  • Concern: How many times will the user double-click your application icon before your application shows up? Will they wonder if they actually started the application only to start several copies after a series of double-clicks?
  • Stressed: You've just bought a new computer with the fastest processor, the fastest disk, and more RAM than is rational, and you convinced your financial officer or IT shop (in my case, it might be my wife!) that this computer would be so fast at everything it did that you can't imagine needing a new computer for many many years! Everything else performs awesome -- just these little WPF applications launch so slowly .........

If you've had those or similar feelings, then what you are in need of is a splash screen!

(Seriously, first try to determine if there's ways of loading your application faster: like minimizing the number and size of assemblies that need to be loaded initially; reduce the amount of JITing that must be done -- less code is often better; use a profiler to find hot spots, use a two stage application launch (a mini-loader followed by the real application); and etc. A splash screen at least makes users feel like something is happening. They received a response back from the system -- it acknowledged their actions and is working.)

One of the typical ways of adding a splash screen would be to add a Window to your project and show it before the main window shows -- you'd probably leave the border off, consider using transparency, it would look awesome. That would work. HOWEVER, it will still take too long to show up -- as there's just too much startup needed by a WPF application before that would be made visible (and the fancier it is, the slower it will load). So, the solution is to build something that relies very little on .NET -- so that it can show BEFORE most of the WPF assemblies and .NET assemblies are loaded and needed.

It's not that difficult to do if you're familiar with Windows 32 programming. I'm talking bare to the metal Win32 programming -- not even the thin wrapper than is offered by Windows Forms or any of the associated Drawing assemblies that are commonly used in a WinForm application: that's too much bloat.

So, what does that mean? It means that this splash screen shows up FAST. If your main EXE is small (maybe just a shim for launching the real application), then it will be even faster!

In the attached ZIP file, I've included the assembly you'll need, a demo project, and a project that is used to create the necessary DLL containing the Bitmap. When I said bare to the metal, I meant it. My splash screen component requires a Native Windows 32 bitmap resource to be made available. You can't use a .NET assembly resource as those are just too slow to load.

Also included is a sample Bitmap that I'm sure you'll want to replace:

image

The bitmap can be of any size (although splash screens typically aren't very large... I like to keep them about 400x250 usually).

There are a few differences about a WPF application that uses this component. First -- you can't allow the default project to create the main window for you automatically.

namespace Demo
{
  public class App : System.Windows.Application
  {
    [STAThreadAttribute()]
    public static void Main()
    {
      Splashing splasher = new Splashing("TheSplash.dll", typeof(App).Module, 101);
      splasher.Show();
      App app = new App();
      app.Run(new Window1(splasher));
    }
  }
}

I've removed the App.xaml and default App.xaml.cs files in my project completely and replaced them with a single App.cs file. In this file, I create an instance of the Splasher component, show it, and then Run the application with a new instance of my main window, Window1. I'm passing the instance of the Splasher to Window1 so the splash screen can be hidden when Window1 has shown (or has progressed far enough to warrant closing).

If you have resources in App.xaml, then you might want to move them to a new file(name), and be sure that the Class reference is removed as well from the xml header. Then add code to the Main method above (before the run and after the App instance is created):

    ResourceDictionary dictionary = new ResourceDictionary();
    dictionary.Source = new Uri("/GlobalResources.xaml", UriKind.Relative);
    app.Resources.MergedDictionaries.Add(dictionary);

Of course, adjust this based on your naming conventions and needs.

The parameters to the Splashing class are:

  • Resource file name: The name of the DLL containing the native Win32 resource Bitmap you want to load. It needs to be in the current directory or use a relative path.
  • Module: Used internally - just pass it the module from the Application object -- that's what it really wants. :)
  • Resource ID: Win32 resources are all assigned integer IDs. This number needs to match the resource ID assigned to your splash screen bitmap. VC++ starts at 101 -- in my sample project, it's the first resource, and it's ID is 101.

You'll need to close/hide the splash screen of course. I use the event IsVisibleChanged in the demo as a signal to hide the splash screen:

    void Window1_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
      if (_splasher != null)
      {
        Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.SystemIdle, 
new ArgsDelegate0(delegate() { _splasher.Close(this); _splasher.Dispose(); _splasher = null; })); } }

The Close method optionally takes a Window object -- it pops the Window to the front (otherwise you may find that the application when run outside of Visual Studio will not retain it's z-order properly). It's bizarre when you leave that out!

What I haven't done in the demo is attach to the various global/local exception handlers to make sure the splash screen goes away properly. Nothing more embarrassing than a splash screen covering an exception dialog (not that you should have that happen ... but ....).

I've included in the zip file the VC++ project needed to build the resource DLL. If you don't have VC++ ... hmmm. I'm not sure what you should do since I don't have access easily to a system that only has, for example, Visual C# Express installed, on it to know what alternatives you might have. There may be some free options on the web that are easy enough. All you need to do is replace the embedded bitmap with an image of your own. In my sample project the Bitmap is called Splash.bmp. As I mentioned before, it can be any size you want. The side benefit of the bitmap living in a secondary DLL is that it can be unloaded and not continue to consume resources like .NET assemblies containing a Bitmap resource would typically do -- once loaded, always loaded.

image

Once built, just copy it to your output folder. There's no real need to include it in your solution as it's not changing.

If persuaded sufficiently, I might add the ability to add changeable text to the splash screen. :)

By clicking on this download link and using the component, you agree to the following terms:

Copyright (c) 2007, WiredPrairie.Us

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the WiredPrairie.Us nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

If you use it in a project, please write and tell me (coder @ this web site).

I agree, now download it.

Comments or questions, etc. - email me or leave a comment. Enjoy.

Technorati tags: , , , ,

Only humans allowed ... how unfair!

"The content of message is intended to be understood by humans."

image

From the Intellisense documentation for FileNotFoundException in .NET 2.0.

 

From a usability perspective, not sure I'd want to see the text of any exception leak to the end user. Certainly not very often. Even if I wrote an end to end application, I'd handle the exception and display it in a more friendly easy to digest manner (and even better, try to prevent it from ever becoming a problem).

June 25, 2007

CompositionTarget performance

From Rob, Composition Target.Rendering and minimized Windows.

From: Helpful MS Employee 
The CompositionTarget.Rendering event puts us into a mode where we continuously animate. You should unhook at every opportunity.

The key thing to never forget about using the Rendering event -- it continuously fires, unlike any typical UI development system -- the more you do, the slower it goes. Win32 WM_PAINT messages only fired when a region became invalidated (i.e, it needed to be refreshed). Even WPF doesn't work that way normally -- as it's a retained mode drawing system -- the "OnRender" method only fires when you want it to (controlled by a number of factors of course). 

This event however, fires many times a second -- for example, it could easily fire 60 times a second!

June 13, 2007

Cheap and Easy WPF clr-namespace references.

(inspired by a forum post)

Tired of the Xaml syntax:

xmlns:local="clr-namespace:Blah.Yawn.Zzzzz"

that you add to every Xaml file that you create? Worse yet is that you end up adding multiple as you're referencing a bunch of auxiliary WPF pages, controls, types, etc.... so you have 3, 4, etc. of these. Yuck.

xmlns:local1="clr-namespace:Blah.Yawn"

xmlns:local2="clr-namespace:Blah.Yawn.Zzzzz"

xmlns:local3="clr-namespace:Blah.Yawn.Sleepy"

xmlns:local4="clr-namespace:Blah.Yawn.Snooze"

There's an easier way if you own the assemblies and you don't need to reference types in the main executable assembly!

If you don't (own them or you haven't broken them out into multiple assemblies), then you'll have to stick with the standard syntax, which requires one namespace per xmlns declaration.


Again, note that this only works for referenced assemblies.


Go to the assembly where your controls are located and add this assembly attribute to the AssemblyInfo.cs file:

[assembly: XmlnsDefinition("http://www.wiredprairie.us/WPF", "WiredPrairieUS.Controls")]

The xmlnamespace (first parameter) is up to you and should be unique (often your companies web site address followed by some schema. It doesn't really need to exist, just be unique to your product/component). The second parameter should map to the namespace where your types you want reference from Xaml are located. In this example, I've created some WPF controls and placed them in the WiredPrairieUS.Controls namespace.

You can reuse the same xmlns multiple times with different CLR namespaces if your types are in multiple namespaces. Maybe for example, I have a WiredPrairieUS.Controls.Advanced namespace:

[assembly: XmlnsDefinition("http://www.wiredprairie.us/WPF", "WiredPrairieUS.Controls.Advanced")]


Then, go back to the ordinal Xaml file that is using these types and add to the namespace declarations:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local=
http://www.wiredprairie.us/WPF

That's it. You don't need to declare multiple xml namespaces to reference types in different CLR namespaces as they are automatically imported by the Xaml parser/compiler (it checks the referenced assemblies for the attribute above).

Nice.

If you're developing components for reuse/resale. Please consider adding this to your assemblies and documenting it if you haven't already.

Great WPF hands on lab posted for an Outlook 2007 visual clone

http://www.microsoft.com/switzerland/msdn/de/events/hol/wpf.mspx

Building Line of Business applications with Windows Presentation Foundation

image

Very Blend focused, but goes through a lot of WPF features. Well done.

June 12, 2007

Pack syntax in WPF

If you do much with WPF, at some point you'll be forced to use the pack syntax.

A bit of history:

The pack syntax was created by an evil-alien empire bent on mental destruction of application developers on the planet Earth.

The pack syntax is rooted in conventions established by the Open Packaging Conventions. Sigh. Designed and intended for use by committees.

The syntax is well documented, if you can find it. I came across one of the best resources for learning about the most common options used in WPF here:

Pack URIs in Windows Presentation Foundation

The trouble is actually deciphering it all. The graphic on that referenced page makes great sense -- once you can wrap your head around it. :)

I'll reiterate some of the common scenarios here. I've seen the same ones pop up in the WPF forums time and time again.

The pack syntax for files within your WPF application is easy, usually no more than a forward slash followed by the file name:

"/MyPage.xaml"

If the file is contained within a subfolder within your main application, just include the folder name:

"/Pages/MyPage.xaml"

image

If the content is relative to the current markup file or code, leave off the forward slash at the beginning, which indicates it's in the root of the application. I'll typically leave it in to be more clear where I'm getting the content from.

But, what if you are referencing Pages in another assembly you've referenced in your project?

The syntax is similar:

"/ExternalPages;component/FancyPages1.xaml"

if the file is in the main "root" folder of the referenced assembly. In this case, the referenced assembly was named "ExternalPages". The ;component syntax is required as part of the pack syntax for referenced content in external assemblies.

If the content is in a different folder:

"/ExternalPages;component/AwesomePages/IncrediblePage1.xaml"

image

If you're referring to content in the secondary assembly and loading it via the pack syntax, you'll need to reference the assembly by name, even though the content is located in that assembly. If, for example, in the ExternalPages project I had a class that loaded FancyPages1.xaml, I'd need to use syntax like this in VB.NET:

Dim u as Uri = New Uri("/" + Me.GetType().Assembly.GetName().Name + ";component/FancyPages1.xaml", UriKind.Relative))

or in C#:

Uri u  = new Uri("/" + this.GetType().Assembly.GetName().Name + ";component/FancyPages1.xaml", UriKind.Relative));

Rather than hard code the name of the assembly, I've taken the approach of grabbing it dynamically in the example above.

What's this about "pack" though? It's really how you specify the type of Uri (and where). You need to use the pack syntax when you're specifying an 'absolute' Uri. It hardly changes the common cases though (which is great!).

The pack syntax for example for the hard coded referenced assembly example above would look like this:

"pack://application:,,,/ExternalPages;component/AwesomePages/IncrediblePage1.xaml"

There's one other type of pack that's typically used when you have "loose" Xaml content, and that's the siteoforigin. The huh? Just think of it as a real "absolute" path to the location where your content lives, relative your your application's executable.

If I used this syntax:

pack://siteoforigin:,,,/MyLoosePage.xaml

That would mean that the content would come from a xaml page called MyLoosePage.xaml that was in the same folder as the application as content -- not embedded as a resource. (What's cool about this is that it also works if your application was downloaded via the web as an XBAP -- the site of origin becomes the host application virtual directory (like http://www.wiredprairie.us/myxbap for example could be the host of an Xbap -- that would become the site of origin).

June 1, 2007

Think you know your .NET string comparison functions?

If you can't easily describe the difference between String.Compare and String.Equals in .NET, then read this.

May 24, 2007

Range converter for WPF

A member on the MSDN forums asked how to enable a button based on the index of the currently selected item in a ListBox. If the index was 0, they wanted the Button to be disabled. I whipped up a Converter:

 

  [ValueConversion(typeof(int), typeof(bool))]
  public class IntToBooleanRangeConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, 
object parameter, System.Globalization.CultureInfo culture) { string range = parameter as string; int intVal = (int) value; if (!string.IsNullOrEmpty(range)) { string[] nums = range.Split(new char[] { '-' }); int[] checkVal = new int[nums.Length]; for(int i = 0; i < nums.Length; i ++) { int.TryParse(nums[i], out checkVal[i]); }

if (nums.Length == 1)
{
return checkVal[0] == intVal;
}
else if (nums.Length == 2)
{
// is it #-# or #-?
if (!string.IsNullOrEmpty(nums[1]))
{
// # - #
return (intVal >= checkVal[0] && intVal <= checkVal[1]);
}
else
{
// #-
return intVal >= checkVal[0];
}
}
else
{
throw new ArgumentOutOfRangeException("IntToBooleanRangeConverter:
Range must be in format #-#, #-, or #"
);
}
}
throw new ArgumentOutOfRangeException("IntToBooleanRangeConverter:
Range must be in format #-#, #-, or #"
);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}

}


Here's a demo:


<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="clr-namespace:WiredPrairieNamespace"
   
xmlns:System="clr-namespace:System;assembly=mscorlib"
   x:Class="WiredPrairieNamespace.Window1"
   x:Name="Window"
   Title="Window1"
   Width="640" Height="480">

    <
Window.Resources>
    <
local:IntToBooleanRangeConverter x:Key="IntToBooleanRangeConverter" />
    </
Window.Resources>

    <
Grid x:Name="LayoutRoot">
        <
ListBox HorizontalAlignment="Left" Margin="79,72,0,149" Width="191"
IsSynchronizedWithCurrentItem="True" x:Name="myList">
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
            <
ListBoxItem>Item</ListBoxItem>
        </
ListBox>
        <
Button IsEnabled="{Binding Path=SelectedIndex,
Converter={StaticResource IntToBooleanRangeConverter},
ConverterParameter=1-, ElementName=myList}
"
HorizontalAlignment="Right" Margin="0,218,181,175"
Width="115" Content="Button" />
    </
Grid>
</
Window>

If the ListBox item zero is selected, the button is Disabled. ConverterParameter controls the results. It accepts either a single value for exact comparison, or a range, such as 0-10 or 5-. The latter would accept values 5 or higher and the former values zero through ten (inclusive).

May 23, 2007

Give me the maximum and no more ListBox!

WPF ListBoxItems are sized to fit their content regardless of what that does to the ListBox visually. If a ListBox has really wide items, while virtualized, the ListBoxItems, as they are created and destroyed may cause the horizontal scrollbar to appear and disappear randomly. I find that annoying from a user experience perspective. (It's one thing that makes a Virtualizing ListBox frustrating).

One simple solution is to hide the horizontal scrollbar completely:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Page.Resources>
    <Style x:Key="StretchedContainerStyle" TargetType="{x:Type ListBoxItem}">
      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    </Style>
  </Page.Resources>
  <Grid>
    <ListBox Width="80" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
      ItemContainerStyle="{StaticResource StretchedContainerStyle}">
      <ListBox.Items>
        <ListBoxItem>
          <Grid>
            <TextBlock>My listbox item 1</TextBlock>
          </Grid>
        </ListBoxItem>
        <ListBoxItem>
          <Grid Background="Silver">
            <TextBlock>My listbox item 1</TextBlock>
          </Grid>
        </ListBoxItem>
      </ListBox.Items>
    </ListBox>
  </Grid>
</Page>

The attached property ScrollViewer.HorizontalScrollBarVisibility does the job nicely. Except, now the content that is beyond the margin (the right margin) is clipped and not visible at all! A tooltip set for each item could resolve the general clipping problem. But, it still seems like half a solution -- partly because the content may not look clipped, thereby misleading the user.

The other "straightforward" technique is to cheat a bit. What I wanted to happen was for the text content to show ellipsis when clipped -- not just get clipped. Here's what's happening in the sample code below (paste into XamlPad to experiment)...

The MaxWidth property of the ListBoxItem is being set to the ListBox's ScrollViewer's ViewPort width. Through a reasonably interesting Binding:

Find an parent (FindAncestor) that is a ScrollViewer (AncestorType={x:Type ScrollViewer}) and bind to the ViewportWidth property.

I added a general TextBlock style that includes the TextTrimming that I wanted as well so it would apply to all TextBlocks.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Page.Resources>
    <Style TargetType="{x:Type TextBlock}">
      <Setter Property="TextBlock.TextTrimming" Value="CharacterEllipsis" />
      <Setter Property="TextBlock.TextWrapping" Value="NoWrap" />
    </Style>
    <Style x:Key="StretchedContainerStyle" TargetType="{x:Type ListBoxItem}">
      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
      <Setter Property="MaxWidth"
        Value="{Binding RelativeSource={RelativeSource FindAncestor, 
                AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}" />
    </Style>
  </Page.Resources>
  <Grid>
    <ListBox Width="80" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
      ItemContainerStyle="{StaticResource StretchedContainerStyle}">
      <ListBox.Items>
        <ListBoxItem>
          <Grid>
            <TextBlock>My listbox item 1</TextBlock>
          </Grid>
        </ListBoxItem>
        <ListBoxItem>
          <Grid Background="Silver">
            <TextBlock>My listbox item 123 </TextBlock>
          </Grid>
        </ListBoxItem>
      </ListBox.Items>
    </ListBox>
  </Grid>
</Page>

 

Technorati tags: , ,

Stretching ListBoxItem Content to fit a Listbox

Have you wanted to completely fill, stretch,  or maximize the content of a ListBoxItem in WPF to match the width of the viewport (the scrollable area)? I have -- and it took me far too many searches to find it AGAIN yesterday (so, I'm posting it here so I'll be able to find it again when I forget this again). By default, a ListBoxItem in a listbox is only as wide as the content needs to be. So, if you're trying to align content to the right side for example, it may not end up where you want it.

You only need to do two things:

  1. Create a Style that sets that the HorizontalContentAlignment property to Stretch.
  2. Use the style created in step 1 as the value for the ItemContainerStyle property of the ListBox.

See below for an example:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:sys="clr-namespace:System;assembly=mscorlib" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Page.Resources>
    <Style x:Key="StretchedContainerStyle" TargetType="{x:Type ListBoxItem}">
      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    </Style>
  </Page.Resources>
  <Grid>
    <ListBox ItemContainerStyle="{StaticResource StretchedContainerStyle}">
      <ListBox.Items>
        <ListBoxItem>
          <Grid>
            <TextBlock>My listbox item 1</TextBlock>
          </Grid>
        </ListBoxItem>
        <ListBoxItem>
          <Grid Background="Silver">
            <TextBlock>My listbox item 1</TextBlock>
          </Grid>
        </ListBoxItem>
      </ListBox.Items>
    </ListBox>
  </Grid>
</Page>

Paste the above into XAMLPad. Try removing the ItemContainerStyle to see the impact of setting the HorizontalContentAlignment property.

 

Technorati tags: , , ,

May 16, 2007

Creating WPF FlowDocuments on the non-UI thread

From a post on the MSDN forums came a great question:

How to create and populate a FlowDocument on a thread other than the UI thread?

Here's what happens normally:

The calling thread cannot access this object because a different thread owns it.

Arggh.

FlowDocuments aren't freezable (yet they are DependencyObjects). That means they can't be created on one thread and then used on another. Bummer.

Two solutions came to mind:

  1. Do as much work as possible on the non-UI thread, prepping data, etc. Then, at the last possible moment, Invoke a method back in the UI thread and build the flow document.
  2. Cheat! Create the FlowDocument in the background thread. Then, save it as a Xaml file to memory, and reload it in the UI thread! Something like this:
      FlowDocument doc = new FlowDocument(new Paragraph(new Run("hi")));
      System.IO.MemoryStream stream = new System.IO.MemoryStream();
      XamlWriter.Save(doc, stream);
      stream.Position = 0;
      
      this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ZeroDelegate)delegate()
        {
          flowDoc = (FlowDocument) XamlReader.Load(stream);
        }
      );

I'm not sure what the performance characteristics would be of either option -- so you'll need to do some testing. I'd love to hear that there's a better way -- it' s not obvious though and doesn't follow the typical pattern for dealing with this type of stuff. It's very unfortunate that the document can't be manipulated / locked / etc. on another thread. That seems like an obvious use-case that many applications would want to do.

May 9, 2007

Separated at birth? JavaFX and WPF/XAML?

There's a buzz about JavaFX on the net, so I thought I'd spend just a moment taking a look.

Rather than me trying to describe it (other than to say, "Silverlight", "Apollo", "Flash"), here's what the web page says:

JavaFX Script is a highly productive scripting language that enables content developers to create rich media and content for deployment on Java environments. JavaFX Script is a declarative, statically typed programming language. It has first-class functions, declarative syntax, list-comprehensions, and incremental dependency-based evaluation. It can make direct calls to Java APIs that are on the platform. Since JavaFX Script is statically typed, it has the same code structuring, reuse, and encapsulation features (such as packages, classes, inheritance, and separate compilation and deployment units) that make it possible to create and maintain very large programs using Java technology. See the FAQ for more information.

There are a few demos available on that page, but I picked JavaFXPad. You'll need Java 1.5+ installed to run the demos. I was greeted with a XAMLPad clone and a simple demo. It looks like this:

So, that inspired me to do a brief comparison of something I'm more familiar with, WPF/XAML. For comparison though, here's the JavaFX code:

 

import javafx.ui.*;
import javafx.ui.canvas.*;
import javafx.ui.filter.*;

Group {
content:
[Rect {
x: 10
y: 10
width: 460
height: 140
fill: LinearGradient {
x1: 0
y1: 0
x2: 1
y2: 0
stops:
[Stop {offset: 0, color: green},
Stop {offset: .5, color: new Color(.5, 1, 0, 1)},
Stop {offset: 1, color: green}]
}
stroke: green
strokeWidth: 3
},
Text {
x: 120
y: 50
content: "JavaFX"
font: Font {face: VERDANA, style: [ITALIC, BOLD], size: 60}
fill: LinearGradient {
x1: 0, y1: 0, x2: 0, y2: 1
stops:
[Stop {
offset: 0.2
color: red
},
Stop {
offset: 0.5
color: orange
},
Stop {
offset: .8
color: red
}]
}
filter:
[Glow {
amount: 0.1
},
Noise {
monochrome: true
distribution: 0
}]
},
View {
transform: translate(160, 200)
content: Button {
icon: Image { url: "javafxpad/images/duke.gif" }
text: "Click Me!"
}
}]
}


Simple. And familiar. So, I threw together the XAML version:



 

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Canvas>
<Rectangle Stroke="green" StrokeThickness="3"
Width="460" Height="140"
Canvas.Left="10" Canvas.Top="10">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="green" Offset="0"/>
<GradientStop Color="#FFAED000" Offset="0.5"/>
<GradientStop Color="green" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Width="400" Height="86" Canvas.Left="53" Canvas.Top="36"
FontFamily="Verdana"
FontSize="60" FontStyle="Italic"
FontWeight="Bold"
Text="Not JavaFX"
TextWrapping="Wrap">
<TextBlock.Foreground>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="red" Offset="0"/>
<GradientStop Color="red" Offset="1"/>
<GradientStop Color="orange" Offset="0.51"/>
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>
<Button Width="172" Height="117" Padding="6,6,6,6" Canvas.Left="187" Canvas.Top="185">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
Width="Auto" Height="Auto" Orientation="Horizontal">
<Image Width="100" Height="100" Source=""/>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Click Me!"
TextWrapping="Wrap"/>
</StackPanel>
</Button>
</Canvas>
</Page>

 


Very close. The syntax is easier to read in JavaFX, only because there's no curly brackets. It would download faster to a client when compared to a Silverlight version -- but in the days of broadband, I'm not sure how big of a deal it would be. I don't know of a way to make the ugly fill noise they added in the Java version, but everything else is reasonably close.


It will be interesting to watch it progress. I'd imagine it being relatively easy to create a converter for the two environments if one was so inclined. I'm not. (XAML to JavaFX would probably be a lot simpler).

April 22, 2007

Disposing Virtualizing Stack Panel

A VirtualizingStackPanel is very useful when using WPF -- when items scroll out of the viewable area, they may be destroyed to reclaim the valuable UI resources they are consuming. By default, the VirtualizingStackPanel automatically destroys the virtual tree for items that become reclaimed automatically, without the item being aware at all. Often, items are mapped to a DataTemplate which provide the UI.

I was in need of a way to have the virtualized item know when it was being reclaimed -- as the internals of the object held onto resources that could have been considered wasteful if they were to pointlessly be held (in this case, it was a bitmap). Using a simple overridden method of the VirtualizingStackPanel, OnCleanUpVirtualizedItem, it's easy to inform the displayed item when its visuals are being removed.

  public class VirtualizingStackPanelEnh : VirtualizingStackPanel
  {
    protected override void OnCleanUpVirtualizedItem(CleanUpVirtualizedItemEventArgs e)
    {
      IDisposable disp = e.Value as IDisposable;
      base.OnCleanUpVirtualizedItem(e);
      if (disp != null)
      {
        disp.Dispose();
      }
    }
  }

Above is my simple implementation. Items which want to know they no longer are being shown can implement IDisposable. When the Dispose method is called, clean up any internal resources that may be wasteful if the item is not being displayed. Of course, your items don't have to implement IDisposable, it could be anything you want, I have an implementation that just calls a "Cleanup" method on a known object as well. (It could be an event that is raised to the list item as well).

    <Style x:Key="PhotoListBoxStyle" TargetType="{x:Type ListBox}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ListBox}">
            <Border Background="{TemplateBinding Panel.Background}"
                BorderBrush="{TemplateBinding Border.BorderBrush}"
                BorderThickness="{TemplateBinding Border.BorderThickness}">
              <ScrollViewer HorizontalScrollBarVisibility="Auto" Template="{DynamicResource ScrollViewerControlTemplate1}">
                <local:VirtualizingStackPanelEnh IsItemsHost="True" Orientation="Horizontal"/>
              </ScrollViewer>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

To use the new stack panel, I created a new ControlTemplate for a ListBox and embedded the VirtualizingStackPanelEnh directly inside the ScrollViewer.

April 15, 2007

CommandTarget, MenuItem, ContextMenus, and Binding, oh my!

MenuItems can be bound directly to a command. The MenuItem is enabled or disabled based on several factors, including:

  • Does a control have focus?
  • Does the Command have a CommandTarget specified?
  • Does the CanExecute on the CommandBinding return True?

All in all -- not too bad.

That allows me to do something like this:

  <Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.Close" 
CanExecute="AlwaysCanExecute"
Executed="ApplicationCommandsClose" /> </Window.CommandBindings> <Window.InputBindings> <KeyBinding Key="Escape" Command="ApplicationCommands.Close" /> </Window.InputBindings> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Menu> <MenuItem Header="_File"> <MenuItem Command="ApplicationCommands.Close" > <MenuItem.Header> Exit </MenuItem.Header> </MenuItem> </MenuItem> </Menu>
    private void AlwaysCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }

private void ApplicationCommandsClose(object sender, ExecutedRoutedEventArgs e)
{
Close();
}


I always want the Exit menu enabled (at least here), so I've created a method called "AlwaysCanExecute" that always returns True.


Trouble happens though if I start using a ContextMenu on a control that does not get focus. In this example an Image:

    <Image Width="128" Height="128" Source="myimage.png" Stretch="Fill">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Close">
<MenuItem.Header>
Exit
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>

Here I've added a context menu to an image. If I run this application, the MenuItem for Exit is always disabled. The Window CommandBindings are still set.


There seem to be two work arounds.


The first thing I tried though was to use a Binding syntax to set the CommandTarget to a particular element.

CommandTarget="{Binding ElementName=MainWindow}"

The error message that happens is this:



System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=MainWindow'. BindingExpression:(no path); DataItem=null; target element is 'MenuItem' (Name='mnuClose'); target property is 'CommandTarget' (type 'IInputElement')


This really should have work. I think it's a bug (but would like to hear differently).


Oddly, if I, using this:


 

<MenuItem Command="ApplicationCommands.Close" x:Name="mnuClose" 
CommandTarget="{Binding ElementName=MainWindow}" >
<MenuItem.Header>
Exit
</MenuItem.Header>
</MenuItem>

And then in code, set the CommandTarget explicitly:

    public Window1()
{
InitializeComponent();
mnuClose.CommandTarget = this;
}

It works fine. So, the Binding is the problem. That's solution one. If you don't mind adding code and giving the context menuitems a name, you're good to go.


However, I wasn't satisfied.


It just can't seem to find the right scope to locate the elements (hence the error). I tried many Binding combinations and couldn't seem to find one that worked.


So, in the hunt to find an alternative, I discovered the easiest work around that involves zero code is to duplicate the CommandBindings like this:

      <Image.ContextMenu>
<ContextMenu>
<ContextMenu.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
CanExecute="AlwaysCanExecute"
Executed="ApplicationCommandsClose" />
</ContextMenu.CommandBindings>
<MenuItem Command="ApplicationCommands.Close" >
<MenuItem.Header>
Exit
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Image.ContextMenu>

I've added the Commands (by adding a Binding) that I'm using in the ContextMenu directly to the ContextMenu CommandBindings property. I'm calling the same methods as I called for the Window.CommandBindings (as the logic of when and what to do hasn't changed). It's not elegant as I'd like and I suppose I could have written a helper class to guarantee the command bindings have been properly duplicated, but ... that's left as an exercise to the reader!


I like setting the CommandBindings in the application level so this bugs me that I need to duplicate them and keep them up to date. I can't imagine that Microsoft would declare this anything but a bug. If someone knows better leave a comment if they're enabled or send me an e-mail to coder @ this website.


If the control can get focus, this all just works as expected without any trickery. Enjoy.

April 14, 2007

Extracting thumbnails from a JPEG/JPG in .NET 3.0

.NET 3.0 contains a vast array of media related classes. So many that at times it can be overwhelming! Classes that relate to image handling and manipulation number greater than ten!

In this post though, I'll discuss only a few of those that relate to retrieving or creating a thumbnail of a JPG/JPEG. If you need to create only a few thumbnails and want your code to be entirely in XAML, the easiest way to do that is to use the BitmapImage class.

Instead of specifying the Source directly on the Image element, you need to set the Source property to a BitmapSource object. (If you pass a file name or web address directly,  a converter is used to automatically create a BitmapImage for you as a coding-time saving technique.).

 

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:sys="clr-namespace:System;assembly=mscorlib" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <Canvas>
  <Border Canvas.Left="20" Canvas.Top="20" 
    Background="White" BorderBrush="Silver" BorderThickness="1" Padding="8" Width="Auto" Height="Auto">
  <Border.BitmapEffect>
    <DropShadowBitmapEffect />
  </Border.BitmapEffect>
  <Image Width="200">
    <Image.Source>
      <BitmapImage DecodePixelWidth="200"  
         UriSource="D:\temp\needle.JPG" />
      </Image.Source>
  </Image>
  </Border>
  </Canvas>
</Page>

(If you want to use this sample in XAMLPad, make sure you change the UriSource property to a file on your computer). The result should look something like this:

The BitmapImage class has two helpful properties to reduce the size of the image. But you might be saying to yourself, why can't I just use the Width property on the Image class? Sure, that works. If the image isn't very big, nothing bad will happen. However, if the image is large, you're using memory unnecessarily. The image is loaded and cached in memory, and then transformed to the necessary size. So, if you have an image that is 2000x1500, and you want to show it at 200x150, you're using 10 times the memory you actually need! That hardly seems like a good idea. Not only will it take more valuable memory, it's going to be a bit slower as the memory is copied and the image is shrunk to fit. That's where the two useful properties I hinted at come in, DecodePixelWidth and DecodePixelHeight. You'll only need to use one of them as they proportionally reduce the size of your image when you just set one. If you know that the image is going to be smaller than the original, do your users a favor and use them.

What happens if you set the DecodePixelWidth/Height too low and end up displaying the image at a larger size? Depending on the ratio, the effect can be subtle to dramatic. For a dramatic example, I've used the same code as above, but changed the DecodePixelWidth to 20:

That might be the Space Needle in Seattle or a picture of your skinny uncle. Be sure that you set the DecodePixelWidth/Height to a size that makes sense for your application.

That's one way to create a thumbnail.

For my recent PhotoScroll demo application, I wanted more control -- I wanted a class that could dynamically provide the images and create a thumbnail (and optionally extract it from a JPG if the thumbnail was embedded). That required some tricks.

I won't paste all of the code here, for the complete code, download the sample application available on the other post.

The code uses a couple of simple "oops, we don't need to load anymore" exit points (the variable _loading), so you can ignore those here. After the code, I'll walk through the important parts.

 
protected virtual void DoLoadImage(object o)
{
  // if we don't need to load, drop out ...
  if (!_loading) { return; }

if (System.IO.File.Exists(_filename))
{
MemoryStream mem = null;
try
{
byte[] buffer = File.ReadAllBytes(_filename);
// by reading the data into an in-memory buffer, we prevent the file from being read in the UI thread
// -- which speeds up access dramatically!
mem = new MemoryStream(buffer);

if (!_loading)
{
mem.Dispose();
return;
}

// we need to read the metadata in the UI thread, so we'll open the bitmap and grab it using a
// delegate/invoke. (since the user is waiting for this, there's little benefit to doing this
// as an async invoke).
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (NoArgumentDelegate)delegate()
{
bool foundThumbnail;
BitmapDecoder imgDecoder = BitmapDecoder.Create(mem, BitmapCreateOptions.None, BitmapCacheOption.None);
BitmapFrame frame = imgDecoder.Frames[0];
_imageSource = frame.Thumbnail;
foundThumbnail = _imageSource != null;

if (!foundThumbnail)
{
// we'll make a thumbnail image then ... (too bad as the pre-created one is FAST!)
TransformedBitmap thumbnail = new TransformedBitmap();
thumbnail.BeginInit();
thumbnail.Source = frame as BitmapSource;

// we'll make a reasonable sized thumnbail with a height of 240
int pixelH = frame.PixelHeight;
int pixelW = frame.PixelWidth;
int decodeH = 240;
int decodeW = (frame.PixelWidth * decodeH) / pixelH;
double scaleX = decodeW / (double)pixelW;
double scaleY = decodeH / (double)pixelH;
TransformGroup transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(scaleX, scaleY));
thumbnail.Transform = transformGroup;
thumbnail.EndInit();

// this will disconnect the stream from the image completely ...
WriteableBitmap writable = new WriteableBitmap(thumbnail);
writable.Freeze();
_imageSource = writable;
}

// Reading some metadata in a NON-UI thread throws an exception
// It's not as simple as doing a freeze, or cloning or ... anything
// else I could discover ... those all CRASHED.
Metadata = new Metadata(frame.Metadata as BitmapMetadata);
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
finally
{
if (mem != null)
{
mem.Close();
}
_loading = false;
RaisePropetyChanged("IsLoading");
RaisePropetyChanged("ImageSource");
}

}
}


First, the file is loaded into a MemoryStream. As I mentioned in an earlier post, I've found the fastest way to load a Bitmap in code is to avoid using a FileStream and the StreamSource property or the UriSource property directly. The best technique from my experiments is to load the file directly into a MemoryStream object and use that as the Stream parameter or property (depends on which Bitmap objects you're using).


 

      if (System.IO.File.Exists(_filename))
{
MemoryStream mem = null;
try
{
byte[] buffer = File.ReadAllBytes(_filename);
// by reading the data into an in-memory buffer, we prevent the file from being read in the UI thread
// -- which speeds up access dramatically!
mem = new MemoryStream(buffer);

 

The code verifies the file exists and then reads the entire file into a byte array and immediately creates a wrapping MemoryStream object.

Since I wanted to grab some of the Windows Imaging Component metadata, which isn't all threadsafe, I need to perform the next steps the main UI thread (I'll talk about why and what errors might happen if you don't later).

 

          // we need to read the metadata in the UI thread, so we'll open the bitmap and grab it using a 
          // delegate/invoke. (since the user is waiting for this, there's little benefit to doing this
          // as an async invoke).
          Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (NoArgumentDelegate)delegate()
          {
            bool foundThumbnail;
            BitmapDecoder imgDecoder = BitmapDecoder.Create(mem, 
BitmapCreateOptions.None,
BitmapCacheOption.None); BitmapFrame frame = imgDecoder.Frames[0]; _imageSource = frame.Thumbnail; foundThumbnail = _imageSource != null;

Up until this point, I had created a background thread to load photos on demand from a queue -- so it was the background thread that fetched the data for the file.

Using the delegate syntax to make the code easier to debug/trace, I use the BitmapDecoder static method Create to open the file. Since this is a JPG file, there will only be one frame. The BitmapFrame object returned can be used directly as an image elsewhere, but in this case, I want something from that BitmapFrame, the Thumbnail.

Until I started working with these classes a few months ago, I hadn't realized that most modern digital cameras embed a thumbnail directly within the JPG file! Not only is that very cool, it's very practical as it's easy to access the data without needing to load and decode the entire image. That makes accessing the thumbnail fast. Very fast. I've got plenty of images that don't have a thumbnail however. Rather than maintaining an memory expensive large image and shrinking it on demand, I added code to create my own thumbnail on the fly.

 

              TransformedBitmap thumbnail = new TransformedBitmap();
              thumbnail.BeginInit();
              thumbnail.Source = frame as BitmapSource;
              
              // we'll make a reasonable sized thumnbail with a height of 240
              int pixelH = frame.PixelHeight;
              int pixelW = frame.PixelWidth;
              int decodeH = 240;
              int decodeW = (frame.PixelWidth * decodeH) / pixelH;
              double scaleX = decodeW / (double)pixelW;
              double scaleY = decodeH / (double)pixelH;
              TransformGroup transformGroup = new TransformGroup();
              transformGroup.Children.Add(new ScaleTransform(scaleX, scaleY));
              thumbnail.Transform = transformGroup;              
              thumbnail.EndInit();

With a little bit of math, a ScaleTransform, and using yet another media class, TransformedBitmap, I created a thumbnail, with a height of 240. Since I used the empty constructor, I needed to follow the ISupportInitialize pattern:  BeginInit and EndInit. Only within the context of a Begin/End can the properties of the TransformedBitmap class be set (well, at least only the generally useful properties). In the code above, it only sets the Transform property to the new TransformGroup.

When EndInit is called, the magic of the TransformedBitmap class happens (it's transformed!). The bitmap is now ready for use.

The thing that was bugging me though was the hard connection between the TransformedBitmap and the original BitmapFrame returned from the BitmapDecoder (and thus to the MemoryStream). Unless I could sever that connection, I couldn't close/dispose the MemoryStream. So, my simple work-around was to create a new image based on the transformed bitmap:

 

              // this will disconnect the stream from the image completely ...
              WriteableBitmap writable = new WriteableBitmap(thumbnail); 
              writable.Freeze();
              _imageSource = writable;

Bing. Done. I stored the new image away for use by a listbox (which is binding to a thumbnail property of my Photo class).

 

            // Reading some metadata in a NON-UI thread throws an exception
            // It's not as simple as doing a freeze, or cloning or ... anything
            // else I could discover ... those all CRASHED.
            Metadata = new Metadata(frame.Metadata as BitmapMetadata);

The final piece that I wanted to mention bugged me to no end! The BitmapMetadata classes -- they're great, but they aren't always thread safe. For certain types of metadata, they throw crazy exceptions.  This is my favorite:

A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in PresentationCore.dll
System.Runtime.InteropServices.COMException (0x800706F4): A null reference pointer was passed to the stub. (Exception from HRESULT: 0x800706F4)
at MS.Internal.HRESULT.Check(Int32 hr)
at System.Windows.Media.Imaging.BitmapMetadataEnumerator.MoveNext()
at PhotoScroll.Metadata.LoadKeywords(BitmapMetadata metadata) in d:\Dev\Source\PhotoScroll\PhotoScroll\Metadata.cs:line 187

Of course, the stub had a null reference pointer. (uhh?) If you ever mistakenly thought all this new great .NET 3.0 was all managed code -- BZZZT! Wrong. What was particularly frustrating about that exception is that it only happens with certain types of metadata queries! xmp appears to be the most likely to crash if it's not queried on the UI thread -- but I didn't do an exhaustive test by any means. So, my solution was, as I mentioned, to create the image and load the metadata on the UI thread. It affects overall performance of the application, but using the tricks I mentioned above, I've kept the performance to a very respectable level.

April 13, 2007

Photoscroll -- the worst named WPF demo application with source code!

With the exception of this project being called, WindowsApplication72, I'll admit that this is one of the poorest named projects I've distributed. I could have changed the name, but since it's really more of a demo right now than any truly useful functionality, I decided to not bother.

The intention of Photoscroll is to demonstrate a number of useful features in WPF -- and to show off some of the simple things that can be done with WPF that you may not have ever thought of including in an application if you weren't using WPF.

The application code written in C# is available for download below. You might be curious what the application looks like:

Cool animations when images are loading -- no 'jerky' or hanging user interface as the images load.

Tooltips on steroids -- with a Vista like glass effect (that works on XP).

Intermediate images shown while the full size image is loaded...

It uses the WIC BitmapMetadata (Windows Imaging Component) classes to extract available meta data from photos (and show it in a geeky, here's all the raw info available way):

If you have any questions about the code, feel free to send me an e-mail (coder@ this website).

Download (C#/.NET 3.0 required)

Due to the large size of the final EXE (as it contains quite a few graphics), you'll need to build the project to run it as I've excluded it from the download.

I'll be posting some details about the code very soon.

Here are some of the .NET 3.0 classes/properties that the sample code uses:

  • BitmapMetadata (ContainsQuery and GetQuery)
  • IValueConverters
  • Dispatcher
  • Binding
  • VirtualizingStackPanel
  • RoutedCommands
  • Table (and binding to a FlowDocument)
  • BitmapImage
  • BitmapDecoders and BitmapFrame
  • DoubleAnimation
  • DiscreteObjectKeyFrame
  • ControlTemplate
  • DataTrigger
  • Style
  • Trigger EnterAction ExitAction
  • Drag and Drop DataFormats.FileDrop
  • VisualBrush Visual
  • Thumbnail
  • Tooltip

Although I've put a copyright notice and warning on much of the code, all that I really want is a thank-you e-mail/post and a link to my website if you use the code. :)

April 9, 2007

Binding a table in a FlowDocument

Inspired by a post from Paul here providing a method to bind data to a FlowDocument Run object, I extended the idea to bind to a dynamic Table.  I prefer the attached property syntax in Paul's approach to a "BindableRun" object that Filipe used here. I didn't change the name from Paul's code (the name was fine for my purposes).

 

  public static class BindableExtender
  {
    public static Table GetBindableTable(DependencyObject obj)
    {
      return (Table)obj.GetValue(BindableTableProperty);
    }
    public static void SetBindableTable(DependencyObject obj, Table tbl)
    {
      obj.SetValue(BindableTableProperty, tbl);
    }
 
    public static string GetBindableText(DependencyObject obj)
    {
      return (string)obj.GetValue(BindableTextProperty);
    }
 
    public static void SetBindableText(DependencyObject obj,
        string value)
    {
      obj.SetValue(BindableTextProperty, value);
    }
 
    public static readonly DependencyProperty BindableTextProperty =
        DependencyProperty.RegisterAttached("BindableText" ,
            typeof(string) ,
                typeof(BindableExtender),
                new UIPropertyMetadata(null,
                BindableProperty_PropertyChanged));
 
    public static readonly DependencyProperty BindableTableProperty =
        DependencyProperty.RegisterAttached("BindableTable",
            typeof(Table),
                typeof(BindableExtender),
                new UIPropertyMetadata(null,
                BindableProperty_PropertyChanged));
 
    private static void BindableProperty_PropertyChanged(
        DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs e)
    {
      if (dependencyObject is Run)
      {
        ((Run)dependencyObject).Text = (string)e.NewValue;
      }
      if (dependencyObject is FlowDocument)
      {
        ((FlowDocument)dependencyObject).Blocks.Clear();
        if (e.NewValue != null)
        {
          ((FlowDocument)dependencyObject).Blocks.Add((Block)e.NewValue);
        }
      }
 
    }
  }

 

I added all of the code that handles Tables. Not too hard to find. The important stuff regarding binding happens in the PropertyChanged event. If the object passed is a FlowDocument, the code clears the current blocks that might be in the existing FlowDocument and then adds the Table.

How did I build the table? Using code like this to build the table:

 

private static Table BuildTable()
{
  Table tbl = new Table();
  TableRowGroup rowGrp = new TableRowGroup();
 
  tbl.RowGroups.Add(rowGrp);
 
  TableRow row = new TableRow();
  rowGrp.Rows.Add(row);
  // you can style a row ...
  // row.Style = Application.Current.TryFindResource("HeaderRowStyle") as Style;
  row.Cells.Add(new TableCell(new Paragraph(new Run("DataLabel:"))));
 
  Paragraph pg = new Paragraph();
  // or you can style a Paragraph
  // I couldn't get pg.SetResourceReference(Paragraph.StyleProperty, "DataStyle") to work
  // it seems to never locate the Style resource properly ... the other approach below works
  // however
  // pg.Style = Application.Current.TryFindResource("DataStyle") as Style;
  pg.Inlines.Add(new Run("Some data"));
  row.Cells.Add(pg);
 
  return tbl;
}

 

I added a property to the class bound by the UI:

 

    public Table Metadata
    {
      get { return BuildTable(); }
    }

 

Then, in the XAML, I bound to the property:

 

<FlowDocument local:BindableExtender.BindableTable="{Binding Path=Metadata}">
  <Paragraph/>
</FlowDocument>

 

The important thing to note here is the use of the attached property: local:BindableExtender.BindableTable.

The local namespace refers back to the host namespace (and since it's in the same assembly, the syntax just includes the namespace):

 

xmlns:local="clr-namespace:PhotoScroll"

 

 

 

Technorati tags: ,

April 6, 2007

Universal web application pattern vs. Experience first pattern

Ray Ozzie, Chief Software Architect at Microsoft, was interviewed by Knowledge@Wharton. It's an interesting interview -- broad with not too much depth, but he discusses the software and services question that comes up more and more these days -- how do I deliver my product, and to what platforms?

 

Ozzie:  The guidance that we are giving the development community -- and the guidance that we use in-house -- is to look at applications through the following lens: When the business model behind that app means that you have to get it everywhere, we call that the "universal web application pattern." When the most important thing is the experience that the user has with that application and you might be willing to trade off the breadth of the web for the richness of that experience, we call that an "experience first pattern."
There's no hard line between the two, but there is some guidance there. It's clear that the ad-based model is a "universal web pattern." The whole business model says, "Pick a technology for building that solution that gets to every eyeball on earth." At the opposite extreme are Windows games and, I believe, the Office Desktop components, which are "experience first." You want to make the experiences as rich as you can and you code to the [Windows] platform in order to do that.

 

Adobe is a great example. Flash is a rich client; it's rich code delivered to the edge. It's not a centralized model; it's a decentralized model. It just happens to be tethered to the service.
If anybody has a software and services model, it's Adobe, because of that rich [Flash Player] applet that they extend the browser with. The more they enhance that, as you can see in their Flex and Apollo plans, the more it becomes this unified software and service vision, which is basically the same as Microsoft's vision.
We've been talking about it mostly from a technology perspective and what that can do for an end user. One of the other big things that [a] services [architecture] does for traditional software companies is change the way that we touch our customers.

Long, but interesting if you have the time.

March 28, 2007

Brrrr ... it's cold out here Freezing my WPF off ...

I posted a few days ago about a technique for loading a BitmapImage in the background. Charles  suggested I try loading (the image) in a background thread and then Freeze the BitmapImage; finally, handing it off to the UI thread.

If you'd like to know more about freezing and WPF, there's a decent article available on MSDN here.

Success! It works. But I discovered something sinister going on. Downright evil.

The new implementation was slower. Way slower. How could that be? I was still using a background thread. The code was simpler. I did notice that I had changed from using the MemoryStream to using the UriSource property. A little bit of experimentation later, I discovered that the StreamSource property was also slow when I passed it a FileStream object. Confused thoroughly at this point, I switched back to the MemoryStream in a last ditched attempt knowing it wouldn't help -- but it did!!!! It was fast again! Here's what I ended up with:

 

        private void DoLoadImage(object o)
        {
            if (!_loading) { return; }

byte[] buffer = File.ReadAllBytes(_filename);
// by reading the data into an in-memory buffer,
we prevent the file from being read in the UI thread -- which speeds up

// access dramatically!
MemoryStream mem = new MemoryStream(buffer);

if (!_loading)
{
mem.Dispose();
return;
}

BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = 80;
bi.DecodePixelHeight = 60;
//bi.UriSource = new Uri(_filename); slow slow slow
bi.StreamSource = mem;
bi.EndInit();
bi.Freeze();

_imageSource = bi;
_loading = false;
RaisePropetyChanged("IsLoading");
RaisePropetyChanged("ImageSource");

// if you dispose of the memory stream here, the image will be toast (burnt toast)
// (as the dispatcher won't have run yet).
}


For some reason, the MemoryStream technique blows away the performance of any other option. It's at least 5 - 10 times faster than the other options! If anyone knows why, I'd love to hear it! As you can see though, if you compare to my original post, I've gotten rid of the Dispatcher reference and just directly create the BitmapImage. The key though is that I "Freeze" the BitmapImage so that it can be used outside of the creating thread and back in the UI thread.

WPF Rocks

I'm amazed what can be accomplished in WPF with so little code! I'll be posting in detail about all the little things of this very soon (as there's still one more thing I want to add as a demo), but in 344 lines of C# code, and 314 lines of XAML, I created this tiny little application:

 

It doesn't do much really yet -- but it's still very cool. I've got a custom scroll bar, a small reflection on the photos that are being loaded. An animated wait hourglass that bobbles gently back and forth. Clicking on an image automatically loads a larger view. below. It sizes to fit. It releases the resources smartly when they aren't needed anymore.

The lower part, the large image, is just a simple DataTemplate:

 

    <DataTemplate x:Key="ImageDetailTemplate" DataType="{x:Type local:Photo}">
      <Grid>
        <Image x:Name="imgFull" Source="{Binding LargeImageSource}"/>
      </Grid>
    </DataTemplate>

 

I used the property/attribute IsSynchronizedWithCurrentItem on the ListBox to automatically keep the "detail" view in sync with the selected item in the ListBox:

 

      <ListBox Style="{StaticResource PhotoListBoxStyle}" 
ItemsSource="{Binding Photos}" Grid.Row="0" Margin="6,6,6,0" VerticalAlignment="Top" Height="110" IsSynchronizedWithCurrentItem="True" ItemTemplate="{StaticResource PhotoItemTemplate}" ScrollViewer.VerticalScrollBarVisibility="Hidden" BorderBrush="{x:Null}" > <ListBox.Background> <LinearGradientBrush EndPoint="0.466,0.06" StartPoint="0.465,0.877"> <GradientStop Color="#FF000000" Offset="0"/> <GradientStop Color="#FF3E3E3E" Offset="1"/> </LinearGradientBrush> </ListBox.Background> </ListBox>

<ContentControl Grid.Row="1" ContentTemplate="{StaticResource ImageDetailTemplate}"
Content="{Binding Photos}"/>


The biggest section of XAML code as you'll see in a later post is actually the silly scrollbar I created! :)


I'll add a bit of photo metadata display and the ability to select a directory to scan (maybe via drag and drop) and post details soon.

March 27, 2007

Loading BitmapSources in the Background ...

I've hit this problem several times now.

I've wanted to load an image on another thread (other than the UI thread), and then display it in a WPF UI. Bam. Doesn't work in WPF. You have to load/create the Bitmap on the UI thread that will ultimately display the image. If the images are from the network or a slow source, this can really nail performance of the application (as it pauses and hangs trying to load the Bitmaps). Here's a work-around that I'm experimenting with. I'll post a larger sample soon, but I thought I'd post this now:

 

   1:          private void DoLoadImage(object o)
   2:          {
   3:              if (!_loading) { return; }
   4:   
   5:              // this creates a lot of temporary buffers (should try to reuse them somehow...?)
   6:              byte[] buffer = File.ReadAllBytes(_filename);            
   7:              // by reading the data into an in-memory buffer
, we prevent the file from being read in the UI thread -- which speeds up
   8:              // access dramatically!
   9:              MemoryStream mem = new MemoryStream(buffer);
  10:   
  11:              if (!_loading)
  12:              {
  13:                  mem.Dispose();
  14:                  return;
  15:              }
  16:              this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, 
(DelegateZeroParam)delegate()
  17:              {
  18:                  if (!_loading) {
  19:                      mem.Dispose();
  20:                      return; 
  21:                  }
  22:   
  23:                  BitmapImage bi = new BitmapImage();
  24:                  bi.BeginInit();
  25:                  bi.DecodePixelWidth = 80;
  26:                  bi.DecodePixelHeight = 60;
  27:                  bi.StreamSource = mem;
  28:                  bi.EndInit();
  29:                  _imageSource = bi;
  30:                  _loading = false;
  31:                  RaisePropetyChanged("ImageSource");
  32:              });
  33:              
  34:              // if you dispose of the memory stream here, the image 
will be toast (burnt toast)
  35:              // (as the dispatcher won't have run yet).
  36:          }

The _loading variable I'm using to decide whether loading should still happen for this object. The object (a "Photo" in this case is a Dependency object so it has access to the Dispatcher. The trick is to load the bytes for the image in a thread, but then in the UI thread actually create the Bitmap. I do that using a slight of hand delegate declared as:

    public delegate void DelegateZeroParam();

Of course, you'd need to wrap the actual call to this method in some background loader thread to make it useful.

This technique comes at the cost of bytes, but it make the UI very snappy when compared to a UI-thread only method. (I'll post more about why I'm raising the "ImageSource" property changed in another post).

 

UPDATE: Charles just suggested I try freezing the BitmapImage. New post here.

March 25, 2007

BitmapImage.UriSource binding doesn't work ...

For whatever reason, WPF/XAML doesn't support direct binding of a Uri object to the UriSource of a BitmapImage object. When run, the application fails with an error, "Property 'UriSource' or property 'StreamSource' must be set."  (Here's a complaint about this on MSDN Forums).

This makes easy displays of image items with a source or Uri property more challenging than it should be. I've found one work-around for now (it's not as snappy as I'd like though if the images are large). As with any binding of images, binding to large images may have a negative impact on application performance as Bitmap's must be created on the UI thread.

Here's how to use my new UriToImageConverter value converter:

First, create, in the appropriate Resources, an instance of the new ValueConverter. 

<local:UriToImageConverter x:Key="LocalUriToImageConverter"/>

Of course, you'll need to declare the new xml namespace (usually in the root element in XAML, such as the Window or Page):

xmlns:local="clr-namespace:MyApplicationNamespace"

Then, in the DataTemplate (which is likely where you'd use this), you'll need to use the converter, not as a converter to a Uri, but to the ImageSource itself:

<Image x:Name="imgPhoto" Width="80" Height="60" Grid.Row="0"
Source="{Binding Path=FilenameUri,
Converter={StaticResource LocalUriToImageConverter}}"
>

Finally, add the C# code below to your project.

The Path in this instance can be either a string or a Uri. The trick is that this converter returns a new ImageSource:

 
   1:      public class UriToImageConverter : IValueConverter
   2:      {
   3:   
   4:          public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
   5:          {
   6:              if (value == null)
   7:              {
   8:                  return null;
   9:              }
  10:   
  11:              if (value is string)
  12:              {
  13:                  value = new Uri((string)value);
  14:              }
  15:   
  16:              if (value is Uri)
  17:              {                
  18:                  BitmapImage bi = new BitmapImage();
  19:                  bi.BeginInit();
  20:                  bi.DecodePixelWidth = 80;
  21:                  //bi.DecodePixelHeight = 60;                
  22:                  bi.UriSource = (Uri)value;
  23:                  bi.EndInit();
  24:                  return bi;
  25:              }
  26:   
  27:              return null;
  28:          }
  29:   
  30:          public object ConvertBack(object value, Type targetType, object parameter, 
System.Globalization.CultureInfo culture)
  31:          {
  32:              throw new Exception("The method or operation is not implemented.");
  33:          }
  34:   
  35:      }

Note that once the value is converted to a Uri (if necessary), a new BitmapImage object is created [line 18]. Then, within the appropriate Begin/End Init block [lines 19-23], the UriSource (and an example DecodePixelWidth is set). The new ImageSource (BitmapImage) is returned to the converter. So, the Uri is converted directly to the BitmapImage, with WPF being none-the-wiser!

 

March 23, 2007

Rotated text in a TabControl

It's not obvious how to rotate the text in a WPF TabControl TabItem's Header. However, it is easy. :)

The trick is to adjust the TabItem's HeaderTemplate. I created a simple DataTemplate that rotated the contained content using a LayoutTransform (a RenderTransform would have clipped the content).

 

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window"
    Title="Window1"
    Width="640" Height="480" >

<Window.Resources>
<DataTemplate x:Key="TabItemHeaderTemplate">
<Grid>
<ContentPresenter Content="{TemplateBinding Content}"
RenderTransformOrigin="0.5,0.5" >
<ContentPresenter.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="-90"/>
</TransformGroup>
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</Grid>
</DataTemplate>
</Window.Resources>

<Grid x:Name="LayoutRoot">
<TabControl Margin="163,47,64,50"
IsSynchronizedWithCurrentItem="True"
TabStripPlacement="Left">
<TabItem Header="A big one"
HeaderTemplate="{DynamicResource TabItemHeaderTemplate}">
<Grid>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="128"
Text="1"
TextWrapping="Wrap"/>
</Grid>
</TabItem>
<TabItem Header="A big two"
HeaderTemplate="{DynamicResource TabItemHeaderTemplate}">
<Grid>
<TextBlock FontSize="128"
Text="2"
TextWrapping="Wrap"
Margin="143.02,83.375,143.02,83.375"/>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>

March 13, 2007

RoundedRectangle for GDI+ and C#

OK, I've written this code so many times I've lost count. Every time I write it, I loose it. L So, I'm going to post it here hoping that I'll find it if I should ever need it again in the future. I noticed today when I was searching for code that draws a rounded rectangle (RoundRect) in C# in WinForms, that my web site was displaying within the top 10 hits on Google! What's most annoying about that is that it was WPF related use of a rounded rectangle, not Windows Forms!

In any case, here's the code for helping to draw a rounded rectangle in C#. It's a function sorely missing on the Graphics object built into the .NET CLR.

internal static GraphicsPath CreateRoundedRectanglePath(Rectangle rect, int cornerRadius)

        {

            GraphicsPath roundedRect = new GraphicsPath();
            roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90);
            roundedRect.AddLine(rect.X + cornerRadius, rect.Y, rect.Right - cornerRadius * 2, rect.Y);
            roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y, cornerRadius * 2, cornerRadius * 2, 270, 90);
            roundedRect.AddLine(rect.Right, rect.Y + cornerRadius * 2, rect.Right, rect.Y + rect.Height - cornerRadius * 2);

            roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y + rect.Height -  cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90);

            roundedRect.AddLine(rect.Right - cornerRadius * 2, rect.Bottom, rect.X + cornerRadius * 2, rect.Bottom);
            roundedRect.AddArc(rect.X, rect.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90);
            roundedRect.AddLine(rect.X, rect.Bottom - cornerRadius * 2, rect.X, rect.Y + cornerRadius * 2);
            roundedRect.CloseFigure();
            return roundedRect;

        }

 

Call this function with the rectangle (using X, Y, Width and Height), and the corner radius. It returns a GraphicsPath object. To draw the rounded rectangle, use a built in Pen or create your own and call the DrawPath function on the Graphics object.

Make sure you Dispose of the GraphicsPath object once your done.

A handy way is to do something like this:

using(GraphicsPath path = CreateRoundedRectanglePath(myRect, 4))
{
    graphics.DrawPath(SystemPens.ControlText, path);
}

March 12, 2007

A Drop, an object, the TreeViewItem and ContainerFromElement Solution

How do you return the actual TreeViewItem on which a user dropped a dragged item (usually started with a DoDragDrop)?

It's not obvious, and I'm not sure this is the best solution. The best option would be, if it worked, to call ContainerFromElement on the TreeView. However, calling that returns the parent of the TreeViewItem in question (actually, it might be many ancestors higher than what you'd expect). I wrote my own simple implementation to work around the issue:

void treeKeywords_Drop(object sender, DragEventArgs e)

{

TreeViewItem item = GetTreeViewItemContainerFromElement(sender as ItemsControl, e.OriginalSource as DependencyObject);

if (item != null)

{

// do something with the header--it contains your wrapped object ..

Keyword k = item.Header as Keyword;

if (k != null)

{

Console.WriteLine(k.Title);

}

}

}

 

public static TreeViewItem GetTreeViewItemContainerFromElement(ItemsControl itemsControl, DependencyObject element)

{

DependencyObject d = element;

 

while (d != null && !(d is TreeViewItem))

{

d = VisualTreeHelper.GetParent(d);

}

 

return d as TreeViewItem;

}

The key is the function GetTreeViewItemContainerFromElement. It loops through Visuals, checking each parent. If a parent is a TreeViewItem, it stops and returns it. From the Drop method, the code gets the OriginalSource (which might be a TextBlock for example), passes it and the tree reference to the function and waits for a response. If a TreeViewItem is located, the Header property contains the original object that was stored in the TreeViewItem (at least when data bound).

March 11, 2007

Extended Mode Listbox selection enhancements in WPF

The standard WPF (.NET 3.0) ListBox with SelectionMode set to Extended wasn't behaving the way I wanted. By default ListBoxItems always toggle selection when the left mouse button is pressed down. That's fine for most listboxes I suppose. However, for a listbox that you want to allow drag and drop, it's extremely annoying to have the item you recently selected, become deselected when you start a drag from that item (click down, and unselected!). I wanted the deselection to happen on the mouse button up, not the down (and a quick study of other applications confirmed this is how most other 'extended-mode' ListBoxes behave).

Here is my current work-around for this problem in C#:

public class PhotoListBox : ListBox

{

    protected override DependencyObject GetContainerForItemOverride()

    {

        return new PhotoListBoxItem();

    }

}

 

public class PhotoListBoxItem : ListBoxItem

{

    private bool _mouseWasDownAndSelected = false;

 

    protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)

    {

        if (!IsSelected)

        {


				// it wasn't selected, so just do the normal thing

            base.OnPreviewMouseLeftButtonDown(e);

            return;

        }

 


				// it was selected, so we're going to totally ignore the mouse down...

        e.Handled = true;


				// but we will mark it ...

        _mouseWasDownAndSelected = true;

    }

 

    protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)

    {


				// if we were watching this one, we'll unselect it if it were already selected

        if (IsSelected && _mouseWasDownAndSelected)

        {

            IsSelected = _mouseWasDownAndSelected = false;

        }

 

        base.OnMouseLeftButtonUp(e);

    }

}

 

In the XAML corresponding to my UI I was changing, the only change that was necessary was to modify the ListBox element to use my new PhotoListBox class. I added a CLR reference to the main class:

xmlns:local="clr-namespace:ExampleNamespaceSameAssembly"

 

Then, I modified the ListBox element:

<local:PhotoListBox x:Name="listboxPhotos" SelectionMode="Extended" ItemsSource="{Binding Mode=OneWay}" …. />

 

I've created two new classes: PhotoListBox and PhotoListBoxItem for this example.

When items are added to a ListBoxItem, you can explicitly or implicitly add a ListBoxItem wrapper for each item. In this case, because of the use of databinding in my application, I couldn't easily wrap each item manually (explicitly) in a custom ListBoxItem, so I needed to take a different approach. That approach uses the override method, GetContainerForItemOverride. All that needs to be done in this method is to return a new instance of the custom ListBoxItem, above it's PhotoListBoxItem. WPF takes care of the rest. For each item that was databound into the ItemsSource for my custom ListBox, it's automatically wrapped in the PhotoListBoxItem.

Step 2 in my work was to change the behavior of the standard ListBoxItem. That turned out to be a bit trickier than I had expected. Most natural ways I attempted to solve the problem were prevented by either a lack of overridable methods in the ListBoxItem or publicly available methods on the ListBox itself. Here's how I handled it (refer to the code for the exact detail of course).

The left mouse button is key in changing the behavior, so I knew I needed to override the tunneling-like method (On)PreviewMouseLeftButtonDown. I needed the code to make a simple choice, continue processing the button, or ignore it (Handled = true or false?). It's ignored if the item is currently selected (again, to give that toggle behavior that I actually want: the ListBoxItem should become deselected on the UP only if it was already selected). I added a tracking member variable (_mouseWasDownAndSelected) and in the OnPreviewMouseLeftButtonUp event, toggle the selection as needed and always call the base handler.

 

Enjoy.

February 17, 2007

Running Visual Studio 2005 as Administrator on Vista

I'm still peeved that I need to run Visual Studio 2005 as an Administrator on Vista. Even more annoying is the UAC dialog that I need to see every time I launch it.

One simple shortcut for making it easier to run it as an administrator every time you launch it (so you can stop with the "right-click", "Run as Administrator" option) is to force Windows to launch it always as an Administrative application. It doesn't make the UAC dialog go away, but at least I'm less likely to launch it incorrectly this way (and maybe you'll be too!).

Here's how – it's very simple:

Find the option in your start menu either via search or navigation:

Right click and select Properties:

 

Click the "Advanced…" button to see the Advanced Properties window:

Then check the "Run as administrator" option.

Of course, this same technique works for any application that you must run as an Administrative user.

A few handy WPF Visual Studio 2005 Snippets

Here are a handful of simple snippets I've made to some existing WPF snippets that I find useful when coding .NET 3.0.

Paste them into a file with the extension

.snippet
and place them in the following directory:

…your account…\Documents\Visual Studio 2005\Code Snippets\Visual C#\My Code Snippets

 

Restart Visual Studio 2005 and the new snippets should be available.

The snippets included are:

Name

Description

propdpc

Create a new dependency property, using FrameworkPropertyMetadata, which is normally more useful than PropertyMetadata, and also an event to handle when the property changes (for those occasions when the Options flags don't suffice).

propdpf

Same as above, although without the event.

Rpnc

Code for "RaisePropertyNotifyChanged". If you implement INotifyPropertyChanged and like to encapsulate the call to the event, this is the function you'll often want.

Propr

A standard .NET backed property, but calls the function defined in the previous snippet.

 

<?xml version="1.0" encoding="utf-8" ?>

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

    <CodeSnippet Format="1.0.0">

        <Header>

            <Title>Define a DependencyProperty</Title>

            <Shortcut>propdpc</Shortcut>

            <Description>Dependency property, using frameworkPropertyMetadata, with PropertyChanged event</Description>

            <SnippetTypes>

                <SnippetType>Expansion</SnippetType>

            </SnippetTypes>

        </Header>

        <Snippet>

            <Declarations>

                <Literal>

                    <ID>type</ID>

                    <ToolTip>Property Type</ToolTip>

                    <Default>int</Default>

                </Literal>

                <Literal>

                    <ID>property</ID>

                    <ToolTip>Property Name</ToolTip>

                    <Default>MyProperty</Default>

                </Literal>

                <Literal>

                    <ID>ownerclass</ID>

                    <ToolTip>The owning class of this Property. Typically the class that it is declared in.</ToolTip>

                    <Default>ownerclass</Default>

                </Literal>

                <Literal>

                    <ID>defaultvalue</ID>

                    <ToolTip>The default value for this property.</ToolTip>

                    <Default>0</Default>

                </Literal>

                <Literal>

                    <ID>frameworkoptions</ID>

                    <ToolTip>The options for this property and how it affects the DP</ToolTip>

                    <Default>.None</Default>

                </Literal>

            </Declarations>

            <Code Language="csharp">

                <![CDATA[public $type$ $property$

{

get { return ($type$)GetValue($property$Property); }

set { SetValue($property$Property, value); }

}

 

public static readonly DependencyProperty $property$Property =

DependencyProperty.Register("$property$", typeof($type$), typeof($ownerclass$),

new FrameworkPropertyMetadata($defaultvalue$,

FrameworkPropertyMetadataOptions$frameworkoptions$,

new PropertyChangedCallback($property$PropertyChanged)));

 

private static void $property$PropertyChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)

{

// put code here to handle the property changed for $property$

$ownerclass$ $ownerclass$Obj = depObj as $ownerclass$;

if ($ownerclass$Obj != null)

{

 

}

}

 

$end$]]>

            </Code>

        </Snippet>

    </CodeSnippet>

    <CodeSnippet Format="1.0.0">

        <Header>

            <Title>Define a DependencyProperty</Title>

            <Shortcut>propdpf</Shortcut>

            <Description>DependencyProperty, using FrameworkPropertyMetadata, no change notification</Description>

            <SnippetTypes>

                <SnippetType>Expansion</SnippetType>

            </SnippetTypes>

        </Header>

        <Snippet>

            <Declarations>

                <Literal>

                    <ID>type</ID>

                    <ToolTip>Property Type</ToolTip>

                    <Default>int</Default>

                </Literal>

                <Literal>

                    <ID>property</ID>

                    <ToolTip>Property Name</ToolTip>

                    <Default>NameTheProperty</Default>

                </Literal>

                <Literal>

                    <ID>ownerclass</ID>

                    <ToolTip>The owning class of this Property. Typically the class that it is declared in.</ToolTip>

                    <Default>OwnerClassType</Default>

                </Literal>

                <Literal>

                    <ID>defaultvalue</ID>

                    <ToolTip>The default value for this property.</ToolTip>

                    <Default>0</Default>

                </Literal>

                <Literal>

                    <ID>frameworkoptions</ID>

                    <ToolTip>The options for this property and how it affects the DP</ToolTip>

                    <Default>.None</Default>

                </Literal>

            </Declarations>

            <Code Language="csharp">

                <![CDATA[public $type$ $property$

{

get { return ($type$)GetValue($property$Property); }

set { SetValue($property$Property, value); }

}

 

public static readonly DependencyProperty $property$Property =

DependencyProperty.Register("$property$", typeof($type$), typeof($ownerclass$),

new FrameworkPropertyMetadata($defaultvalue$,

FrameworkPropertyMetadataOptions$frameworkoptions$));

 

$end$]]>

            </Code>

        </Snippet>

    </CodeSnippet>

    <CodeSnippet Format="1.0.0">

        <Header>

            <Title>rpnc</Title>

            <Shortcut>rpnc</Shortcut>

            <Description>Code snippet for a RaisePropertyChanged method (used by propr)</Description>

            <Author></Author>

        </Header>

        <Snippet>

            <Code Language="csharp">

                <![CDATA[protected void RaisePropertyChanged(string propertyName)

{

System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(propertyName));

 

if (PropertyChanged != null)

{

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

}

$end$]]>

            </Code>

        </Snippet>

    </CodeSnippet>

    <CodeSnippet Format="1.0.0">

        <Header>

            <Title>propr</Title>

            <Shortcut>propr</Shortcut>

            <Description>Code snippet for property and backing field with Notification via RaisePropertyChanged method</Description>

            <Author></Author>

            <SnippetTypes>

                <SnippetType>Expansion</SnippetType>

            </SnippetTypes>

        </Header>

        <Snippet>

            <Declarations>

                <Literal>

                    <ID>type</ID>

                    <ToolTip>Property type</ToolTip>

                    <Default>int</Default>

                </Literal>

                <Literal>

                    <ID>property</ID>

                    <ToolTip>Property name</ToolTip>

                    <Default>MyProperty</Default>

                </Literal>

                <Literal>

                    <ID>field</ID>

                    <ToolTip>The variable backing this property</ToolTip>

                    <Default>myVar</Default>

                </Literal>

            </Declarations>

            <Code Language="csharp">

                <![CDATA[private $type$ $field$;

 

public $type$ $property$

{

get { return $field$;}

set

{

if ($field$ != value)

{

$field$ = value;

RaisePropertyChanged("$property$");

}

}

}

$end$]]>

            </Code>

        </Snippet>

    </CodeSnippet>

</CodeSnippets>

 

 

January 16, 2007

How to display a formatted number in WPF

I needed to format a double into a sane format for an application I'm working on.

The data is somewhat more precise than I need.

I wanted to format the exposure time more reasonably with only a few decimal points rather than the 4000 digit number I seemed to be getting ... . So, a handy value Converter was needed to fix the problem:

    [ValueConversion(typeof(object), typeof(string))]
    public class ToStringFormatConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string fmt = parameter as string;
            if (!string.IsNullOrEmpty(fmt))
            {
                return string.Format(culture, fmt, value);
            }
            else
            {
                return value.ToString();
            }
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
        
    }

Then, using Binding and the Converter:

<TextBlock Text="{Binding Path=ExposureTime, Converter={StaticResource stringFormatConverter}, ConverterParameter={}{0:#0.0##}}" />

I needed to add to the resources section of my XAML file, an instance of this object:

<local:ToStringFormatConverter x:Key="stringFormatConverter"/>

The local namespace was added to the Window class (it could be added to any other root level WPF element like a page, etc):

xmlns:local="clr-namespace:MyApplicationNamespace"

To adjust the format, just change the ConverterParameter format shown above in red ({0:#0.0##}). This is just the normal ToString formatters that you have available in any .NET application. No gimmicks. The leading curly braces {} are used to escape the remainder of the string.

The ValueConversion attribute on the class I created is used to indicate to development environments and tools what types of inputs and outputs are expected to the function. In this case, it's always a string out, but the in varies.

Here was the result:

A nicely formatted number.

January 1, 2007

Broken: RichTextBoxes with embedded UIElements

If you've been using the BlockUIContainer or InlineUIContainer elements in a FlowDocument, embedded within a RichTextBox, AND want the embedded UIElements to be enabled in V1 of WPF/.NET 3.0, give up now.

<RichTextBox>
<FlowDocument>
<Paragraph>
<Run>
Here is some text in a paragraph
</Run>
</Paragraph>
<BlockUIContainer IsEnabled="True">
<Button Click="BlockUIClick" IsEnabled="True">Click me please!</Button>
</BlockUIContainer>
<Paragraph>
<Run>
Here is some text in a paragraph after a control.
</Run>
</Paragraph>
<Paragraph>
<Run>
Here is some text in a paragraph
</Run>
<InlineUIContainer>
<Button Click="InlineUIClick">Click me!</Button>
</InlineUIContainer>
</Paragraph>
</FlowDocument>
</RichTextBox>


As of the released version of WPF/.NET 3.0, this functionality no longer works at all if you want the control to actually be enabled -- and appears to be intentional/as expected. Several Microsoft employees have stated as much in forum posts.

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=389348&SiteID=1
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=716043&SiteID=1 (look for a post by Prajakta Joshi)

I'm disappointed to say the least that this wasn't included in the final V1 release, as it's something the company I work for would absolutely have used. I really hope it returns in V2.

Here are some links to a couple of other pages where this was documented as working:

WiredPrairie

Charles Petzold

December 19, 2006

The A-Z game

When you were a child, did you ever take long driving trips with your family? 20 years ago we didn't have all the electronic toys and such to play with, so we were forced to entertain ourselves via simpler means. One game was to name something that began with each letter of the alphabet. The specific "something" changed, but might have been plants, animals, cities, etc.

So, tonight in a geeky trip home from work, the "something" was programming language keywords.

Here's your challenge:

Using each letter of the alphabet (A-Z), come up with a keyword from a programming language you know. For example, the letter "R" might be "Return." Good luck. I couldn't get two letters. How did you do?

December 6, 2006

Atlas/ASP.NET Ajax WPF/E toolkit extender

Here. (Makes it simple to add a WPF/E control to a ASP.NET AJAX enabled web site).

WPF/E and Flickr Demo

I'm sure the WPF/E demo's are going to be flooding the internet ... but in the mean time, here's one that uses Flickr and Script#:

WPF/E and Script#

Setup WPF/E without installing extra VS addins

From John Rayner -- a project template which makes it a bit easier and less impactful to install the WPF/E project templates. The installer that is included with the WPF/E SDK requires extra downloads by default -- which you may not want (or need).

December 1, 2006

ColorToBrushConverter

Although it took me 2 minutes to create this morning after searching for 2 minutes -- I thought I'd post this:

(I needed a Color to SolidColorBrush converter so that I could display the actual physical color rather than the name)

public class ColorToBrushConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Color)
{
return new SolidColorBrush((Color)value);
}
return null;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}

#endregion
}

Although very simple, it was very useful in this situation:

I have an object with a property of type Color:

public Color Color
{
get { return _color; }
set { _color = value; RaisePropertyChanged("Color"); }
}

I wanted to bind a rectangle with the color rather than just the name of the color:

<Rectangle Fill="{Binding Color, Converter={StaticResource myColorToBrushConverter}}" Width="20" Height="20" />

I created an instance of the converter in the local resources:

<ListView.Resources>
<local:ColorToBrushConverter x:Key="myColorToBrushConverter" />
</ListView.Resources>

And made sure I added the clr namespace to the XAML file:

xmlns:local="clr-namespace:WPFCheckBoxDemo"

And here it is in a simple list:

More on the list and why I was doing this in a later post ...

October 15, 2006

Software versioning gone MAD!

When did it become acceptable to consistenly release products under the guise of any of the following terms:

  • Beta
  • Release Candidate
  • Preview
  • Test
  • Developer Preview
  • Sneak Peek
  • Public demo
  • Launch Preview
  • Or any version number less than 1.0?
  • (Or combinations of the above)

What others have you encountered? Or great examples of this problem ...?

Songbird, the be-all cross-platform mash-up music player, digital jukebox, etc. (yawn!) is a product that seems to be suffering from this versionitis.

The Songbird 0.2 Developer Preview Release Cadidate 3! What a mouthful! What does that really mean? Version 0.2? No one should download a version 0.2 of any software application and expect it do anything useful. That's how software is versioned. It's well accepted in the software industry.

Unfortunately, I couldn't find any explanation from the download link that explained what I should expect in a version 0.2 Developer Preview RC3 release. If, as software developers, web site creators, etc, you don't set expectations, and people have a negative experience, it's VERY easy for your users to abandon your product for good. A negative first impression can lead to a long term negative impression of your product/company/etc.

In looking at their web site though, it's clear that much of the application is functional. So, they need to do one of two things:

  • clear up the version and who should really download this application (is this really only for developers?)
  • version it appropriately

Maybe take a more agile approach to development and set some realistic accomplishable goals and release a fully supported, non-beta version. For Songbird in particular, maybe trim a couple of features for a version 1.0 release! Keep the quality bar HIGH though! But please, don't confuse poor unsuspecting users with these terms and web site where the product looks nearly complete if it really isn't.

To be clear, I'm fine with beta products. It's one thing to release a beta/preview right before a release, but there should be limited period of time where the "beta" name is stamped and allowed. Your users need to understand the ramifications though of what it means to install or use your beta products very clearly. Especially if data loss is possible, even if the data loss is a "simple" upgrade from a final beta to the real product. Treat beta users with respect as they are your likely champions. If you wipe out their data, intentionally or unintentionally, you may lose a customer for life.

(And since operating systems are a very different beast from the 99.9% of applications most people encounter, my suggestions are still true -- although the period in which a product can be in beta may need to be longer).

October 12, 2006

Simple XAML Font Viewer

Admittedly, I forgot why I started writing this tiny program, so, before I put it to rest (or remember what my original goal was), I thought I'd post what little code I had for a tiny font viewer.

What's interesting about this code?

  1. The tooltip is styled. Actually, very plainly as a matter of fact, demonstrating how the pop-up can not only change dramatically, but also how it can be free of any bounds entirely (henc the see-thru look).
  2. I demonstrated how a DataTemplate can actually refer to itself (by binding the Tooltip to the current item's DataTemplate).
  3. It shows how you can bind to some simple sources without a line of code (only XAML) using CollectionViewSource.
  4. It also might help you to understand that when you bind to something like a listbox (or anything really), the Binding source doesn't go away -- it's just some visual's layered on top. So, here: <TextBlock FontFamily="{Binding}"> . Sure, this looks strange, but what it's doing is for each Font, it's using the FontFamily object and setting it to the FontFamily of the TextBlock. By using a "naked" binding reference, it's referring directly to the object being bound rather than specifying any named properties (using the Path syntax)
  5. It's also easy to write a fancy list box. Imagine how long this would take to do in Win32!

If you have XAMLPad installed (which if you have the .NET 3.0 SDK you do!), you can copy this XAML and try it.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Height="508" Width="504"

>

<Page.Resources>

<CollectionViewSource Source="{x:Static Fonts.SystemFontFamilies}" x:Key="FontList" />

<Style TargetType="{x:Type ToolTip}">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="{x:Type ToolTip}">

<Border >

<ContentPresenter />

</Border>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

<DataTemplate DataType="{x:Type FontFamily}">

<WrapPanel>

<StackPanel x:Name="MyVisual">

<TextBlock Text="{Binding Source}" x:Name="MyTextName" />

<TextBlock FontFamily="{Binding}">

abcdefghijklmnopqrstuvwxyz

</TextBlock>

<TextBlock FontFamily="{Binding}">

ABCDEFGHIJKLMNOPQRSTUVWXYZ

</TextBlock>

<TextBlock FontFamily="{Binding}">

1234567890

</TextBlock>

<TextBlock FontFamily="{Binding}">

!@#$%^&*()_+-=[]{}\|;:'",./><?

</TextBlock>

<StackPanel.ToolTip>

<Viewbox Width="500" Height="200">

<Label HorizontalAlignment="Left">

<Binding />

</Label>

</Viewbox>

</StackPanel.ToolTip>

</StackPanel>

</WrapPanel>

</DataTemplate>

</Page.Resources>

<Grid>

<Grid.RowDefinitions >

<RowDefinition Height="Auto" />

<RowDefinition />

</Grid.RowDefinitions>

<Label Content="Fonts" Grid.Row="0" />

<ListBox Grid.Row="1" Margin="6" ItemsSource="{Binding}" DataContext="{DynamicResource FontList}" />

</Grid>

</Page>

October 3, 2006

Borland "Turbos" are now available...

Borland, per an earlier announcement, has made available "Explorer" editions of their products: Delphi, C++, Delphi for .NET, and C# here. The explorer edition is free -- yet for some reason, you're only allowed one per machine. With that limitation, I haven't decided which one I'd rather try.... (Borland? Hello? Why only one?!)

Update:

Turbo Delphi for .NET

With Turbo™ Delphi® for .NET, you can develop powerful desktop and web applications in Delphi for WinForms, VCL.NET, and ASP.NET Produce 100% PE Verifiable .NET assemblies and have access to the entire .NET Framework 1.1 Class Library. You can build .NET Web Services, .NET remoting servers and clients, and components for WinForms, VCL.NET and ASP.NET. Code faster with refactorings, live templates, code completion, code insight and block completion. And with the customizable and extendable environment for build-your-own and third party components and IDE add-ons you have everything you need to start real .NET development today!

I can narrow down my choices. I'm not going back to .NET 1.1. (Borland?! Hello?! Why not 2.0?). That eliminates Turbo Delphi for .NET and Turbo C#.

If you're thinking about doing Windows .NET development and want a free option, try the Microsoft Visual Studio Express Editions instead. Download them here.

October 2, 2006

WPF LOB Feedback

Rob Relyea asked recently for feedback regarding needs for LOB applications.

Some of my feedback is here (and Rob, if you're reading this, contact me directly at work for more information about any of these!):

  • Performance is important. Application visuals shouldn't get in the way of getting a job done. I'm certainly not suggesting that LOB applications should look boring! By no means! Instead, I want these visual features to impact typical LOB workflow scenarios minimally.
  • Workstation - it's fair to say that some businesses will upgrade their machines next year after Vista and WPF releases, however historically few will do it because of Vista or WPF. So, Microsoft's workstation recommendations must remain grounded in reality at all times. Many businesses lag behind consumers in their hardware upgrade cycles simply because the demand isn't there. If the LOB applications are functioning well enough on existing hardware, it's a hard sell often to convince an IT purchaser to upgrade hardware. If of course, a software application written in WPF can actually save measurable time of a users, that might convince an otherwise stingy IT budgeteer :) that new workstations are needed.
  • Expression suites and Cider: None of these WPF tools are up to the Microsoft typical quality bar for development experience. In all honestly, I'd rate them at a VB 3 or lower experience for visual designer experience. The code behind and schema of course are awesome, but for a typical enterprise or LOB application developer, they desperately need good UI tools to develop reasonable applications. It's very easy, almost too easy, to create a WPF application that is uglier than the mid-late 90's web sites that looking back were simply AWFUL. With some effort, it is possible to make a reasonable application -- but it's not as easy as it should be.
  • Data Entry controls - Where are the standards that developers have come to expect? We're tired of buying third party controls that often work reasonably well, but are harder to support, don't quite have the features we need, and are bloated bigger than what we'd like. Some of the basic features that are missing in WPF 1.0 include an autocomplete text entry field; a data entry grid; a date-time-calendar style field; a calendar; a charting control (ideally one that can make Office 2007 style charts!); a rich editor that can save HTML and not the venerable RTF who's time has passed; a fully supported graph panel (like Kevin's here/here); Office 2007 like controls/menus/etc (normally not a big fan of copying, but there's some really great innovation there that Microsoft should encourage duplication by providing a great implementation); masked text fields (although I wouldn't use them, I just know lots of developers like them); validating/warning editing fields that like many data entry web pages can visually show fields with problems; and I'm sure there are others that I'll run into but even more important is the last point I make below.
  • Terminal Services / down-level XP Remote Desktop support - Many companies are using terminal servers or virtualized machines to provide access to applications across an enterprise. WPF applications should not be penalized for running in this environment. Make TS (and Citrix too!) support first class to XP/2003 and Vista consuming clients of a remote session.
  • Tools - In the past couple of years, Microsoft has excelled at communication and providing tools at early stages so developers could learn about upcoming technologies. Awesome! The tools though I'm referring to here are the little things. Today for example, I wanted to create a Vista ready icon. Sure there are a number of 3rd party applications who sell Vista ready icon tools -- but all I need is a way to take a PNG and convert it to a ICO format that works on Vista and XP. Not a full blown editor -- I own Photoshop and don't want an editor. There's others that I've run into -- but I can't recall them right now ....
  • XAML/WPF -  During the period where developers/designers will encounter XAML and hard-core WPF -- I'd stress that the more REALISTIC samples/demos/applications that Microsoft can provide the better. The best however would be to provide as many ways though of simplifying the developer experience rather than just providing lots of sample code. WPF is still more like C/C++ and Win32 than C# and WinForms in terms of base complexity. New classes, etc. I suppose one should consider the "My" namepsace of VB and consider whether WPF could use some of those same simplifications.
  • Think Out of the Box - this is the hardest to describe, yet the most important I think as a platform for the future: Thinking outside of the box. If we rest on the standard data entry fields and techniques of the last 20 years, has WPF really changed the way people see computers? The more that Microsoft (and other companies of course) can show that WPF provides a platform that can provide truly innovative application experiences (from consumer to LOB) the better off the ISV industry will be. We're at a crossroads in technology. The next generation of Windows applications can take us where no one was expecting -- making computers a smarter part of our day rather than just being a tool no different than the phone that sits on your desk or a calculator (even some of the best office phones really are still quite stupid and require the user to perform far too many cumbersome and unintuitive tasks).  I'm excited to be part of this wave. But, we must ride it carefully or risk being eaten by the sharks! :)

All for now. Hope this helps.

How to know when your Animation completes and ...

The AnimationTimeline classes in the Windows Presentation Foundation such as DoubleAnimation and ColorAnimation all have a common event Completed. I often use completed to know when an animation I have started in code has finished. If there's only one animation which is often the case, I can easily map the animation Completed event to the animated UIElement logically. However, if I start multiple animations using BeginAnimation, the Completed event unfortunately does not provide any information as to the control that was animated. (Bummer!)

Instead, the Completed event's "sender" is the timeline's Clock. The second parameter to the event is EventArgs, which of course doesn't contain any useful data. So, how might you connect the Clock to the actual target UIElement? There are a couple of ways actually, and I'll present one of them here.

My example uses a DoubleAnimation. Here's the code before:

DoubleAnimation anim = new DoubleAnimation();
anim.Duration = new Duration(TimeSpan.FromSeconds(0.5));
anim.To = 0.1;
anim.FillBehavior = FillBehavior.Stop;
anim.Completed += new EventHandler(AnimationCompleted);

myControl.BeginAnimation(UIElement.OpacityProperty, anim);

Unfortunately, there's no way to tie the PointAnimation instance (anim) to the UIElement, myControl. So, in a typical simple object oriented solution, I created a new derived class, PointAnimationPlus:

DoubleAnimationPlus anim = new DoubleAnimationPlus();
anim.Duration = new Duration(TimeSpan.FromSeconds(0.5));
anim.To = 0.1;
anim.FillBehavior = FillBehavior.Stop;
anim.Completed += new EventHandler(AnimationCompleted);
anim.TargetElement = myControl;

myControl.BeginAnimation(UIElement.OpacityProperty, anim);

Here's my implementation of DoubleAnimationPlus:

internal class DoubleAnimationPlus : DoubleAnimation
{
private UIElement _target;

public UIElement TargetElement
{
get { return _target; }
set { _target = value; }
}

protected override Freezable CreateInstanceCore()
{
DoubleAnimationPlus p = new DoubleAnimationPlus();
p.TargetElement = this.TargetElement;
return p;
}
}

static void AnimationCompleted(object sender, EventArgs e)
{
Clock c = sender as Clock;
DoubleAnimationPlus anim = c.Timeline as DoubleAnimationPlus;

if (anim != null)
{
if (anim.TargetElement != null)
{
Console.WriteLine("DoubleAnimation TargetElement is set!");
}
}
}

As you can see, very simple. The only real trick is that you must override the method CreateInstanceCore. Since an animation is a Freezable object, when the animation completes, a Clone (or CloneCurrentValue) is made (which eventually creates a new instance of the DoubleAnimationPlus class). In my simple implementation, my code creates a new instance of the DoubleAnimationPlus class and then copies the reference to the TargetElement. If you try this code and neglect to override the CreateInstanceCore, you'll immediately note that TargetElement property isn't set in the Completed event -- that's because you didn't actually create an instance properly (and copy the new values).

That's all there is. The other method I considered was to use dependency properties -- but I wanted the explicitness of this technique for now.

September 21, 2006

WPF Layered Windows come at a cost ...

A Microsoft employee, Seema Ramchandani, posted the final word on layered windows in XP: available, but rendered in software rather than hardware. What does that mean? If you've used the recent (June+) WPF bits, you may have seen the new "Window.AllowsTransparency" property. That allows you to create windows that aren't the normal rounded rectangles. Any shape really is possible. The downside is that they won't be quite as fast as they'll be on Vista. Many applications use layered Windows today -- so it's not out of the question to use them still. Just consider whether or not they're really necessary for your application, especially if you're targeting XP and Vista Windows.

Windows UI Top Guideline Violations

A great start to a very helpful document for anyone creating a Windows application (although it has Vista in mind, most of it applies to Xp/2003 applications as well):

Top Guidelines Violations (MSDN)

It's not quite finished and points unfortunately to some uncompleted documents, but it's got a lot of great information.

September 18, 2006

Learn 10 programming languages and get a Job!

eWeek's 10 programming languages you should learn "right now." How many do you know? Any that aren't on the list? How many would you consider yourself an expert in? If you know 10 -- (seriously?) -- are you an expert really in more than 1 or 2 at the most?

Interesting list if you ignore the commentary inline with each language. The explanations for several of the languages is questionable:

6. C

"Why you should learn it: "Learning C is crucial. Once you learn C, making the jump to Java or C# is fairly easy, because a lot of the syntax is common. Also, a lot of C syntax is used in scripting languages," said Duquaine."

Having learned C, C# and some Java, learning C is not crucial. C++ would be a better starting point for Java or C#. And so little of C syntax is used in scripting languages. Curly braces, if statements, and the list doesn't get much longer than that (yes, there are others, but there really aren't that many). The concrete differences far outweigh the similarities.

The person quoted above, Wayne Duqaine also stated: "At last count, I knew/have learned over 24 different languages in over 30 years." Given his comparison of C to C# and Java, I'm not sure how well he really "knew/knows" any of those languages.

Another classic: "Learning C#, which is just Java with a different name plate, is critical if you heavily use Microsoft," said Duquaine.

Yeah. That's all it is Mr. Duquaine. That's like saying OSX is just Microsoft Windows with a different desktop background.

WPF 3D Not ready?

I was experimenting a bit with the WPF-3D features this weekend and noticed this oddity ... (click on thumbnails for larger view):

Monitor 1:

Monitor 2:

What's the difference between these two monitors? The video card. I've got 2 video cards (and 3 monitors) -- when I dragged the window to the second video card's display, the rendering of the image is trashed. Drag it back and everything is fine.

September 13, 2006

WPF Decorators - Build your own "Chrome!"

If you've taken a deep look at how some of the base controls in WPF are constructed, you'll likely have encountered a mysterious element/UIElement at some point, "Chrome." Chrome is a tad confusing when you first encounter it as it seems so magical. Many controls are rendered nearly entirely using these "Chrome" elements (like a Button for example).

One of the first "Chromes" I encountered was actually a Chrome designed for the standard WPF Buttons (ChromeButton). The actual implementation of the "Chrome" for a button may be found in the Microsoft.Windows.Themes namespace. The somewhat interesting part about the Chrome class is that depending on the current user interface style of Windows that is running, a different and unique Chrome assembly is loaded. For example, there's a Chrome library for the Luna (blue XP) theme named PresentationFramework.Luna. There's another for the Vista Aero style named PresentationFramework.Aero.

On my system, the standard WPF button using the Silver XP theme looks like this:

Plain and simple. The button though has some annoying features that make it hard to style any further.

Let's imagine that instead of the standard Silver button, I'd like to make it Green to represent an OK button:

Not too bad so far. The button is green. What about when I actually try to use it ...

Interesting. When I move my mouse over the Button, it reverts back to the original colors. Bummer. What about if I were to click the button ...

Still no green! To say the least, that's a bit annoying. Why Microsoft made the button work this way, I can't answer. I can however provide you with some alternatives.

One solution is to roll your own control template, use the right combination of Borders, rectangles, etc. The sky's the limit. So is, unfortunately, the number of UIElements more than likely. In the first version of WPF that is to be released, UIElements represent an expensive and limited resource in the system, worse in some ways than the old GDI limits, but different in others. Still, it doesn't hurt to conserve, so Microsoft provided another way -- a way to custom render a button like this in a nice reusable pattern. That's exactly what the "Chrome" objects represent in WPF. A fast and lightweight method for rendering a potentially complicated visual. Be warned though that this isn't something that a non-developer would normally do. It's relatively straightforward but certainly outside of the typical non-coder realm. The great thing though is that once developed, it's easy to reference the control either using Expression Interactive Designer or Visual Studio 2005 and use it without understanding how it was built!

My experience has been that it can be EXTREMELY challenging to make some types of "looks" using strictly the UI elements and associated geometries that exist in WPF. Sometimes it seems impossible.

I had a need for a better button. One that would allow me to set different colors AND respect the color in the various typical states (although I have ignored disabled for right now as I had no need. Easy to add though...). The standard button also doesn't provide for a method for changing the border thickness, so I've also provided that functionality.

Here's are the basic steps to creating the Decorator and using it ... (see the attached C# solution with the SmartBorder control and a simple test project that reproduces the screen shot below).

I created a new class library assembly and referenced the PresentationCore/Framework assemblies.

Next, I added a class to the project, deriving from the Decorator base class:

public class SmartBorder : Decorator

To the SmartBorder class, I added a series of DependencyProperty instances for the various necessary properties. For example, here's the CornerRadiusProperty.

public static readonly DependencyProperty CornerRadiusProperty =

DependencyProperty.Register("CornerRadius", typeof(double), typeof(SmartBorder), new FrameworkPropertyMetadata(8.0, FrameworkPropertyMetadataOptions.AffectsRender));

There are two very important steps that must be done to make the new decorator useful: 1) Draw the control, and 2) Measure the control.

The first is accomplished by overriding the OnRender method of the Decorator. Here you're given free access to the DrawingContext (think of this as the place where you draw the series of commands that make up your user interface look). In my decorator, I've used some basic things like the ability to draw a rounded rectangle (see the source code for the other commands I used):

dc.DrawRoundedRectangle(backBrush, borderPen, rc, cornerRadiusCache, cornerRadiusCache);

dc.DrawRoundedRectangle(gradientOverlay, borderPen, rc, cornerRadiusCache, cornerRadiusCache);

Additionally, I wanted to do some drawing which might have occasionally drawn outside of the normal decorator bounds. I didn't want that to happen so I used the clip functionality:

dc.PushClip(new RectangleGeometry(rc, cornerRadiusCache, cornerRadiusCache));

This (and the corresponding dc.Pop) will Clip any drawing commands to the specified shape or rectangle. In this case, I specified a rounded rectangle which mirrors the shape of the button based on the corner radius).

In the override of MeasureOverride, it's the responsibility of the control to respond with the overall desired size of the control. My code simply takes into account the size of the border and adjusts based on the child and returns the overal size. Nothing too complicated.

Additionally, in order to actually use the new SmartBorder, I created a new Control Template:

<ControlTemplate TargetType="{x:Type Button}" x:Key="ShinyButtonTemplate">
<WPD:SmartBorder RenderIsPressed="{TemplateBinding IsPressed}" Background="{TemplateBinding Background}" RenderIsMouseOver="{TemplateBinding IsMouseOver}"
CornerRadius="4"
OuterGlowBrush="{DynamicResource OuterGlowBrush}">
<ContentPresenter
x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
Margin="{TemplateBinding Padding}"
/>
</WPD:SmartBorder>
</ControlTemplate>

The SmartBorder is part of the WPD namespace which was declared in the root element of the host:

xmlns:WPD="clr-namespace:WiredPrairie.Decorators;assembly=WiredPrairie.Decorators"

Since my ControlTemplate specifies that all buttons should use the specified control template, it's easy to see the results (actually, only those buttons that don't override the default by using a new style or control template combination):

<Button Background="Aqua">
<StackPanel HorizontalAlignment="Left">
<Label>Big Label</Label>
<Label Style="{DynamicResource ShinyButtonMiniLabelStyle}">Small Label</Label>
</StackPanel>
</Button>

If you have any questions please e-mail. The source code for the sample project and the smart border control is attached (tested only with RC1 of .NET 3.0). If you happen to use the code somewhere / in something, I'd love to know!

WPSmartBorder.zip (41 KB)

If you use VB.NET, just compile the solution and use the WiredPrairie.Decorators.dll in your VB projects by referencing it.

Enjoy.

September 3, 2006

Progress on my simple WPF Media Center...

Progress on my simple WPF Media Center clone interface:

You need to upgrade your Flash Player

The animation is pretty close as the list wraps around (an infinite list). The button stays centered yet the text moves. The text fades off on the top and bottom now as well in this new version (which is done a bit differently than the previous screen shots).

I of course used several Storyboard's. The only real trick that I needed was to override the FillBehavior default -- rather than Hold, I needed it to "Stop".

<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="Height" FillBehavior="Stop">

<SplineDoubleKeyFrame KeySpline="0.5,0.5,0.5,0.5" KeyTime="00:00:0.25" Value="0.0"/>

</DoubleAnimationUsingKeyFrames>

In this code, I wanted the animation to finish, and then restore the Height back to the original value for the target element (which was set in code rather than being set in XAML). If you don't do this and rely on the default animation behavior, you'll find that the value cannot be set in code. So, in this example, if I try to set the Height to something else like this: myControl.SetValue(Control.HeightProperty, 50.0), the value won't take as the animation is "holding" it at the last value specified in the animation. Sometimes that's what you'd want, but not always. Thankfully, there's an out by using "Stop".

I'm not aware of anyway to simulate the Media Center 2005 user interface entirely from XAML faithfully. In my code, which wasn't very long, I did need to hook several events of the Storyboard to know when the animation was complete:

Storyboard sb = FindResource("HeightToZero") as Storyboard;

if (sb != null)

{

sb.Completed += new EventHandler(HeightToZeroCompleted);

}

September 1, 2006

WPF Media Center 2005 Fake-Out

Some experimenting this evening with WPF:

Done completely in WPF without any code. It doesn't yet have the cool sliding animation that the Microsoft Media Center uses, but it's nice looking nonetheless! The active option glows much like the Media Center and the edges of the list fade into nothing-ness just like the real thing. I just eye-balled the original so the colors aren't exact matches by any means (I wasn't going for exact match, just the spirit of the MediaCenter).

To do the faded list of buttons, for the purposes of this demo, I used an opacity mask like this:

<ScrollViewer Opacity="1" Margin="165,92,191,104" x:Name="ScrollViewer" VerticalScrollBarVisibility="Hidden">
<ScrollViewer.OpacityMask>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<LinearGradientBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform X="-0.5" Y="-0.5"/>
<ScaleTransform ScaleX="1.0141371299213975" ScaleY="1.0141371299213975"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="-89.602136078970744"/>
<TranslateTransform X="0.5" Y="0.5"/>
<TranslateTransform X="-0.010563229438366996" Y="0.0035211220837406574"/>
</TransformGroup>
</LinearGradientBrush.RelativeTransform>
<GradientStop Offset="0.48717948717948717" Color="#FF000000"/>
<GradientStop Offset="1" Color="sc#0, 1, 1, 1"/>
<GradientStop Offset="0" Color="sc#0, 1, 1, 1"/>
<GradientStop Offset="0.85897435897435892" Color="#FF000000"/>
<GradientStop Offset="0.14102564102564102" Color="#FF000000"/>
</LinearGradientBrush>
</ScrollViewer.OpacityMask>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Center" Width="Auto" Height="Auto" x:Name="StackPanel" ClipToBounds="True" CanVerticallyScroll="True" Margin="0,0,0,0">
<Button Height="38.106666666666" x:Name="Button_Copy3" Content="Expression"/>
<Button Height="38.106666666666" x:Name="Button_Copy4" Content="XAML"/>
<Button Height="38.106666666666" x:Name="Button_Copy5" Content="VB.NET"/>
<Button Height="38.106666666666" x:Name="Button_Copy6" Content="Microsoft"/>
<Button Height="38.106666666666" x:Name="Button" Content=".NET" Width="284"/>
<Button Height="38.106666666666" x:Name="Button_Copy" Content="C#"/>
<Button Height="38.106666666666" x:Name="Button_Copy1" Content="Web"/>
<Button Height="38.106666666666" x:Name="Button_Copy2" Content="2D/3D"/>
<Button Height="38.106666666666" x:Name="Button_Copy7" Content="Button"/>
</StackPanel>
</ScrollViewer>

August 13, 2006

Mono, Miguel de Icaza interview

I so rarely watch more than a couple of minutes of any on line interviews, but I found this interview with Miguel de Icaza of Novell interesting to watch (from Port 25, the open source software lab @ Microsoft). The discussion centers primarily around Mono and its implementation and testing.

Very near the end, Miguel mentions WPF/Avalon somewhat negatively and compares it to J2EE (as a big beast, specifically potentially the "Waterloo" of Microsoft). Specifically, he agrees that the surface area of WPF is too large and it's much too hard to learn (earlier).

August 8, 2006

Turbo Pascal is back!

Turbo Pascal is back! Not quite -- Turbo Delphi, Turbo C++ and Turbo C# however are for real. Borland announced that they would be soon releasing "Turbo" branded developer products aimed at two different markets: the hobbyist and the professional. There will be an "Explorer" edition of each product which is free and a "Professional" edition priced under $500.

Considering Borland is trying to sell their development business, I'm not quite sure how giving away copies is going to make them any additional revenue. No matter, I'll be happy to download a copy and check them out when they're available.

August 6, 2006

WPF: Too long, too late?

I couldn't agree more.

June 28, 2006

Adobe Flex 2 Released

Adobe announced the final release and availability of Flex 2 today.

There's a new web site, flex.org, a new blog from the team, a final release of Flex Builder, the Eclipse based IDE ($499 US), and of course the free SDK available for download.

A new version of the Adobe Flash player is needed to use Flex content, version 9 is available here. (They're bundling the Yahoo toolbar in with the download by default, so uncheck that if you don't want it -- Adobe! Make it an OPT-IN, not OPT-OUT installation).

I'd heard rumors about the IDE being around $1000 US, so it's great that they priced it more reasonable. I'm sure it's a competitive price to other IDEs, but with hobbyist interests in Flex, I'd like a $49 edition ... (or free like Microsoft has done with Visual Studio Express editions).

June 21, 2006

Disappearing WPF blog

Short but sweet. The ATC Avalon Team blog with it's first post on May 25 2006, just posted today that they wouldn't be posting anymore. It was fun while it lasted. Too bad.

June 20, 2006

Rapid one day (24 hour) development ...

In the spirit of the Yahoo event that I just posted recently ...

RailsDay at WeBreakStuff. It's a nice testament to the ability of a sharp fast 'agile' team and the ability to produce good stuff in a short amount of time. Of course, the reality of not having an installed set of customers almost always makes development easier (for so many reasons).

June 15, 2006

Yahoo Hack Day

A neat idea. Other companies should do something like this -- anyone know of any?

Yahoo is giving their engineers a day (24 hours) to build something they think is cool and demo it. (I'm not sure if non-engineers get to participate?).

Some more specific information here.

June 11, 2006

WPF Posts According to Technorati

I've not explored Technorati much before and so I was mildly interested in the fact that they'll permit use of their charts on other sites. So, for example, here are the number of posts which mention WPF in the last 30 days:

Posts that contain WPF per day for the last 30 days.
Technorati Chart
Get your own chart!

Might be useful to put on your own web site to track topics you're interest in. Or then again, maybe not.

WinFX becomes .NET 3.0

Announcing the new name for Avalon, I mean WinFX, .NET 3.0.

Not that I'm too surprised. Microsoft has made some awful product naming decisions. Most recently bone-headed naming by marketing and legal:

Sparkle becomes Microsoft Expression Interactive Designer. Huh? Rolls off the tounge, doesn't it?

Here's my humorous speculation as what Microsoft would do if they released a pro-photographer software application.

Compare/contrast this first though to Apple's Aperature, and Adobe's still in beta Lightroom.

June 12, 2006. Microsoft is proud to announce the beta release of dLens, a new professional and amateur photography application, aimed to ....

Six months pass while excitement builds, web sites are excited, forums created, etc. and a new press release is distributed:

Dec 15, 2006. Microsoft announces the release of a new generation of professional and amateur photography applications available in two versions: The Microsoft Expression Still Photo Management and Editing Tool, Professional Edition, or MESPMETPE for short, and the Microsoft Expression Still Photo Management and Some Editing Tool Functionality, Basic Edition, or MESPMSETFBE ....

In any case, back to my lack of surprise. The WinFX downloads always were versioned 3.0 and some beta number (most recently 2). So, they've headed off the typical criticism of releasing a 1.0 product and skipped ahead right to 3.0! Excellent!

I can see the download options now for .NET 3.0:

.NET 3.0, for computers running Microsoft Windows XP and Windows 2003 Server. Includes the .NET framework 2.0, WPF, WWF, and all other meaningless product names we could think of. If it hasn't been branded .NET, you don't need it.

.NET 3.0 for computers running Microsoft Windows 2000. Most of the above, but we had to leave out some of the acryonymed products and functionality as we didn't get around to making them run on Windows 2000. Sucks huh?

.NET 3.0 for computer running really old unsupported operating systems. Includes an installer that suggests you upgrade to a newer version of Windows. It would have been terribly slow even if it had installed and run anyway!

Scoble leaves Microsoft ... and I don't care.

I see that Robert Scoble is leaving Microsoft after 3 short years. Here are some corrections to the announcement.

My reaction: Yawn.

He asked if people will unsubscribe. You betcha. I will.

IMHO, he wrote too much for me to keep up with to begin with (high noise level) and the only reason I had subscribed in the first place was Microsoft news.

I personally don't have time for podcasting, nor am I interested in it. I can get tons more information delivered and processed much more quickly by reading and scanning through blog posts, news articles, etc. I can't do that by listening to someone for 45 minutes about who knows what.

Although he certainly was a 'blog-champion' at Microsoft, the real stars are the day-to-day 'do-ers' at Microsoft, the programmers, the program managers, the designers, etc. that jumped into blogging (and continue to blog) day to day about their products, etc. In the end, the evangelists job is to tell you something you don't already know at a reasonbly high level. Once they've done their job, it's up to the individuals to keep you hooked. I challenge each and everyone of them to step up and not forget that. Additionally, I challenge their managers to allow them to have some dedicated time every week to blog (or communicate -- I don't care what forum they use) about their products, roles, etc.

Of course, this is directed to not just inside of Microsoft, but in any company (technology focused or not!).

I personally care more about the a product when I can see and interact with the actual maker/designer/etc: personal rather than anonymous.

June 7, 2006

Flash outside of the browser ...

I can't believe that it has taken Macromedia, I mean Adobe, so long to announce this product. It should have been released by now. In any case, I missed seeing this news last month: Flash to jump beyond the browser (CNET). A code-named project, Apollo, will let applications written for the Flash platform run without a browser. According to reports though, it won't be available until early 2007.

In doing a bit more research, this wasn't news. The announcement was made last year at Macromedia's MAX 2005 conference. It was referred to as the "Universal Client."

Looking at the feature list shown on the screen shot of the slide from the original MAX 2005 announcement, it looks like WPF/.NET to me.

Apollo/Flash, however, has hundreds of thousands of more people who know that platform today than WPF/XAML.

I'd definitely be interested in checking it out--I'm always interested in looking at the possibilities of non-Microsoft solutions. (Although the fact that they mention it along with the PDF / Acrobat platform is a bit worrisome from a stability standpoint -- Adobe releases far too many patches/updates to their reader software!).

May 26, 2006

Cingular exceptions...

I smirked a bit when reading this post from Joel about the Cingular web site this evening. It reminds me of my experiences with Cingular's web site recently.

May 25, 2006

We need another image file format...?

According to Microsoft, it's about time we replace JPEGs with the new Windows Media Photo format.

More details on ZDNet.

Somehow, I'm sure Microsoft hopes to make some money on this new format (licensing?). If they weren't, they wouldn't have given it such a dumb name.

Microsoft still doesn't understand how to play nice in the industry; if it were the "Open Media Photo" format and it was free of licensing, it might be adopted much more quickly. I guess we'll have to wait and see.

May 23, 2006

WPF Visual Brush with no code!

A little fun with the WPF VisualBrush element tonight:

In previous builds of WPF, it was always necessary to set the VisualBrush's Visual property in code. That's no longer necessary as this demo shows.

<VisualBrush x:Name="myVisualBrush" ViewboxUnits="Absolute"
Stretch="None" AlignmentX="Left" AlignmentY="Top"
Opacity="0.50" Visual="{Binding ElementName=BallArea}">

The complete code is here: Code

You should be able to paste this into a working March 2006 CTP build of WPF (XamlPad or XamlCruncher). Click the ball to see the dumb animation I added.

May 16, 2006

Google Web Toolkit Released to little fanfair ...

Google announced the "Web Toolkit (Beta)" today. I'm sure there was much excitement across the blog universe today.

Yawn. Being more of a .NET guy myself, I'm even less excited about the actual details:

Google Web Toolkit (GWT) is a Java development framework that lets you escape the matrix of technologies that make writing AJAX applications so difficult and error prone. With GWT, you can develop and debug AJAX applications in the Java language using the Java development tools of your choice. When you deploy your application to production, the GWT compiler to translates your Java application to browser-compliant JavaScript and HTML.

There's a new blog covering the details here.

No Mac OSX support.

(I still like Yahoo's web library right now).

May 15, 2006

IValueConverter and WPF

It's likely at some point you'll encounter a situation where you want to do some data binding in WPF. However, the data you're binding to may not be in the format you actually want to display to the end user. WPF has a relatively simple solution to this problem, value converters.

In the following example, I wanted to have a slider that as I moved the slider would increase the date on the image displayed. The slider's native data type is a double. I wanted to display the date as Day of the Week, Date.

As I use the slider, I'd like it to look more like this:

The date should update immediately as the slider value changes.

There's no simple binding that does everything. There might be a completely XAML based solution for this, but obscure XAML isn't the best for long term maintenance, no matter how cool it was that it could be done all in XAML. So, instead I present the value converter.

Here's the label I'm using:

<Label VerticalAlignment="Top" HorizontalAlignment="Center"

FontFamily="Tahoma"

Foreground="White"

Content="{Binding Value, ElementName=DateSlider, Mode=OneWay, Converter={StaticResource SillyDateConvert}}"

FontSize="10pt"

FontWeight="Bold"

SnapsToDevicePixels="True" >

<Label.BitmapEffect>

<OuterGlowBitmapEffect GlowColor="Black" GlowSize="2" Opacity="1" />

</Label.BitmapEffect>

</Label>

The important attribute to pay particular attention to is the Content attribute of the label. Specifically, the Converter of the Binding. Converter={StaticResource SillyDateConvert} refers to a resource I declared in the Resources:

<wp:SillyDateConverter x:Key="SillyDateConvert" />

The namespace, wp, is declared in the root element as

xmlns:wp="clr-namespace:ShowValueConverter"

In a separate code file, I've created a class, called the SillyDateConverter (C# here):

[ValueConversion(typeof(double), typeof(String))]

public class SillyDateConverter : IValueConverter

{

public object Convert(object value, Type targetType,

object parameter,

System.Globalization.CultureInfo culture)

{

DateTime dt = DateTime.Now;

dt = dt.AddDays( (double) value);

return dt.ToString("D");

}

public object ConvertBack(object value,

Type targetType,

object parameter,

System.Globalization.CultureInfo culture)

{

throw new Exception("The method or operation is not implemented.");

}

}

Since my code only does one way binding, I've left the default exception in the ConvertBack method. In the Convert method, I use the bound value from the slider (passed as value), to construct and then return the formatted date.

As a final example, I added another converter to round a double. For some reason, the slider doesn't quite operate the way I'd expect. Even though small change is set to 1, it allows decimals to creep in if I use the mouse.

Here is the sample project (zip): ShowValueConverter.zip (21 KB)

Built and tested with the Feb/March 2006 CTP of WPF.

May 7, 2006

WPF Binding to an Element and Rotating

A simple example of how to bind an element's property.

XAML below:

The real trick is right here:

<RotateTransform Angle="{Binding ElementName=Slider, Path=Value}"/>

This says, binding the Angle property to the Value property of an Element named Slider somewhere in the current context of the Grid. As the Value changes, so does the Angle, automatically. Slick and easy.

<Grid

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

Background="#FFFFFFFF"

x:Name="DocumentRoot"

x:Class="UntitledProject1.Scene1"

Width="640" Height="480">

<Rectangle Stroke="sc#1, 0.623615, 0.623615, 0.623615" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="187,103,177,166" Width="Auto" Height="Auto" x:Name="Rectangle" RenderTransformOrigin="0.5,0.5" RadiusX="10" RadiusY="10">

<Rectangle.RenderTransform>

<TransformGroup>

<TranslateTransform X="0" Y="0"/>

<ScaleTransform ScaleX="1" ScaleY="1"/>

<SkewTransform AngleX="0" AngleY="0"/>

<RotateTransform Angle="{Binding ElementName=Slider, Path=Value}"/>

<TranslateTransform X="0" Y="0"/>

<TranslateTransform X="0" Y="0"/>

</TransformGroup>

</Rectangle.RenderTransform>

<Rectangle.Fill>

<SolidColorBrush Color="sc#1, 0.487050742, 0.4300494, 0.9016877"/>

</Rectangle.Fill>

</Rectangle>

<Slider HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="14.8823593128807,0,0,150.402281758685" Width="179" Height="46.9601551083915" x:Name="Slider" RenderTransformOrigin="0.5,0.5" Maximum="360" SmallChange="1" TickPlacement="Both" LargeChange="5" TickFrequency="45">

<Slider.RenderTransform>

<TransformGroup>

<TranslateTransform X="0" Y="0"/>

<ScaleTransform ScaleX="1" ScaleY="1"/>

<SkewTransform AngleX="0" AngleY="0"/>

<RotateTransform Angle="45"/>

<TranslateTransform X="0" Y="0"/>

<TranslateTransform X="0" Y="0"/>

</TransformGroup>

</Slider.RenderTransform>

</Slider>

<Label HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="17,0,0,119" Width="67" Height="24" x:Name="RotationLabel" RenderTransformOrigin="0.5,0.5" Content="{Binding Value, ElementName=Slider, Mode=OneWay}"/>

<Label FontWeight="Bold" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="17.0000000000001,0,0,134.723333333333" x:Name="Label" RenderTransformOrigin="0.5,0.5" Content="Rotation" />

</Grid>

Consolas, a new font for Visual Studio 2005

My last font change in a code editor was to ProggyCleanTT. However, Microsoft recently made available a new font, Consolas, available for licensed users of Visual Studio 2005 (found on "Only Passionate People Win"). Consolas is a significant improvement over "Courier New" and is better than Proggy Clean. Here are some examples:

Courier New (10 pt):

Proggy Clean (12 pt):

Consolas (10 pt):

Here are some more examples, this time real code (C++) (thumbnailed each one so you can open them in a new browser tab for comparison):

Courier:

Proggy Clean:

Consolas:

If you compare Consolas and Proggy Clean, you'll see how the kerning is better in Consolas which helps readability. It's not a major improvement from Proggy Clean, but if you're a licensed user of Visual Studio, and you're tired of Courier, download Consolas setup (4.3 MB) and give it a whirl. It automatically changes the Visual Studio editor font during the installation. It's free!

Announcing HTMLGrabber v2

I've updated my HTMLGrabber this weekend with several new features:

  1. Now can recursively follow nested framesets.
  2. Syntax highlighting (it's not perfect, but it does reasonably well in most situations)
  3. CTRL+A now works when the edit field has focus!

Before capture:

Capturing ...

After grab:

Download it from here:

WiredPrairieHTMLGrabber.zip (65 KB)

Or here.

Just unzip and run the executable. No install needed. If you encounter a web page that doesn't work please leave me a message with the details.

April 24, 2006

Yahoo Farechase

Yahoo's FARECHASE is one of the slickest airline travel search tools I've used. It's heavily Web 2.0 -- AJAX, etc -- and very well done.

It's got the autocomplete/suggest behavior ..

Nice calendar support:

And slick, live results searching (no more "waiting" screens like other sites):

It's slick how the search results can be modified live without page refresh:

I have selected one of the flights (which for some reason pops up a new window rather than being in place):

Rather than doing the booking at Yahoo, you're taken to the airline's web site for final booking (or to another 3rd party travel site):

I know where I'll search for flights in the future. It's not Expedia or Orbitz anymore.

April 8, 2006

Glowing Text in WPF

WPF has some simple features which really can make some effects easy. For example, if you'd like to add a shadow or a glow to a TextBlock in WPF (or other elements, such as a Button), you need to only add a BitmapEffect to the proper UI element. For example, in the code below, I've applied it to a TextBlock element.

Here's the XAML:

<Page
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid Background="#ff2d76ff">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="48"
Foreground="Gold"
FontFamily="Courier New"
FontWeight="500"
Typography.StandardLigatures="True">
<TextBlock.BitmapEffect>
<OuterGlowBitmapEffect GlowColor="Black" GlowSize="3"/>
</TextBlock.BitmapEffect>
<Span>Wired</Span><Span
FontFamily="Verdana"
FontSize="40"
Foreground="Orange"
FontStyle="Italic">Prairie</Span>
</TextBlock>
</Grid>
</Page>

April 4, 2006

400+ differences: Visual Studio 2005

400+ differences of Visual Studio 2005.

"The difference is obvious" claims the web site. I'm not sure what they are comparing to actually -- other than something else. I clicked through a couple to see what they thought was different.

Difference #1: .NET Remoting

Excuse me? The first difference is .NET Remoting? A technology they will be deprecating in favor of the Windows Communication Foundation?

Some of the differences have a Learn More button that takes you to a new page with a more detailed description. Things like .NET Remoting, oddly do not though.

The only reason I'd suggest visiting the web site would be the to view the series of brief videos, "Life as a Developer." I chuckled at 6, 109, and 256.

I don't know why they chose to list 400+ things in a user interface like this. Who is going to click through 400 things?

March 20, 2006

Atlas -- available for use on your web site!

Atlas, the AJAX-style framework running on ASP.NET 2.0 from Microsoft, is now available for download -- more importantly (for a lot of developers), with a go-live license! Oddly, I can't seem to find a specific link proving that although a number of "Atlas" blogs make that claim (here and here).

March 10, 2006

Ruby, .NET and James Gosling

I'm just starting to learn Ruby -- slowly. First step -- get it installed in a virtual machine running Linux and on my Mac. Next step, start reading.

During my hunt for information, I came across this news article about James Gosling today via Loud Thinking. Go to Loud Thinking for the Ruby part, but here's a fun quote about .NET from James:

The Father of Java then dismissed Microsoft's C# as having had potential, but no longer: "We were afraid they were going to do something really creative - but they're hopelessly focused on one platform."

Eh? James, dude, you need to take a vacation. Microsoft actually doesn't need to work on other platforms, the Mono project addresses the cross platform "issue" quite nicely for .NET.

How's Sun's stock doing James? That cross-platform language working out real well for Sun financially? In the end, like most businesses, they're out to make money, just like Microsoft, focused hopelessly on their evil platform.

February 26, 2006

Yahoo's Design Pattern Library

I feel like I'm all about "Yahoo" tonight. Considering I don't use their services much (not sure why honestly), I'm excited to see how more open they are becoming. One recent addition is their new design library. Essentially it's what their developers use for their various web properties to bring consistency across the Yahoo brand. Although not yet finished, it's great that a big company like Yahoo is opening up to show how they do software design. I particularly like how each design pattern is short, simple, with brief text and examples as needed and available. Check them out if you're interested in usability.

Yahoo's Browser support

It's interesting to see the web browsers Yahoo considers important in the marketplace and how supportable each is. IE 7 is listed as "A-grade" meaning must-support.

February 23, 2006

WPF and Microsoft Expression Interaction Designer blog

Another new blog from Unni Ravindranathan for developers interested in WPF and the Microsoft Expression Interaction Designer. Lots of interesting demonstration projects. Recommended.

February 16, 2006

Charles Petzold's WPF book blog

If you're interested in XAML / WPF, you might want to follow Charles Petzold's book blog here. From the stuff he's putting in the blog, I'm excited to get my hands on the book. In progress TOC is here.

A while back, he wrote me ... and I guess I was helpful.

February 8, 2006

Buggy RAM stealing software

Why should an average consumer put up with this? I'm running some common applications on my machine.

  • Adobe Photoshop CS (496MB)
  • Firefox (260 MB)
  • Harmony Remote control software (233 MB)
  • Visual Studio 2005 (157 MB)
  • Adobe Illustrator (70 MB)
  • Microsoft Outlook (53 MB)

Walking through the top RAM consumers on my PC, we start with Photoshop. I have 3 basic documents open, with very few entries in the history and only a handful of layers. I routinely find I need to shut down Photoshop to eliminate a general ever growing application footprint. Next up Firefox. I have 16 active tabs. Why do 16 separate web pages require over 256 MB of RAM? My guess is there's a set of nasty bugs leaking memory. It's the latest build. Next up, and the absolute worst offender overall is the HarmonyClient. This is the software that Logitech installs to manage the Harmony remote control I own. Most annoyingly, I don't have the remote plugged in! It's just sitting there, ever growing. (To add insult to that, the HarmonyClient software is a terrible piece of software from a usability perspective). Visual Studio is up next -- I think it too has a problem -- it seems to grow and grow and grow. It's not uncommon that I need to restart it to free some memory on my machine at work. Illustrator is suprisingly low actually given that I do have some open documents and have been working with it. Outlook at 53 MB doesn't shock me given how many messages I have open and the total size of my e-mail store.

The only reason I went to look is that Photoshop complained there was insufficent memory to complete an operation. That's unusual on my machine as I have 2 GB of RAM installed.

What's the average consumer to do? Demand better! Post bugs/complaints in forums. Don't settle for buggy software. You shouldn't have to. Take your money elsewhere. Open source clearly isn't any better than purchased software.

Developers: Assume that users will leave your software running far longer than you expect. So that little nagging memory leak that you know about can turn into a massive leak over the course of several weeks. I hardly ever shut my computer down -- when I'm not using it, I leave it in standby or hibernate (if the latter happens to work).

February 5, 2006

I'm experimenting with some SQL Server 2005...

I'm experimenting with some SQL Server 2005 Express edition features on a web page and hitting all sorts of issues. This article on MSDN helps explain some of the issues I'm having (especially around the User Instance=True connection string attribute). Unless I'm missing something simple (which is possible), this is an odd system -- and I hate the fact that I need to switch the True to False when I deploy my web application to a web server. The convenience of having a private local SQL Express database is lessened by the need to make configuration file changes to make it work properly.

January 31, 2006

A few interesting ASP.NET 2.0 links

Changes in code behind and deployment for ASP.NET 2.0

File upload improvements in ASP.NET 2.0

For compatibility with 1.1 ASP.NET projects, Microsoft released a preview of a new Visual Studio 2005 project model for web application which mirrors the functionality in Visual Studio 2003. With this, web projects include only files specifically listed in your project (not all files in the directory), and all code is compiled to a single assembly.

January 18, 2006

Embedding a combo box in a RichTextBox

UPDATE (January 2007):

As of the released version of WPF/.NET 3.0, this functionality no longer works at all if you want the control to actually be enabled -- and appears to be intentional/as expected. Several Microsoft employees have stated as much in forum posts.

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=389348&SiteID=1
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=716043&SiteID=1 (look for a post by Prajakta Joshi)

I'm disappointed to say the least that this wasn't included in the final V1 release, as it's something the company I work for would absolutely have used. I hope it returns in V2.


Someone wrote me yesterday asking how I'd gotten a combo box to be embedded successfully in a RichTextBox in WinFX. Since I last discussed this, several WinFX CTPs have come and gone, so I thought I'd post a slightly updated sample. The only real difference is that I changed the FlowDocument IsEnabled property to "True."

Oddly, even though this works on my system just fine, it doesn't work on his. Does it work on yours?

embed.xaml (1 KB)

xml version="1.0" encoding="utf-8"?>

<Grid

xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"

xmlns:c="http://schemas.microsoft.com/winfx/markup-compatibility/2005"

Background="#FFFFFFFF"

x:Name="DocumentRoot"

Width="640" Height="480">

<ColumnDefinition/>

<RowDefinition/>

<RichTextBox x:Name="myRichTextBox" Margin="88,98,210,126" Width="Auto" Height="Auto" RenderTransformOrigin="0.5,0.5">

<FlowDocument AllowDrop="True" IsEnabled="True" IsHyphenationEnabled="False">

<Paragraph>

<Run>This is so cool. A combo box floating in the text region of the rich text control.Run>

<InlineUIContainer>

<ComboBox

IsSynchronizedWithCurrentItem="True"

x:Name="embeddedCombo"

RenderTransformOrigin="0.5,0.5"

Width="150" Height="23" IsEnabled="True" />

InlineUIContainer>

Paragraph>

FlowDocument>

RichTextBox>

Grid>

January 9, 2006

Design templates for ASP.NET

Interested in making your web site look like a lot of other web sites? Here are some new templates provided for ASP.NET by Microsoft.

Seriously -- if you're planning on using one of these, try to adjsut some colors, etc., to personalize the template. Although these templates look nicer than typical templates, you'll still want to make them your own - unless you don't care that 'everyone' else has the same look and feel as you.

December 22, 2005

A new button style is coming (and it’s for Win32!)

IanG has all of the details here about a new button style for Win32 developers available in Vista, called the "command link."

And I thought I had gotten the memo that Win32 was dead.

#define BS_COMMANDLINK 0x0000000EL

and

#define BCM_SETNOTE (BCM_FIRST + 0x0009)

#define Button_SetNote(hwnd, psz) \

(BOOL)SNDMSG((hwnd), BCM_SETNOTE, 0, (LPARAM)(psz))

are key to using the new button style.

The bad part will be if it's only available for Vista and not XP or 2003 ...

December 6, 2005

Set Process Priority Utility

A small scriptable utility I threw together which given a process ID will set that process to a specified priority class, by default to "idle/low."

The utility runs only on Windows 2000, XP, 2003 and Vista.

SetPrio.zip (27 KB) or SetPrio.exe (60 KB)

To use the utility, make sure it is in your path and type SetPrio /P {ProcessID} where {ProcessID} is the process that you want to adjust.

Sets the priority of a process.

SetPrio /P processID [/L Priority]

/P The process ID (PID) to change.

/L Priority - 0 - 4

0 = Low [Default]

1 = Below Normal

2 = Normal

3 = Above Normal

4 = High

Example: SETPRIO /P 9170 /L 1

This utility normally is *not* successful with processes you did not create. For example, you cannot change the priority of an executable started by another user in a terminal server enviornment (and it does not matter that you are an administrator).

No guarantees or warrantees. It works well enough for what it was designed and intended to do. For the Windows programmer types, the utility is little more than a fancy wrapper for the API SetPriorityClass. I just needed it scriptable and to be able to run on an arbitrary process (one that I wanted to start at normal priority, but change it if necessary).

November 17, 2005

The continuing saga of the Avalon / WPF Combobox ...

In the continuing saga of how cool WPF/Avalon is ... I've gone ahead and styled the combo box inside the rich text control -- it's not really "stylin'" by any means ... as matter of fact, it's quite ugly. However, that's not the point that I'm trying to make. I'm thrilled that I CAN style the combo box if I need to, and that it's not too overly difficult. It's a bit clumsy, but it's mostly logical. Here's a couple of snippets from the XAML:

<ControlTemplate x:Key="FancyComboTemplate" TargetType="{x:Type ComboBox}">
<Grid>
<Border Padding="1,1,1,1"
Background="{TemplateBinding Panel.Background}"
BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}">
<Grid IsSharedSizeScope="True" VerticalAlignment="Bottom">
<ColumnDefinition Width="1"/>

....

<Popup Placement="Bottom"
Focusable="False" HasDropShadow="True"
IsOpen="{Binding IsDropDownOpen, BindsDirectlyToSource=False, RelativeSource=/TemplatedParent}"
PopupAnimation
="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" BorderThickness="1,1,1,1"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
<Border.Background>
<SolidColorBrush Color="#FFFFFFFF"/>

....

And here is the styled combo box. Notice the standard drop down has been replaced with the world's most boring button -- a solid color ellipse!

November 15, 2005

MSDN Ready To Launch 2005

I attended today one of the many Ready to Launch Events sponsored by Microsoft to drive awareness of the new Visual Studio 2005 and SQL Server 2005 features.

Oh my. I've been to boring courses, but this was DRY. And SLOW. And useless. If you've kept up with the never ending saga of "soon to be released" news and articles regarding Visual Studio and SQL Server at all within the past year or so, this should be a (limited) review.

Here's my analogy:

PDC 2005 in LA = Fast and the Furious
Ready To Launch (Local) = Driving Miss Daisy

The good: For attending and filling out a very brief survey, I received a full copy of Visual Studio 2005 Standard edition and a single user license of SQL Server 2005. The better: they didn't care when I left 2 and a half hours into the four hour session.

Bad: The guy presenting barely knew the material -- if anything was outside of the script he seemed to fall apart. As a representative of Microsoft trying to sell this stuff to us ... I expect someone knowledgeable. It didn't seem like he'd really used some of the stuff he demo'ed to us.

WPF / Avalon is so cool ...

I'm going to work on this ... but I wanted to do it in steps. Essentially a rich text box with a cool interactive, in-line, combo-box. First step is to make the basics work with a simple data binding.

Try doing this in Win32 programming!

<Grid.Resources>
	<ResourceDictionary>
		<XmlDataProvider x:Key="ListDS" 
		Source="Data.xml" d:IsDataSource="True"/>
	</ResourceDictionary>
</Grid.Resources>
<ColumnDefinition/>
<RowDefinition/>
<RichTextBox x:Name="myRichTextBox" Margin="88,98,210,126"
	Width="Auto" Height="Auto" 
	RenderTransformOrigin="0.5,0.5">
	<FlowDocument AllowDrop="True" IsEnabled="False" 
	IsHyphenationEnabled="False">
	<Paragraph><Run>This is so cool. A combo box 
		floating in the text region of the rich 
		text control.</Run>
	<InlineUIContainer>
	<ComboBox IsSynchronizedWithCurrentItem="True" 
	x:Name="embeddedCombo" 
	RenderTransformOrigin="0.5,0.5" 
	Width="150" Height="23" IsEnabled="True" 
	DataContext="{Binding Source={StaticResource ListDS}, XPath=/List/ListItem, Mode=Default}" 
	ItemsSource="{Binding Source={StaticResource ListDS}, XPath=/List/ListItem, Mode=Default}" 					
	SelectedValuePath="{Binding XPath=/List/ListItem, Mode=Default}" />
	</InlineUIContainer></Paragraph>
	</FlowDocument>
</RichTextBox>

November 14, 2005

Bags and a CRAZY web site ...

I was looking at a new camera bag for my digital SLR, a Nikon D70. I came across Crumpler bags. Even if you have zero interest in buying a bag of any sort (they sell more than just camera bags), you need to go to this site. It's the craziest consumer shopping site I think I've ever come across. Check out the big red button in the lower right corner of the web site. Use the chain when you're through! (And that's not all, there's all sorts of other oddities strewn about!).

November 13, 2005

Xbox 360 compatibility list

Microsoft has provided a complete listing of all current Xbox titles which will be playable on the Xbox 360. It's got at least 200 titles. It's got the ones I still play (what about you?). This might renew my interest in getting a 360. It does require a system with a hard drive. Supposedly many of the games actually will look better on the 360 as they can be upconverted to HD and also can take advantage of the new anti-aliasing support.

There are some screen shots at Bungie of the potential improvements in Halo 2. Halo 2 also can take advantage of 720p wide screens, which is especially nice for single-box multiplayer action. In the last screen shots, Bungie must have used some extremely low poly-count planets -- check them out in the background of the image.

November 11, 2005

Free Fonts for Developers

What font do you use in your code or text editor?

I'm tired of Courier New as my monospace editor font. Lucidia Console is a bit bold for my liking.

In the past, I've used ProFont. However, in Visual Studio 2005, it's acting very strangely, with some characters nearly overlapping in places. I've switched to another free font, Proggy Clean (at 12 pt). It's clean, easy to on the eyes, and has a unique zero.

Others have found Anonymous to their liking, but I couldn't see using it for normal coding. It probably would look better in print or on a image.

Anyone prefer using a non-monospaced font? If so, which one (and for what type of development)?

November 8, 2005

VS Express Editions for Free!

I'm sure this is making the rounds, but Microsoft, through Nov. 6, 2006 has priced the Visual Studio Express editions at a price that is hard to beat: FREE! Start at the Express edition web site for information.

It really is free too -- the products can be used even for commerical software development projects.

If you're looking to learn .NET, this is a great way to get started. Pick your favorite edition and download it today!

November 5, 2005

Josh Einstein’s TerminalTraceListener

A handy and free component from Josh Einstein which provides a remote access to a TraceListener via a Telnet session.

October 18, 2005

Bindows

Bindows - Very cool, but VERY expensive at 695 per developer. It’s a OOP DHTML framework for doing very rich web browser applications. Check out the demo on the home page for an example of what it can do.

The price of gas.

We’re looking to buy a new car. Given the rising prices of gasoline, I’m paying more attention the fuel economy of the various vehicles we’re considering. I wanted to have a reference table available for comparing the various MPG ratings and the potential cost of fuel for a certain amount of driven miles. So, rather than breaking out Excel (or even just calculator), I created a simple web page with the functionality I wanted. Provide a range of MPG, the cost of gas, and the miles driven, and bam!, out pops a handy table with row highlighting. Simple. No server side code was harmed in the making (AJAX free!). It works for miles or kilometers and gallons or liters … it doesn’t care (or is that kilometres and litres?). Give it a spin. If there’s a feature you’d like added, post a comment and I’ll see what I can do (no promises though).

Although I can’t and won’t guarantee the values are accurate nor fit for any particular use … I think they probably are and my testing seems to show they are . This was tested in Firefox, IE 6 and Opera.

Fuel Economy Cost Calculator

October 10, 2005

.NET 2.0 Dictionary Annoyance ...

Arrgh! I want to create an “observable” Dictionary class. Anytime an item is added, removed, etc., I want an event to be raised.

Using the System.Collections.Generic.Dictionary<TKey, TValue> class however is a no-go. Microsoft did not provide virtual methods for the dictionary modification methods (like Add). Dictionary happens to be one of those classes that isn’t a quick aggregation away from making a new version either, it has complex serialization, implements a bunch of interfaces, etc. Very annoying. I would have been less annoyed (in a way) had they done the same for the Collection<T> class. It however has all of the needed virtual methods to make it possible to observe. (OK, so that’s actually a positive thing, but it’s inconsistent).

I could use the C# new modifier, but then it doesn’t give me the true OOP behavior I need as there would be still methods of getting access to the dictionary without going through my methods by simple type-casting.

If I’m missing something, please help! Otherwise, score one less for the .NET 2.0 Framework.

September 26, 2005

Windows Presentation Foundation Book Update

If you have purchased or intend to purchase “Programming Windows Presentation Foundation” by Chris Sells and Ian Griffiths, be sure to note that there are a number of errata/changes already available for the book (here). The most recent changes bring the book content up to the functionality and behavior of the September CTP builds of WPF (which is what they made available at the PDC).

The book has been good so far, decent coverage of a lot of important topics. I was bothered by how many of the samples didn’t work (knowing that it was because the book was written to Beta 1), but it wasn’t immediately obvious what the right fixes were — so I’m happy that they continue to update the content (too bad my physical book can’t just be updated somehow!). More than just fixes though, they explain some of the features and controls that are available in the September CTP build (such as a tree and list view control).

 

 

September 23, 2005

XAML Fun

Some fun this morning with XAML.

XAMLFun1

If you want to experiement or try different combinations, here’s the XAML I used:

<Page xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" >
  <Grid>
 <Grid.Resources>
  <Style TargetType="{x:Type TextBlock}">
   <Setter Property="FontSize" Value="20" />
   <Setter Property="Foreground" Value="White" />
   <Setter Property="HorizontalAlignment" Value="Center" />
   <Setter Property="VerticalAlignment" Value="Center" />
   <Setter Property="FontWeight" Value="Bold" />
  </Style>
 </Grid.Resources>
 <Grid.ColumnDefinitions>
  <ColumnDefinition />
  <ColumnDefinition />
  <ColumnDefinition />
 </Grid.ColumnDefinitions>
 <Grid.RowDefinitions>
  <RowDefinition/>
  <RowDefinition/>
  <RowDefinition/>
  <RowDefinition/>   
  <RowDefinition/> 
 </Grid.RowDefinitions>
 
 <Rectangle Grid.ColumnSpan="3" Grid.RowSpan="4" Fill="Black"> 
 </Rectangle>

 <TextBlock Grid.Column="0" Grid.Row="0" Foreground="Red">
  BLUE
 </TextBlock>

 <TextBlock Grid.Column="1" Grid.Row="0" Foreground="Blue">
  GREEN
 </TextBlock>

 <TextBlock Grid.Column="2" Grid.Row="0" Foreground="Yellow">
  RED
 </TextBlock>

 <TextBlock Grid.Column="0" Grid.Row="1" Foreground="Green">
  BROWN
 </TextBlock>

 <TextBlock Grid.Column="1" Grid.Row="1" Foreground="Red">
  GRAY
 </TextBlock>

 <TextBlock Grid.Column="2" Grid.Row="1" Foreground="Purple">
  BLACK
 </TextBlock>

 <TextBlock Grid.Column="0" Grid.Row="2" Foreground="Red">
  WHITE
 </TextBlock>

 <TextBlock Grid.Column="1" Grid.Row="2" Foreground="Yellow">
  RED
 </TextBlock>

 <TextBlock Grid.Column="2" Grid.Row="2" Foreground="Green">
  TAN
 </TextBlock>

 <TextBlock Grid.Column="0" Grid.Row="3" Foreground="Blue">
  PURPLE
 </TextBlock>

 <TextBlock Grid.Column="1" Grid.Row="3" Foreground="Red">
  BLUE
 </TextBlock>

 <TextBlock Grid.Column="2" Grid.Row="3" Foreground="Blue">
  PINK
 </TextBlock>

 <Rectangle Grid.ColumnSpan="4" Grid.Column="0" Grid.Row="4" Fill="Black"> 
 </Rectangle>

 <TextBlock Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3">
  Try to say the names of the colors, not the words.
 </TextBlock>

  </Grid>
</Page>

September 18, 2005

Microsoft Internet Explorer Developer ToolBar

From Microsoft, currently in beta, is the Internet Explorer Developer Toolbar.

IEDevBar1

Several outstanding features: an on screen ruler (you can see it in the screen shot above) with some useful switches:

IEDevBar2

Also included is the DOM Explorer (more below about that), find an element by clicking feature, validation, cache and cooking clearing and much more!

In the first screen shot, I have the toolbar configured to show DIV element outlines along with the CLASS and ID information for each element.

IEDevBar3

There’s a very slick DOM Explorer toolbar also available:

IEDOMExplorer1

You can walk the DOM tree and explore the structure of the document and the styles that are applied to any given element. VERY handy. The toolbar also will “unpin” if you don’t want it attached to the browser window.

I’m not going to list all of the features here … that’s on the download page.

I’ve used the one built into Firefox and been happy with that … this seems to be at least a clone, if not better!

My hats off to the Microsoft developers for this great new IE toolbar.

Warning: According to reports, this will not currently work in Windows Vista as of Sept 2005.

 

September 12, 2005

Off to the PDC 2005

I’m off for the week to LA for the Microsoft Professional Developer’s 2005 conference. From the buzz that preceded this event, I hope to come away quite impressed and excited about Microsoft’s development platforms and strategies. Join in the opening session with bill gates here.

My plan is to post about the interesting stuff that happens. I’ve got my camera packed this time to capture the event more visually than in the past. I haven’t decided if I want to tote my laptop around during the day … it gets heavy ….

The ongoing question around the office has been why upgrade to Windows Vista? If this question isn’t answered this week, Microsoft will have missed the boat entirely (it needs to be at least partially answered with promises to finish answering very soon). I need to be able to convince my customers that Vista is the platform they need to be on in the next 3 to 5 years. Right now, our customers have a healthy dose of skepticism that I share. With Microsoft porting the Windows Presentation Framework (AKA Avalon) back to Windows XP and Windows 2003 Server, the clear “must have” upgrade path became quite hazy, almost muddy in fact.

I want to use the new technologies Microsoft is developing … but there are hundreds of millions of desktops and servers that need to be upgraded. Please Microsoft, help us communicate your direction with our customers. We cannot be expected to do this on our own. Your evangelists need to help us (like Tim Sneath, Robert Scoble and many others are but a few examples). Produce good REALISTIC upgrade scenarios (with costs for software and hardware carefully considered), with honest and truthful benefits (and never leave out the negatives, otherwise it’s just another lame sales pitch!). The better the material you produce, the more empowered we become to help sell your plans to our mutual customers.

If you don’t know why someone should buy a copy of Windows Vista today yet … I’m very worried (especially for our large enterprise customers). The right answer for large enterprise customers cannot be, “but look at that cool fade effect and animation there!”

July 27, 2004

ASP.NET vs PHP

It's been a while since I have looked at any PHP so I am unable to offer any lucid commentary regarding the ASP.NET vs. PHP debate (originally sponsored by Oracle and Sean Hull) currently taking place on Bertrand Le Roy's blog ("Some comments on Oracle's comparison of PHP and ASP.NET).

Bertand's counter point blog entry is nicely crafted to carefully discuss the various points made by Sean. Even though Bertand works for Microsoft his criticisms of the original article are definitely worth reading -- if for no other reason than to be shocked by some of the seemingly ludicrous comments made by Sean (a senior consultant at his firm!).

PHP and traditional ASP shine in one area primarily -- they're relatively easy to dabble in and still be rewarded (being interpreted and not compiled especially). I'm not sure if ASP.NET 1.1 is really for the dabbler web developer (or blog user). For a lot of people, they don't need a powerful object oriented development system (something PHP 5 seems to tout quite a lot) -- they just want something beyond a basic static web system.

June 9, 2004

.NET Image Resizing Console Application

I just finished creating a small .image manipulating NET console application that has several primary capabilities:

  • resize an image to a specified size (or percentage)
  • rotate an image 90 degrees counter-clockwise
  • reduce the quality

Nothing too revolutionary about the code. I'll mention a couple of the more interesting/useful pieces.

When saving an Image to a file via the Image.Save method, a codec is needed if you need to do anything custom. In my code, I needed to optionally reduce the quality of the image to a user specified value (the /Q:## option). For some reason Microsoft made retrieving a particular codec an effort left up to the developer rather than wrapping it in a handy static method.

private static ImageCodecInfo GetEncoder(string mimeType)
{
  ImageCodecInfo[] encoders = 
      ImageCodecInfo.GetImageDecoders();

for (int i = 0; i < encoders.Length; ++i) {
if (encoders[i].MimeType == mimeType) {
return encoders[i];
}
}
return null;
}


To retrieve the JPEG encoder:

ImageCodecInfo codec = GetEncoder("image/jpeg");

To set the quality parameter for the JPEG encoder:

EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] =
new EncoderParameter(Encoder.Quality,
(long) quality);

reducedImage.Save(destinationFile, codec, parameters);


To reduce the image to the user specified value (the /W:##, /H:##, or the /S:## options), the application takes the original image and draws it to a smaller image at the best quality (using the InteroplationMode):

using (Image reducedImage = 
new Bitmap(scaleWidth,
scaleHeight,
image.PixelFormat))
{
using (Graphics g = Graphics.FromImage(reducedImage)) {
g.InterpolationMode =
InterpolationMode.HighQualityBicubic ;
g.DrawImage(image, resizedRect);
}
... continues ...

The remainder of the code is house keeping, dealing with handling the various command line parameters and displaying help text. By default, the application creates the modified version of the image in a "Modified" subfolder (a subfolder of the original image's folder). To overwrite the image, use the /O option. The application displays the help text if no command line parameters are provided.


I haven't tested the application extensively. Use at your own risk.


The full code and executable is here: resize.zip.

May 5, 2004

Finding the Component Container

Ever wonder how to create a Component (a non-visual entity) which can automatically obtain a reference to the containing control when it is created? The ErrorProvider component does this. As soon as it is placed on a Windows Form (or more accurately the component tray), the ContainerControl property is set to the Form.

Although it's not hard for developers to manually select the containing control from the dropdown that would be provided, its simple to provide it automatically. Once you see how easy it is to add this support, you'll never go back to the days of manually setting the property again (or forgetting to set it!).

I'll walk you through the basics of creating the component. All of the source you need to make this work is here (in C#).

First, you'll need a component:

public class Demonstration : System.ComponentModel.Component
{ ...

Next, for convenience and to save some keystrokes, you'll want to add a couple of namespaces to your class file:

using System.ComponentModel.Design;
using System.Windows.Forms;

Add a private field which stores the ContainerControl reference:

private ContainerControl _containerControl = null;

Add a property so that users of the component can review or change the value:

public ContainerControl ContainerControl
{
  get { return _containerControl; }
  set { _containerControl = value; }
}

Finally, add an overridden implementation of the Site property:

public override ISite Site
{
  get { return base.Site; }
  set
  {
    base.Site = value;
    if (value == null)
    {
      return;
    }

IDesignerHost host = value.GetService(
typeof(IDesignerHost)) as IDesignerHost;
IComponent componentHost = host.RootComponent;
if (componentHost is ContainerControl)
{
ContainerControl = componentHost as ContainerControl;
}
}
}


So, what is this really doing? If you read in MSDN about this property and its interface type, there's a good chance you'll be scratching your head in confusion. The example in MSDN is of little help.


The containing Form (which has an IContainer in this instance) sets the Site property upon creation of the component. The ISite interface derives from IServiceProvider, which contains the method called above (GetService). None of the methods on ISite provide access to the containing control, so a different technique is needed.


Using the GetService method, an object (service) implementing the IDesignerHost interface is retrieved. Once this object is available, we're almost there! Using the IDesignerHost interface, the RootComponent is accessed and cast to an IComponent. The RootComponent is the WinForm for the active designer. If the root component is a ContainerControl, we cast it and store it away in our property.


Once you have compiled this new component (from a separate assembly to make using it within Visual Studio .NET more convenient) and added it to the toolbox, the component is ready for its first test. Place an instance onto a WinForm (or UserControl) (either drag and drop or double-click the component). Check the properties: the ContainerControl property is set to the host!


If you look at the other ISite interface methods, you may wonder why none of those were used. In this case, the values retrieved from those methods represent objects that are owned by the form, not the actual instance of the form (it has the values/interfaces, not "is a" value/interface).


That's it. Enjoy!