Archive for October, 2010

Persisting WPF control state

I would like to be able to easily save/persist the state of an element in WPF. In my current project we have a “desktop” with gadgets that you can move around; you have the possibility to change width and position on ListView columns. But the problem is that this is only stored in memory as the application is running. I would like to be able to easily write in xaml what properties and elements that should have its state persisted.

The first thing that came to my mind was Attached Properties. I just love attached properties and what you can achieve with them. I found a blog post by Tomer Shamam where he describes his implementation of exactly my problem. I downloaded the sample application he had, plugged the implementation into my project and viola, I can now easily Persist property values when closing and restarting my application. I did however change his implementation a bit to fully satisfy my needs, and also how the states are persisted. We need to save them in our database since our WPF application is running through Citrix and the users are using our application at several machines/terminals and therefore we need to save the user profile settings in a central repository.

Check out this blog if you want to know how to persist WPF control state.

http://blogs.microsoft.co.il/blogs/tomershamam/archive/2007/06/08/wpf-control-state-persistency.aspx

But in his solution you cannot save the order of columns in a ListView. So how should I do that?

Persisting WPF ListView Column Order

Let’s start by investigating what is happening when you move a column in a ListView. You can add an event handler to the GridView.Columns.CollectionChanged  event and from there get indication of what is happening.


((GridView)listView1.View).Columns.CollectionChanged += Columns_CollectionChanged;
void Columns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move)   
    { … }
}

From NotifyCollectionChangedEventArgs you get the old index and new index for the column that you are moving.  We can then track all these changes to be able to later know exactly what moves that has been made on the columns collection and what order the columns has now.

The next problem is how do we easily move columns in a ListView? Fortunately there is a Move method on the columns collection


((GridView)listView1.View).Columns.Move(oldIndex, newIndex);

This method will move the specified item at the old index to a new index in the collection. So if we have done 10 column moves in the ListView and tracked all these in memory, we can then later reproduce the exact same moves using the Move method, this is what I would call a quick solution since we are probably doing a lot of moves that are unnecessary.

Instead let’s compare the original positions with the end result and from that calculate what minimal moves are needed to be able to achieve the same end result.

The implementation I ended up with removed and inserted the columns at their correct position during loading of the controls. But to be able to easily save the column order for each user I created a custom control that inherits ListView and provided a dependency property that is always up to date with the current column order as a string. For example, “3,0,1,2,4,5”, this means that column 3 in the original ListView is moved to be the first column in the end result.

I have a local field that holds the current column order


private List currentColumnOrder;

Event handler that updates the currentColumnOrder field to reflect moves made by the user

void Columns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move)
    {
        var item = currentColumnOrder [e.OldStartingIndex];
        currentColumnOrder.RemoveAt(e.OldStartingIndex);
        currentColumnOrder.Insert(e.NewStartingIndex, item);
    }
}

Save

var columnOrder = string.Join(",",currentColumnOrder.Select(o => o.ToString()).ToArray());
// Set the ColumnOrder property to hold the current column order so that we can later persist it
// using ElementState Attached Properties
ColumnOrder = columnOrder;

Load

var indices = ColumnOrder.Split(',');
var tempColumns = new List();
foreach (var col in ((GridView)this.View).Columns)
{
    tempColumns.Add(col);
}
// Move columns to their proper position
for (int i = 0; i < indices.Length - 1; i++)
{
    var index = int.Parse(indices[i]);
    var col = tempColumns[index];
    // Only move column if it not in default position
    if (view.Columns[i] != col)
    {
        view.Columns.Remove(col);
        view.Columns.Insert(i, col);
    }
}

Xaml

<ctrls:PersistingListView

x:Name="SearchResultListView"

ItemsSource="{Binding SearchResult}"

SelectedItem="{Binding SelectedItem}"

ElementState.Mode="Persist"

ElementState.UId="ArticleSearchResultListView"

ColumnOrder="{PropertyState Default=''}">

<ListView.View>

<GridView

ElementState.Mode="PersistOrder"

ElementState.UId="ArticleSearchResultGridView">

<!--RowNumber-->

<GridViewColumn

Width="{PropertyState Default=40}"

ElementState.Mode="Persist"

ElementState.UId="ArticleSearchResultRowNumberColumn">

. . .

I cannot give you the complete code, but send me an email if you want more detailed source code.

Advertisements

Reduce frame rate for WPF animations to gain performance

 

Animations are very cool, but they can cause a high CPU load. One reason could be a missing hardware acceleration due to a old graphics adapter or a software rendering constraint. Another reason could be the the high frame rate of animations that is set to 60 fps by default.

You can easily lower the framerate for all animations by overriding the DesiredFrameRate property of the timeline. Just add the following code to your project and play around with the setting to find a good tradeoff between performance and aesthetic.

Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline),
   new FrameworkPropertyMetadata { DefaultValue = 30 });

Or you can set the framerate individually for each animation in XAML, using the following code:

<DoubleAnimation
    Storyboard.TargetProperty="Opacity"
    Duration="0:0:0.5" From="1.0" To="0.5"
    Timeline.DesiredFrameRate="30" />

In my current project we have a WPF application that is executed through Citrix. The frame rate has not been an issue until the application was run on a thin client/terminal with extremely limited resources and the application took 100% CPU when running the simplest animation. Our LOB WPF application does not have many animations but the once that are there are needed, so removing animations was out of the question. But we really don’t need 60 fps! So lowering frame rate to 15 made CPU performance go from 100% to 10% when animations was executed, and they all looked the same. Nice feature to know about!

Pixlr – ”Photoshop Online”

 

I just have to say that I love the online pixlr editor! even though it is not a Silverlight application :)

You can open Adobe Photoshop files with it and have access to all layers , and most of the features and effects I tend to use in Adobe Photoshop etc. You can then save a PXD file so that you can continue with you images at a later time. I use it a lot to build mock-up UI and wireframes. If you have not seen it you better check it out.

What is Pixlr?
Pixlr is the creator of online cloud-based image tools. Today we have two applications in our suite, one we call
Pixlr Editor and the other is Pixlr Express. They are built in Flash and you need to have the Flash plug-in (get flash) to get it to work, however 98% of all computers have flash so you are probably set.

Advertisements
%d bloggers like this: