WPF Tidbits
Here’s a couple of ‘how do you that in wpf?’ that may save you an hour or two. There’s nothing particularly astounding in any of them but implementation is different enough from the WinForm counterparts that it may not be intuitive enough.
Modal Dialog
<Window x:Class="WpfDialogTestApp.WpfDialog"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Wpf Dialog" Height="103" Width="257"><Grid><Grid.RowDefinitions><RowDefinition Height="30"></RowDefinition><RowDefinition></RowDefinition></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition></ColumnDefinition><ColumnDefinition></ColumnDefinition></Grid.ColumnDefinitions><TextBlock VerticalAlignment="Center">Enter#</TextBlock><TextBox Name="textNumber" Grid.Column="1"></TextBox><Button Grid.Row="1" Name="buttonOK" Click="buttonOK_Click">OK</Button><Button Grid.Column="1" Grid.Row="1" IsCancel="True">Cancel</Button></Grid></Window>
Horizontal Listbox
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:v="clr-namespace:HorzListboxTestApp.Views" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="HorzListboxTestApp.Views.MainWindowView" MinHeight="70" MinWidth="300"><UserControl.Resources><Style TargetType="ListBox"><Setter Property="ItemsPanel"><Setter.Value><ItemsPanelTemplate><StackPanel Orientation="Horizontal"VerticalAlignment="Center"HorizontalAlignment="Center"/></ItemsPanelTemplate></Setter.Value></Setter></Style><Style TargetType="{x:Type ListBoxItem}"><Setter Property="HorizontalContentAlignment" Value="Stretch" /><Setter Property="ContentTemplate"><Setter.Value><DataTemplate><Border BorderThickness="2"><StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"><v:LBItemView/></StackPanel></Border></DataTemplate></Setter.Value></Setter></Style></UserControl.Resources><Grid><Grid.RowDefinitions><RowDefinition></RowDefinition><RowDefinition></RowDefinition></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="80"></ColumnDefinition><ColumnDefinition></ColumnDefinition></Grid.ColumnDefinitions><ListBox Grid.Column="1" Grid.RowSpan="2" ItemsSource="{Binding Path=Items}"></ListBox><Label Background="{Binding ContentBrush}"></Label><TextBlock Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Path=SelectedItem}"></TextBlock></Grid>
MVVM Context Menu
<UserControl x:Class="HorzListboxTestApp.Views.LBItemView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Height="50" Width="40"><UserControl.Resources><Style x:Key="buttonWithTip" TargetType="{x:Type Button}"><Style.Triggers><Trigger Property="IsMouseOver" Value="true"><Setter Property="ToolTip"Value="{Binding Path=GetTip}"/></Trigger></Style.Triggers></Style><Style x:Key="ContextMenuItemStyle"><Setter Property="MenuItem.Header" Value="{Binding Path=OptionText}"/><Setter Property="MenuItem.Command" Value="{Binding Path=OptionCommand}" /></Style></UserControl.Resources><Grid><Grid.RowDefinitions><RowDefinition Height="*"></RowDefinition><RowDefinition Height="20"></RowDefinition></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition></ColumnDefinition><ColumnDefinition></ColumnDefinition></Grid.ColumnDefinitions><Button Background="{Binding ContentBrush}" Grid.ColumnSpan="2" Command="{Binding Path=SelectedCommand}" Style="{StaticResource buttonWithTip}"><Button.ContextMenu><ContextMenu ItemContainerStyle="{StaticResource ContextMenuItemStyle}" ItemsSource="{Binding Path=MenuOptions}" /></Button.ContextMenu></Button><TextBlock Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding ItemNumber}"></TextBlock><CheckBox Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding Path=ItemChecked, Mode=TwoWay}"></CheckBox></Grid></UserControl>
Two points will provide all the clarity we need. ContextMenu has an ItemsSource same as a listbox so we can bind to it and provide the list of items to display. The more important part is the whole point of the context menu, to provide the user with a mechanism to make a selection. So we need a way to identify which item the user selected. Well, if we use the same approach we used for the listbox then we can let each menu item be independent on to itself. In other words, provide a ViewModel for each context menu item. To provide the binding all that’s needed is to define a style for the menu item. The style simply defines where the text for the menu item comes from and how to bind the command that will be executed when a user selects the item. The code above shows how both of these were implemented and the support class is shown below.
public class MenuOptionItem{public string OptionText { get; set; }public ICommand OptionCommand { get; set; }public MenuOptionItem(string optionText){this.OptionText = optionText;OptionCommand = new DelegateCommand(OptionSelected);}public void OptionSelected(){MessageBox.Show("You selected:" + OptionText);}}
The sample project also shows how you can dynamically create context menus based on the state of the item.
Sortable/Checkable ListView
<UserControl x:Class="ListViewTestApp.Views.MainWindowView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:c="clr-namespace:ListViewTestApp.Common"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Height="200" Width="350"><Control.Resources><DataTemplate x:Key="HeaderTemplateArrowUp"><DockPanel><TextBlock HorizontalAlignment="Center" Text="{Binding}"/><Path x:Name="arrow" StrokeThickness = "1" Fill = "Gray" Data = "M 5,10 L 15,10 L 10,5 L 5,10"/></DockPanel></DataTemplate><DataTemplate x:Key="HeaderTemplateArrowDown"><DockPanel><TextBlock HorizontalAlignment="Center" Text="{Binding}"/><Path x:Name="arrow" StrokeThickness = "1" Fill = "Gray" Data="M 5,5 L 10,10 L 15,5 L 5,5"/></DockPanel></DataTemplate><DataTemplate x:Key="HeaderTemplateTransparent"><DockPanel><TextBlock HorizontalAlignment="Center" Text="{Binding}"/><Path x:Name="arrow" StrokeThickness="1" Fill="Transparent" Data="M 5,5 L 10,10 L 15,5 L 5,5"/></DockPanel></DataTemplate><!--Sort handler that sorts the columns--><c:GridViewSortHandler x:Key="sortHandler"ColumnHeaderSortedAscendingTemplate="HeaderTemplateArrowUp"ColumnHeaderSortedDescendingTemplate="HeaderTemplateArrowDown"ColumnHeaderNotSortedTemplate="HeaderTemplateTransparent" /><Style x:Key="OrderItemStyle" TargetType="{x:Type ListViewItem}"><Setter Property="HorizontalContentAlignment" Value="Stretch" /><Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" /></Style></Control.Resources><Grid><ListView c:GridViewSortHandler.GridViewSortHandler="{StaticResource sortHandler}" Name="listOrders" Width="Auto"ItemContainerStyle="{StaticResource OrderItemStyle}"ItemsSource="{Binding Path=OrderItems}"><ListView.View><GridView><GridViewColumn Width="30"><GridViewColumn.CellTemplate><DataTemplate><CheckBox IsEnabled="{Binding Path=CheckEnabled}" IsChecked="{Binding Path=ItemChecked}"></CheckBox></DataTemplate></GridViewColumn.CellTemplate></GridViewColumn><c:SortableGridViewColumn Width="100" Header="Date" SortPropertyName="OrderDate" DisplayMemberBinding="{Binding Path=OrderDate}" /><c:SortableGridViewColumn Width="50" Header="Order#" SortPropertyName="OrderNumber" DisplayMemberBinding="{Binding Path=OrderNumber}" /><c:SortableGridViewColumn Width="50" Header="Item" SortPropertyName="ItemSKU" DisplayMemberBinding="{Binding Path=ItemSKU}" /><c:SortableGridViewColumn Width="25" Header="Qty" SortPropertyName="ItemQty" DisplayMemberBinding="{Binding Path=ItemQty}" /><c:SortableGridViewColumn Header="Description" SortPropertyName="" DisplayMemberBinding="{Binding Path=Description}"/></GridView></ListView.View></ListView></Grid></UserControl>
The sample project provides all the implementation details. Each column is also decorated with a ’sort direction’ arrow indicating ascending/descending sort order. You can extend the code to provide multi-column sort if required as well as custom sort algorithm. You’ll find more information with the AvalonControlsLibrary which by the way includes a number of other useful controls.
One last note on the CheckBox, it’s enabled from code. The reason is that sometimes you want to prevent the user from making the selection twice; maybe the order has already been shipped.
1 Comment to “WPF Tidbits”
-
Twitted by K3n5u — October 23, 2009 @ 12:00 am
RSS feed for comments on this post. TrackBack URI