Introduction
This article is going to highlight some of the awesomeness of WPF (and Silverlight) through XAML, INotifyPropertyChanged, Item Templates, Data Templates and Control Templates.
The Sample Project
To demonstrate these concepts, I have uploaded a sample project to MSDN that you can download and play with yourself.
A Brief Explanation
Firstly, to produce a list of buttons with menus, I am using a ListBox:
<
Grid
>
<
ListBox
ItemsSource
=
"{Binding Applications}"
ItemTemplate
=
"{StaticResource
ApplicationsTemplate}"
ItemContainerStyle
=
"{StaticResource NoHighlightStyle}"
ItemsPanel
=
"{DynamicResource HorizontalItemsPanel}"
/>
</
Grid
>
To list the buttons horizontally, I change the ItemsPanel:
<
ItemsPanelTemplate
x:Key
=
"HorizontalItemsPanel"
>
<
VirtualizingStackPanel
Orientation
=
"Horizontal"
IsItemsHost
=
"True"
VerticalAlignment
=
"Top"
/>
</
ItemsPanelTemplate
>
<
Style
x:Key
=
"NoHighlightStyle"
TargetType
=
"{x:Type ListBoxItem}"
>
<
Setter
Property
=
"Template"
>
<
Setter.Value
>
<
ControlTemplate
TargetType
=
"{x:Type ListBoxItem}"
>
<
Grid
Background
=
"{TemplateBinding Background}"
>
<
ContentPresenter
x:Name
=
"contentPresenter"
ContentTemplate
=
"{TemplateBinding ContentTemplate}"
Content
=
"{TemplateBinding Content}"
HorizontalAlignment
=
"{TemplateBinding HorizontalContentAlignment}"
Margin
=
"{TemplateBinding Padding}"
/>
</
Grid
>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
To generate the actual buttons, I change the ItemTemplate:
<
DataTemplate
x:Key
=
"ApplicationsTemplate"
>
<
Grid
>
<
RadioButton
x:Name
=
"tb"
Content
=
"{Binding Name}"
Style
=
"{DynamicResource MouseOverPopupRadio}"
GroupName
=
"GrpApp"
/>
<
Popup
IsOpen
=
"{Binding IsChecked, ElementName=tb}"
>
<
ListBox
ItemsSource
=
"{Binding Options}"
ItemTemplate
=
"{StaticResource
OptionsTemplate}"
ItemContainerStyle
=
"{StaticResource NoHighlightStyle}"
/>
</
Popup
>
</
Grid
>
</
DataTemplate
>
<
Style
x:Key
=
"MouseOverPopupRadio"
TargetType
=
"{x:Type RadioButton}"
>
<
Setter
Property
=
"Template"
>
<
Setter.Value
>
<
ControlTemplate
TargetType
=
"{x:Type RadioButton}"
>
<
TextBlock
Text
=
"{TemplateBinding Content}"
Margin
=
"5"
Padding
=
"5"
Background
=
"Yellow"
/>
</
ControlTemplate
>
</
Setter.Value
>
</
Setter
>
</
Style
>
Also note the popup contains a ListBox for the options, like a Context Menu:
<
DataTemplate
x:Key
=
"OptionsTemplate"
>
<
Button
IsEnabled
=
"{Binding IsEnabled}"
Content
=
"{Binding Name}"
Margin
=
"5"
Click
=
"OptionButton_Click"
/>
</
DataTemplate
>
Below is the class for the options. Because we want to control the IsEnabled property of each option, we use
INotifyPropertyChanged to notify the Ui that IsEnabled has changed:
public
class
Option : INotifyPropertyChanged
{
public
string
Name
{
get
;
set
; }
public
int
ParentIndex
{
get
;
set
; }
bool
_IsEnabled;
public
bool
IsEnabled
{
get
{
return
_IsEnabled;
}
set
{
if
(_IsEnabled != value)
{
_IsEnabled = value;
RaisePropertyChanged(
"IsEnabled"
);
}
}
}
void
RaisePropertyChanged(
string
prop)
{
if
(PropertyChanged !=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs(prop)); }
}
public
event
PropertyChangedEventHandler PropertyChanged;
}
When an option button is clicked, we can extrapolate back to find the actual option, its parent application and therefore change the IsEnabled property of the other options, and of course act upon the application depending on the option.
private
void
OptionButton_Click(
object
sender, RoutedEventArgs e)
{
var button = sender
as
Button;
var option = button.DataContext
as
Option;
var application = Applications[option.ParentIndex];
switch
(option.Name)
{
case
"Share"
:
foreach
(var opt
in
application.Options)
opt.IsEnabled
= opt.Name ==
"Share"
?
false
:
true
;
break
;
default
:
foreach
(var opt
in
application.Options)
opt.IsEnabled
= opt.Name ==
"Share"
?
true
:
false
;
break
;
}
}
This is a silly function that simply changed the IsEnabled property, and hence updates the UI, but it shows how I can reference the option, and
it's parent class, which allows me to do whatever I need to do.
See Also
Another important place to find a huge amount of WPF related articles is the TechNet Wiki itself. The best entry point is WPF Resources on the TechNet Wiki