Mad Props!

Omniscience is just a Google-search away.

Login

You're reading Mabsterama, the weblog of Matt Hamilton. Enjoy your stay.

Saving an Offline Copy of a SharePoint Document Library

We had a requirement at work this week which had a few people bamboozled: How can we “archive” a SharePoint document library to CD so that auditors could take it off site? It only needs to be a read-only copy, but it needs to include not only the documents themselves, but any “metadata” custom columns that the site owner has added to the library.

We tried a few different options, but nothing seemed to do what we wanted. A lot of people I asked suggested Groove, but after dicking around in Groove for a while, I couldn’t find how to access the “metadata” for any file that it had downloaded. In the end we settled on a fairly simple solution, which I will document here for future reference.

Note: I’m blurring out the data in these screenshots. Not because it’s overly sensitive, but mostly because I want to focus on the “how” rather than the what. Also I couldn’t be bothered constructing a “fake” document library just for the purpose of this blog post. ;-)

First, open the document library in a view that contains all the “metadata” columns you want to export and click Actions|Export to Spreadsheet:

Actions|Export to Spreadsheet

Your list will open in Excel, and the first column will be a hyperlink back to the original document on your SharePoint server:

The exported data in Excel

Add a new column (I like to make it the second column), and insert the formula into the first data cell:

=HYPERLINK(A2)

You’ll now have a second column of hyperlinks, but the links won’t go anywhere because they only link to a filename, rather than a full path:

image

Because there is no path on the hyperlinks in the second column, Excel will look for the linked file in the same folder as the spreadsheet itself. So now all you need to do is save that spreadsheet to a folder, and also save all the files out of the document library itself into that same folder! I like to call the spreadsheet “index.xlsx” so it’s clear that this is the index for all the documents.

Do you have an easier way to save an offline, archived copy of a document library? If so, I’d love to hear it! Leave me a comment!

Get Reactive with Rx in .NET 4.0

Over at Channel9 they’ve recently uploaded a couple of videos on a new feature of .NET 4.0 called the Reactive Framework:

Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx)

Kim Hamilton and Wes Dyer: Inside .NET Rx and IObservable/IObserver in the BCL (VS 2010)

What is the Reactive Framework? I’ll try to explain it briefly, but if you want to skip to the end of this post, I’ll add some links to other blogs that you should check out.

The .NET framework right now provides a pair of classes that can be used to iterate over a collection of objects: IEnumerable<T> and IEnumerator<T>. Enumerable objects provide the actual list, while enumerators do the “heavy lifting” of asking for the current item and moving forward through the list. When you think about it, this is a “pull” operation – the enumerator is “pulling” the data from the enumerable.

The Reactive Framework (Rx) introduces a new pair of classes: IObservable<T> and IObserver<T>. Observable objects provide a way to “subscribe” to events, and observers (which do the subscribing) are notified when those events happen. In a way it’s a bit like traditional events in the framework, but with a game-changing addition: It’s queryable from LINQ.

What does this mean? It means you can do ridiculous LINQ queries like this one, which sets up an observer that only reacts when the user presses the a, b and c keys successively:

IObservable<Unit> abcPressed =
   from firstKeyPressEvent in pressedIs(Key.A)
   from secondKeyPressEvent in pressedIs(Key.B).Take(1).Until(pressedIsNot(Key.B))
   from thirdKeyPressEvent in pressedIs(Key.C).Take(1).Until(pressedIsNot(Key.C))
   select new Unit();

abcPressed.Subscribe(() => Debug.WriteLine("ABC was pressed."));

Weird? Yeah, today it’s weird. I have a feeling that, a year or two from now, this sort of “querying events” code will be a very natural way to code.

For more information on the Reactive Framework, a good place to start is Jafar Husain’s blog “unfold”, specifically this post:

Introducing Rx (Linq to Events)

You should also check out Paul Baturn’s series of posts entitled “Reacting to the Reactive Framework”. Start with part one here:

Reacting to the Reactive Framework: Part 1

Good News and Bad News

About a month ago I received a call at work from Sal. This is a pretty rare occurrence, but it didn’t worry me at the time. When I asked what was up, she replied, “I have good news and bad news.”

The bad news was that her work was shutting down. I’m sure the “global financial crisis” hasn’t helped, but the company she worked for had been running things very close to the bone for a few years and I guess they reached the tipping point this year and had no choice but to close up shop. So Sal was going to be out of work.

The good news, however, was that since Sal was a full time employee she was getting a redundancy package. The amount, at the time, was almost exactly what was left owing on our mortgage! So we knew that when Sal’s work finished up we’d essentially be debt free.

That day comes today.

Well, that’s not strictly true. Sal finishes work today and she’ll probably get the redundancy package today, but of course there are hoops to jump through to pay off the mortgage and waiting periods while money moves around. Anyway, the crux of it is that for all intents and purposes we own our house now.

For those playing at home, that’s this house that I blogged about seven years ago:

Our home being built

Seven years is a pretty good time period in which to pay off a mortgage. Since I was putting about two thirds of my salary into the home loan, the fact that we’ve paid it off means I’m essentially getting triple my take-home pay each week from now on. What will we do with it? Well, I’m gonna start by picking up a few niceties that I’ve gone without for a while, like a new PC. Then we’re going to plan a European holiday. After that, we’ll settle back down and perhaps look at buying an investment property.

Sal, of course, is going to look for another job. It’s fortuitous timing because she was starting to get a bit restless where she was. I know times are tough for job-seekers right now, but I have every confidence that she’ll find something that she loves before too long.

Sorry if this comes across as a bit self-promotional, but it’s a big milestone in our lives and I wanted to get it “out there” if only so we can look back at this post in another seven years with fond memories.

Name this [Anti-]Pattern

Over the long weekend I managed to do a little bit more work on the next version of Comicster. I don’t know when this version will ever see the light of day because I seem to be spending as much time ripping code out and rewriting it as I do adding new features! Still, it’s a fun and educational way to pass the time.

In this latest batch of work I have been trying to extract the “online database” functionality out of the core executable and turn it into a MEF extension. That way I can introduce other online databases (such as the new ComicVine API) by copying a DLL into a known folder. In doing so, I’ve employed a pattern (or possibly an anti-pattern) which is working quite well. If you know what its name is, please tell me!

I start by defining an interface which will be the contract that any online database extensions will have to fulfil:

public interface IServiceExtension
{
    Character GetCharacter(string id);
    Creator GetCreator(string id);
    Publisher GetPublisher(string id);
    Trade GetTrade(string id);
    Title GetTitle(string id);
    Issue GetIssue(string id);
}

That’s not the complete interface, but this snippet at least gives us the ability to download the details for all the items that Comicster can track online.

However, to use this interface in a program would be quite clunky. I’d have to do a switch on the currently-selected item’s type, calling the appropriate “Get*” method on the interface. Something like:

if (SelectedItem is Issue)
{
    onlineItem = GetIssue(((Issue)SelectedItem).Id);
}
else if (SelectedItem is Title) ...

That’s ugly code, and I know it must be possible to refactor it to take advantage of the fact that all the known types in Comicster derive from a common class (Comicster.Item). So I plan to add a new abstract method to Comicster.Item, like this (I’ll think of a better name eventually):

abstract class Item
{
    abstract Item GetById(IServiceExtension service);
}

This method, then, would have overrides in classes that descend from Item, like this:

class Issue : Item
{
    override Item GetById(IServiceExtension service)
    {
        return service.GetIssue(this.Id);
    }
}

class Title : Item
{
    override Item GetById(IServiceExtension service)
    {
        return service.GetTitle(this.Id);
    }
}

So each class knows which method to call on IServiceExtension to get a copy of its own “online” version. The program code to download the online item becomes much simpler now:

onlineItem = SelectedItem.GetById(service);

This means that no matter what type the selected item is, it will “know” how to download a copy of itself from the online database service. It’s a little bit like the Strategy Pattern, I suppose, except that each derived class knows how to use the given service in its own way. Is there a name for this pattern? Or is it an anti-pattern? Will it work? What do you think?

The Not-So-Hidden Cost of Censorship

With permission, I’d like to reproduce this forum post by industry insider Mark Newton about the Labor government’s plan for Internet censorship:

The Govt claims to be budgeting $44.5m for this scheme.

In Estimates on May 25th we see Ms. O'Loughlin telling Senator Birmingham about the ACMA blacklist:

Ms O’Loughlin—At 30 April ACMA’s blacklist contained 977 URLs.

Then, addressing Senator Ludlam:

Ms O’Loughlin—With the current breakdown at 30 April, 51 per cent were refused classification and around 32 per cent were child abuse material and child sexual abuse material.

According to my superhuman math skills, 51% of 977 is 499 URLs.

If we are to believe Conroy's expression of the Policy Of The Week for the week ending Friday 5th June 2009, those 499 URLs are the only ones that will be blocked.

... at a cost of $44.5m. That's nearly $90,000 per URL.

If 32 per cent of the list was "child abuse material and child sexual abuse material," and assuming that ACMA is actually qualified to make that kind of judgment, that means 313 URLs were in that category.

At $142,173 of Commonwealth expenditure per URL, I reckon I could employ a police officer for an entire year, tell him that his only mission for the year is to track down the pedophile who published that URL, and pay whatever travel expenses are required to track the pedophile down to his house anywhere in the world and arrest him personally.

But no, the Conroy/Rudd plan is to leave that material online where everyone in the world who happens to be interested in it can still find it, and spend all the money on Conroy's List instead — Which will inevitably be published, again. Senator Stephen Conroy will be spending Commonwealth funds on making it easier for people who are turned on by child abuse to find child abuse imagery.

I guess in these trying times, our Government figures that pedophiles need economic stimulus too. :-)

The only saving grace that such a scheme will have is that everyone knows that ACMA's content assessment process is so woefully compromised that it's highly unlikely that the list will be comprised of child abuse imagery, so leaking it will probably prove to be harmless. Refused Classification is a pretty broad brush, and almost all of it is legal, so who cares if it gets out?

Is that part of Conroy's political calculation here?

Mark’s posts on the Whirlpool Forum threads about Labor’s censorship plans are of consistently high quality, but this one was pure gold, and I had to put it out there for others to read.

ClickOnce and Desktop Shortcuts

Ok, this was a weird one. This post isn’t a solution, merely a description of the problem and a workaround. I’m putting it out there for Google-juice just in case someone else has the same issue and has an idea of a proper solution.

We have a couple of applications internally that are ClickOnce deployed. They date back to Visual Studio 2005 so they’re .NET 2.0 applications, and they’ve had plenty of updates over the years which get pulled down automatically by the clients. It works a treat.

Now, if you remember all the way back to the days of yore before there was such thing as .NET 3.5 SP1, you’ll know that ClickOnce originally didn’t give you the option of creating a desktop shortcut. The only thing you could do as part of the deployment was to create an icon in the Start menu. That was fine for us – our users would install the application and then drag the Start menu icon onto their desktop manually.

3.5 SP1 introduced the ability to create a desktop shortcut as part of the ClickOnce installation, and I think it has caused a bit of a problem for us.

You see, just recently our IT department pushed out .NET 3.5 SP1 via a group policy. That’s awesome for us, because every PC in the company now has it installed. Our applications, though, are still .NET 2.0, and thus don’t have the “create desktop icon” setting in their ClickOnce options.

With the latest updates to our applications, it seems that the client PCs are looking at the update, noticing that it doesn’t have an option to create a desktop shortcut, and removing the existing desktop icon for the app! So everybody who updates to the latest (.NET 2.0) version of our app is calling us, complaining that their desktop icons have disappeared!

The only workaround we have right now is to update the apps themselves to .NET 3.5 SP1 apps, and check the “Create desktop icon” checkbox in the ClickOnce options dialog. It’s not much of a solution, and it’s a drag for those clients who don’t want a desktop icon, because it gets re-created with every update. Still, it’s all we can do right now.

If you’ve seen this behaviour, leave us a comment! It’s a shame that ClickOnce couldn’t just leave the icon there if the update tells it not to create one.

Playing with ViewModel in WPF

Please don’t read this post as some sort of lesson in best-practice for WPF. It’s purely about my (mis)adventures with the Model-View-ViewModel (MVVM) pattern so far, in which I totally misuse and abuse the pattern to get something working.

We’ve been working on a WPF navigation-style application that’s essentially a big load of CRUD. It has a bunch of pages, and they all have a very similar layout:

A common page in my app

After creating the seventh or eighth version of this page, I started wondering whether we couldn’t just have one “view” page with this layout, and a bunch of “ViewModel” classes that provide the page with its logic. We could use WPF’s DataTemplate system to customize what the list displayed, and its Command system for the buttons and “extra” commands.

I found this article by Josh Smith on MSDN and gave it a quick skim. My one big take-away from it was his RelayCommand class, which is a lightweight, lambda-based ICommand implementation. So I stole that and started spiking.

ViewModel<T>

So here’s my ViewModel class in all its glory. As you can see, it has a command for each button (Find, New, Edit and Delete). They’re instances of RelayCommand which are tied to some virtual methods on the class. When I want a new ViewModel I simply derive from this class and override the methods like CanDelete(object item) or CreateNew().

Notice that the class has a property called SearchCriteriaControl. This is of type UserControl, and represents the “Search criteria” section in my sketched-out page above. That way a ViewModel can customize how the user searches for its items.

The other thing I’ve done here is given my ViewModel SelectedValue and SelectedValuePath properties. These are dependency properties, and are created with FrameworkPropertyMetadata.Journal = true, so when you navigate away from the page and then back again, it remembers which item you last selected in the ListBox.

Along the way I have also added two properties to RelayCommand. One is “Text” so that I can customize what the buttons show (certain objects might show “Disable” rather than “Delete”, for example). The other is a boolean “IsAvailable” property which I set to false if I want the button to disappear completely. Certain objects can be created and deleted but never edited, and it would be frustrating for a user to see an Edit button that’s always disabled.

Here’s a snippet of XAML from my view page:

<StackPanel>
    <ContentControl
        x:Name="searchCriteriaControl" 
        Content="{Binding SearchCriteriaControl}" 
        Margin="0,0,0,5"
        TabIndex="0"
        IsTabStop="False"
        Keyboard.PreviewKeyDown="searchCriteriaControl_PreviewKeyDown"
        />
    <Button 
        Command="{Binding FindCommand}" 
        Width="75" 
        HorizontalAlignment="Right" 
        Content="_Find"
        TabIndex="1" />
</StackPanel>

So you can see that I’m presenting the user with the customized Search Criteria UI, and a “Find” button that tells the ViewModel to perform the search. The PreviewKeyDown event handler is so the user can always press Enter to perform the search. Here’s the strip of buttons along the bottom:

<DockPanel DockPanel.Dock="Bottom" LastChildFill="False" Margin="10"> <DockPanel.Resources> <BooleanToVisibilityConverter x:Key="b2v"/> <Style TargetType="Button"> <Setter Property="DockPanel.Dock" Value="Right"/> <Setter Property="Margin" Value="5,0,0,0"/> <Setter Property="Width" Value="75"/> <Setter Property="Content"

Value="{Binding Command.Text,RelativeSource={x:Static RelativeSource.Self}}" /> <Setter Property="Visibility"

Value="{Binding Command.IsAvailable,RelativeSource={x:Static RelativeSource.Self},Converter={StaticResource b2v}}" /> </Style> </DockPanel.Resources> <Button Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedItem,ElementName=listBox1}" TabIndex="5"/> <Button Command="{Binding EditCommand}" CommandParameter="{Binding SelectedItem,ElementName=listBox1}" TabIndex="4" /> <Button Command="{Binding NewCommand}" TabIndex="3" /> </DockPanel>

So each button is bound back to its corresponding command, and also pulls its visibility and content from the command’s IsAvailable and Text properties, respectively.

Lastly, here’s the list of “extra” commands at the bottom-left of the page:

<GroupBox DockPanel.Dock="Bottom" Header="Extras" Padding="5"> <GroupBox.Style> <Style TargetType="{x:Type GroupBox}"> <Style.Triggers> <DataTrigger Binding="{Binding ExtraCommands.Count}" Value="0"> <Setter Property="Visibility" Value="Collapsed"/> </DataTrigger> </Style.Triggers> </Style> </GroupBox.Style> <ItemsControl DockPanel.Dock="Bottom" ItemsSource="{Binding ExtraCommands}"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Margin="0,3">

<Hyperlink Command="{Binding}"><TextBlock Text="{Binding Text}"/></Hyperlink>

</TextBlock> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </GroupBox>

So we simply display the extra commands as a list of hyperlinks inside a GroupBox, and hide the entire thing if there aren’t any extra commands.

Now the big reveal. Here’s what my page looks like with a derived ViewModel driving it:

The working view page with an "Entries" ViewModel

So the “Edit” and “Delete” buttons are disabled because the selected item is read-only (a property on the model). You can see that I’ve provided my own Search Criteria control (a label and a date TextBox). The big “Entries” label at the top is bound to the “Title” property on the ViewModel so it changes depending on what type you’re dealing with.

This is working pretty well in my spike. I haven’t yet hit any major stumbling blocks, and I’ve managed to get a few different view models working, including one that swaps out the search criteria for a simple CheckBox (“active only”).

One thing that’s still fuzzy right now is how much the ViewModels should know about my application. For example, when the user clicks the “New” button, the ViewModel will have to navigate to another page; but should it know which page it needs to navigate to, or should I be “injecting” that into the ViewModel with a Func<Page> or something?

More to come! Leave me a comment and tell me where you think I’m going wrong!

Internet Celebrity Power

On Saturday, Scott Hanselman tweeted about the new Stack Overflow “flair” feature, and I directed him here to Mad Props so he could check out my “Xbox GamerCard” version (you can see it over there in the sidebar on the top-right). Subsequently he posted this tweet, linking to my blog post about the Windows 7 logo.

Witness the power of an Internet celebrity. Here’s what happened to my daily hit rate when Scott linked to me:

Views per day

That post quickly jumped to #4 on my most viewed posts of all time chart. Not bad for a single link on Twitter!

WPF Killed the RadioButton Star

Ok, it’s a really dumb title for a blog post, but this is just a little love for WPF and its ability to completely restyle a standard control.

In our most recent project, we have a list of groups of pigs that we show the user so they can choose one to work with. The groups might be male, female, castrates or mixed, and we need to let the user filter the list by sex so that there’s not as many to choose from.

We ended up using RadioButtons, one for each sex. As an example, here’s the XAML for the “Females” option:

    <RadioButton Grid.Column="2" 
                 Tag="{x:Static qfp:ProgenySex.Female}"
                 ToolTip="Show only female groups">
        <Label>_Females</Label>
    </RadioButton>

However, we knew that the filtering options in the finished product shouldn’t look like radio buttons. In fact, the effect I wanted was closer to the FlatButtons appearance on the TabControl in Windows Forms:

image

In the end, with a little bit of XAML templating, I was able to achieve exactly the look I wanted. It’s even better than the TabControl! Here’s a screenshot:

image

So the “buttons” at the top of the list are actually RadioButtons (using the XAML above), and in this screenshot the user has selected “Females”. Below is a part of the XAML we used to restyle the radio buttons (I’ve omitted the icon setters etc). It just goes to show how powerful WPF is!

<Style TargetType="{x:Type RadioButton}"> <EventSetter Event="ToggleButton.Checked" Handler="SetProgenySexFilter" /> <Setter Property="Focusable" Value="False" /> <Setter Property="GroupName" Value="filter"/> <Setter Property="IsTabStop" Value="False" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type RadioButton}"> <ControlTemplate.Resources> <Style TargetType="{x:Type Image}"> <Setter Property="Height" Value="16" /> <Setter Property="Width" Value="16" /> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Margin" Value="0,0,2,0" /> </Style> <Style TargetType="{x:Type TextBlock}"> <Setter Property="VerticalAlignment" Value="Center"/> </Style> <Style TargetType="{x:Type Label}"> <Setter Property="Padding" Value="0"/> </Style> </ControlTemplate.Resources> <Border x:Name="PART_border" CornerRadius="2" Padding="3px" Margin="2px" Background="Transparent" BorderThickness="1" BorderBrush="{x:Static SystemColors.ControlDarkBrush}" SnapsToDevicePixels="True"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Image x:Name="PART_icon" /> <ContentPresenter x:Name="PART_content" /> </StackPanel> </Border> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="True"> <Setter TargetName="PART_content" Property="TextBlock.FontWeight" Value="Bold"/> <Setter TargetName="PART_border" Property="Background"> <Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Color="{x:Static SystemColors.ControlLightColor}"

Offset="0"/> <GradientStop Color="{x:Static SystemColors.ControlColor}"

Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> </Trigger>

Windows 7 Doubles My RAM (Sort Of)

Here’s a strange little quirk I discovered after installing the Windows 7 Release Candidate on my old home PC (whose original specs you can see here):

Installed memory (RAM): 2.00 GB (1.00 GB usable)

Now, I have this PC open, and I can guarantee that there’s only 1GB of RAM installed. I don’t have anything else plugged in that would account for the “2.00 GB” that’s being reported.

I’ve read others on the net complaining that Windows 7 reports that it’s only using a subset of their installed RAM, but this is reporting that it’s using all of the installed RAM, and letting me know that in some alternate reality (perhaps a future date?) I will actually have a whole ’nother gigabyte to use!

So there you go – Windows 7 is so powerful it can actually see RAM from alternate timelines. Perhaps Windows 8 will be able to use it!