When in Rome

…take 200 pictures on your first day. Well, that’s what we did, but we won’t inflict them all on the world. We had to delete a bunch after my first photo-taking rampage anyway so that we’d have enough space on the SD card to actually take pictures for the next 3 weeks of our trip. :) The Vatican was just so amazing, it was hard for me to stop myself. Of course, the best part of the Vatican – the Sistine Chapel – did not allow photography, and it’s probably a good thing or else I probably would have used my whole GB worth of storage there.

Unifying the CD Collections

On a suggestion from the KEXP blog, Kenny and I recently invested in a few sets of DiscSox CD sleeves. He came home from band practice the other night to find me snapping open all of my old jewel cases and transferring the CDs + front and back inserts into sleeves. He joined me and helped me finish my collection, and then we got out his big CD Logic books and started transferring his CDs from those as well. The sleeves (and the storage tray) are wonderful because they allow for storage of the inserts in addition to the CDs, they save enormous amounts of space, they are easy to sift through quickly, and they won’t crack like our old jewel cases did.

And now both of our CD collections are integrated into one large alphabetized library. Pretty weird. I suppose it’s one of those things we needed to do before the wedding to really feel like we were forming a union. But there were a few coincidences that worked out nicely in the unification process, e.g. I own Pablo Honey, Amnesiac, Kid A, and Hail to the Thief; Kenny owns OK Computer and The Bends; between the two of us we have a pretty good Radiohead collection (oddly, OK Computer and The Bends are my two favorite Radiohead albums, so I don’t know why I’ve owned the others and not those two all this time). There were a couple of other artists for whom we now have the complete oeuvre with no duplicates, and other nice scenarios where we did not have overlap simply because one of us had lost a CD somewhere down the line.

It all seems to bode well for the marriage thing. And I know KW will never want to call it off because he’d lose my entire Beatles collection.

Launching the Browser from a Hyperlink

One question that has been coming up frequently over the past few days has been how to launch the browser when the user clicks on a hyperlink. The answer is different for the browser and standalone cases:

Browser (XBAP or Loose XAML)

XBAPs and loose XAML support special named targets for Hyperlink; these are the same named targets that are supported by HTML:

  • _self: the browser should load the document in the same frame as the element that refers to this target
  • _parent: the browser should load the document into the immediate parent frame of the current frame. This value is equivalent to _self if the current frame has no parent.
  • _blank: the browser should load the document in a new, unnamed window
  • _top: the browser should load the document into the full, original window (thus canceling all other frames). This value is equivalent to _self if the current frame has no parent.

The following XAML illustrates using a special target:

<TextBlock>
   <Hyperlink NavigateUri="http://microsoft.com" TargetName="_top">
      Navigate the top-level window to Microsoft.com
   </Hyperlink>
</TextBlock>

If you specify a target that does not exist, a new browser window will be created and the navigation will occur in that window. If you specify a target that already exists, the existing browser window will be used.

This functionality all relies on the named browser targeting feature that already exists in Internet Explorer. Note that the WPF navigation framework also supports named targets, so if you have a Frame in your Window named “_self,” “_top,” etc., those Frames will be searched first before delegating to the browser. This means that the hyperlink targeting logic will start by looking for an Avalon Frame inside the application with the specified name. If it does not find it, and the application is hosted in the browser, then it will call the browser’s navigate function with the specified target name.

I have a sample HTML page that hosts loose XAML in an iframe and demonstrates each of the special hyperlink targets. You can try it out here, and download both pages from here.

Standalone

Unfortunately WPF v1 does not have this convenient feature in the standalone case as well, since we can’t call into the browser in this case. For standalone scenarios, the simplest way to accomplish this task is to handle Hyperlink’s RequestNavigate event, and launch the default browser in your event handler.

The following XAML creates the Hyperlink and attaches an event handler:

<TextBlock>
   <Hyperlink RequestNavigate="HandleRequestNavigate" Name="hl" 
      NavigateUri="http://microsoft.com">
      Open Microsoft.com in the default browser
   </Hyperlink>
</TextBlock>

And the following code implements the event handler:

void HandleRequestNavigate(object sender, RoutedEventArgs e)
{
   string navigateUri = hl.NavigateUri.ToString();
   // if the URI somehow came from an untrusted source, make sure to
   // validate it before calling Process.Start(), e.g. check to see
   // the scheme is HTTP, etc.
   Process.Start(new ProcessStartInfo(navigateUri));
   e.Handled = true;
}

You can download the source for this sample here.

Creating a File Upload Control that Works in the Sandbox

In HTML, it is easy to add file upload capabilities to your application using the INPUT element with type=”file”:

<input type="file" />

This renders a text box and a button, and when a user clicks on the button she is confronted with a file open dialog. She selects a file, and the text box gets populated with file name:

The input element is usually used inside of a form, and the contents of the file are sent to the form processing agent when the form is submitted.

WPF does not provide a file upload control out of the box, but it is very easy to construct your own using a TextBox, a Button, and Microsoft.Win32.OpenFileDialog. I have created a simple version of this control by deriving from UserControl. My UserControl has one public property, called File, which is of type Stream, and an event, called FileSelected, which is raised when the user chooses a new file using the OpenFileDialog.

The main logic for this control lives in the Click event handler for the Button:

        void SelectFile(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            bool? dialogResult = ofd.ShowDialog();

            if (dialogResult == true)
            {
                TBFileName.Text = ofd.SafeFileName;
                file = ofd.OpenFile();

                // raise the FileSelected event:
                OnFileSelected(EventArgs.Empty);
            }
        }

In order to make this work inside the sandbox for an Internet Zone XBAP, I need to populate the TextBox using the SafeFileName property on OpenFileDialog, rather than using the FileName property. FileName will provide the full path to the file, and hence is not safe to use in partial trust (you will get a SecurityException if you try to use it from an Internet Zone XBAP). SafeFileName gives you a sanitized file name – simply the name of the file, without disclosing the path. This may be used safely by a partial trust application.

If you have the July CTP installed, you can try out a little test XBAP that uses this control here. You can download source for the control and the test app here.