A Simple WP7 JumpList in WPF
Introduction
I am deeply impressed by Metro UI, the typography based design created by Microsoft. It is very clean and fast. In this article, I am demonstrating a simple example of how a WP7 JumpList can be created in WPF. The WP7 JumpList was demonstrated in the MIX '11 event.

Using the code
The WPF JumpList control is composed of two controls-
- IndexControl - used to display the indices.
- JumpListControl - encapsulates the contents of the JumpList.
IndexControl
The IndexControl is used to display the index in the ValuesPanel as well as the JumpListPanel. It has a dependency property IndexValue which is used to display the index value in the IndexControl.
public static readonly DependencyProperty IndexValueProperty =
DependencyProperty.Register("IndexValue", typeof(string), typeof(IndexControl),
new FrameworkPropertyMetadata((new PropertyChangedCallback(OnIndexValueChanged))));
The IndexControl also has a dependency property IndexState to define its state.
public static readonly DependencyProperty IndexStateProperty =
DependencyProperty.Register("IndexState", typeof(IndexStateType), typeof(IndexControl),
new FrameworkPropertyMetadata(IndexStateType.ListDisplay, (new PropertyChangedCallback(OnIndexStateChanged))));
There are three states, defined by the enum IndexStateType.
public enum IndexStateType
{
// The state when the IndexControl is displayed in the long list.
ListDisplay,
// The state when the IndexControl is displayed in the JumpList and this index has NO values
IndexNotFound,
// The state when the IndexControl is displayed in the JumpList and this index has values
IndexFound
}
Based on its state the look and feel of the IndexControl changes.
JumpListControl
The WPF JumpList control has two panels -
- ValuesPanel - used to display the values (names) in a long list format.
- JumpListPanel - used to display the indices.
At any given instance, only one of the above panels is visible in the JumpListControl, while the other panel remains hidden.
ValuesPanel
The ValuesPanel displays the values (or names) in a long list format. For each index, the IndexControl and the set of Names are encapsulated in a StackPanel and added to the parent StackPanel.
JumpListPanel
The JumpListPanel consists of a WrapPanel in which the IndexControls are added.
The JumpListPanel defines two dependency properties -
- Values - ObservableCollection<string> - serves as an input for the control.
public static readonly DependencyProperty ValuesProperty =
DependencyProperty.Register("Values", typeof(ObservableCollection<string>), typeof(JumpListControl),
new FrameworkPropertyMetadata((new PropertyChangedCallback(OnValuesChanged))));
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(string), typeof(JumpListControl),
new FrameworkPropertyMetadata((new PropertyChangedCallback(OnSelectedValueChanged))));
When the JumpListControl is initialized, it adds IndexControls, to the JumpListPanel, for the indices a-z. The state of each of the IndexControl is set to IndexNotFound.
public JumpListControl()
{
InitializeComponent();
JumpListScrollView.Visibility = System.Windows.Visibility.Hidden;
foreach (char idx in indexes)
{
IndexControl idxCtrl = new IndexControl
{
Width = 50,
Height = 50,
IndexValue = idx.ToString(),
IndexState = IndexStateType.IndexNotFound,
Margin = new Thickness(4, 4, 0, 0)
};
idxCtrl.MouseLeftButtonDown += new MouseButtonEventHandler(OnIndexClickedInJumpList);
JumpListPanel.Children.Add(idxCtrl);
}
}
When the Values dependency property is set by the user, the JumpListControl parses the list and creates the indexed long list in the ValuesPanel and changes the state of the IndexControls, in the JumpListPanel, from IndexNotFound to IndexFound.
private void Parse(ObservableCollection<string> values)
{
Dictionary<string, List<string>> valueDict = new Dictionary<string, List<string>>();
List<string> valueList = values.ToList();
// Sort the values
valueList.Sort();
// Get the distinct indexes
foreach (string str in valueList)
{
string key = Char.ToLower(str[0]).ToString();
if (!valueDict.ContainsKey(key))
{
valueDict[key] = new List<string>();
}
valueDict[key].Add(str);
}
// Set the IndexState of all the IndexControls whose index has been found as IndexFound
JumpListPanel.Children.OfType<IndexControl>()
.Where(i => valueDict.Keys.Contains(i.IndexValue))
.All(i =>
{
i.IndexState = IndexStateType.IndexFound;
return true;
});
// Add the index and the related names to the Values Panel
foreach (string key in valueDict.Keys)
{
StackPanel stkPanel = new StackPanel { HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch };
IndexControl idxCtrl = new IndexControl
{
Width = 50,
Height = 50,
IndexValue = key,
IndexState = IndexStateType.ListDisplay,
Margin = new Thickness(4),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
VerticalAlignment = System.Windows.VerticalAlignment.Center
};
idxCtrl.MouseLeftButtonDown += new MouseButtonEventHandler(OnIndexClickedInValuesPanel);
stkPanel.Children.Add(idxCtrl);
foreach (string str in valueDict[key])
{
TextBlock tb = new TextBlock
{
FontFamily = font,
FontWeight = FontWeights.Light,
FontSize = 22,
Foreground = Brushes.White,
TextAlignment = TextAlignment.Left,
Margin = new Thickness(4, 4, 0, 0),
Text = str,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
VerticalAlignment = System.Windows.VerticalAlignment.Center
};
tb.MouseLeftButtonDown += new MouseButtonEventHandler(OnValueSelected);
stkPanel.Children.Add(tb);
}
ValuesPanel.Children.Add(stkPanel);
}
}
When the ValuesPanel is being displayed and the user clicks on any of the indices, then the ValuesPanel is hidden and the JumpListPanel is displayed. When the user clicks on any of the indices in the JumpListPanel, the JumpListPanel is hidden, the ValuesPanel is displayed and the ValuePanel scrolls to the index selected by the user.
Clicking on any of the Names will set the SelectedValue property of the JumpListControl.
EndPoint
What I have attempted here is a basic implementation of the JumpList control which accepts a list of strings as input. It can be further modifield to accept a collection of object's (say a person's record) and an image can be displayed alongside each name in the ValuesPanel.
Points of Interest
An important lesson I learnt during the development of this control is that when you are adding items to a WrapPanel (say with Orientation=Horizontal) and the items extend beyond the height of the WrapPanel, then the vertical scrollbar will not appear automatically (even though you set ScrollViewer.VerticalScrollBarVisibility="Auto").
To overcome this problem, you need to wrap your WrapPanel with a ScrollViewer.
<ScrollViewer Name="JumpListScrollView"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<WrapPanel Name="JumpListPanel"
Orientation="Horizontal">
</WrapPanel>
</ScrollViewer>
History
23rd May, 2010 - Version 1.0 released.
发表评论
Zl03rg wow, awesome article post.
Y9KHv4 Thank you for your blog article. Much obliged.