コントロールを拡張して ICommandSource をサポートし、コマンドをトリガーするアクションを決定します。
これを Combo Box で行い、コマンドのトリガーとして OnSelectionChanged を使用しました。最初に、CommandComboBox と呼ばれる拡張された Control ComboBox にコマンドをバインドする方法を XAML で示します。次に、ICommandSource のサポートを ComboBox に追加する CommandComboBox のコードを示します。
1) XAML コードで CommandComboBox を使用する:
XAML 名前空間の宣言には次のものが含まれます
xmlns:custom="clr-namespace:WpfCommandControlsLibrary;assembly=WpfCommandControlsLibrary">
ComboBox の代わりに CommandComboBox を使用し、次のようにコマンドをバインドします。
<custom:CommandComboBox
x:Name="ux_cbSelectLanguage"
ItemsSource="{Binding Path = ImagesAndCultures}"
ItemTemplate="{DynamicResource LanguageComboBoxTemplate}"
Command="{Binding Path=SetLanguageCommand, Mode=Default}"
CommandParameter="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=SelectedValue, Mode=Default}"
IsSynchronizedWithCurrentItem="True"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Grid.Column="1" Margin="0,0,20,0" Style="{DynamicResource GlassyComboBox}" ScrollViewer.IsDeferredScrollingEnabled="True"
/>
2) CommandComboBox のコード
ファイル CommandComboBox.cs のコードを以下に示します。このファイルを WpfCommandControlsLibrary というクラス ライブラリに追加し、それを別のプロジェクトにしたので、拡張コマンドを使用するために必要なソリューションに簡単に追加でき、さらに WPF コントロールを簡単に追加して、ICommandSource インターフェイスをサポートするように拡張できました。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WpfCommandControlsLibrary
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfCommandControlsLibrary"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:WpfCommandControlsLibrary;assembly=WpfCommandControlsLibrary"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:CustomControl1/>
///
/// </summary>
public class CommandComboBox : ComboBox, ICommandSource
{
public CommandComboBox() : base()
{
}
#region Dependency Properties
// Make Command a dependency property so it can use databinding.
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(CommandComboBox),
new PropertyMetadata((ICommand)null,
new PropertyChangedCallback(CommandChanged)));
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
// Make CommandTarget a dependency property so it can use databinding.
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register(
"CommandTarget",
typeof(IInputElement),
typeof(CommandComboBox),
new PropertyMetadata((IInputElement)null));
public IInputElement CommandTarget
{
get
{
return (IInputElement)GetValue(CommandTargetProperty);
}
set
{
SetValue(CommandTargetProperty, value);
}
}
// Make CommandParameter a dependency property so it can use databinding.
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(CommandComboBox),
new PropertyMetadata((object)null));
public object CommandParameter
{
get
{
return (object)GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
}
#endregion
// Command dependency property change callback.
private static void CommandChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
CommandComboBox cb = (CommandComboBox)d;
cb.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
}
// Add a new command to the Command Property.
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
// If oldCommand is not null, then we need to remove the handlers.
if (oldCommand != null)
{
RemoveCommand(oldCommand, newCommand);
}
AddCommand(oldCommand, newCommand);
}
// Remove an old command from the Command Property.
private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = CanExecuteChanged;
oldCommand.CanExecuteChanged -= handler;
}
// Add the command.
private void AddCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = new EventHandler(CanExecuteChanged);
canExecuteChangedHandler = handler;
if (newCommand != null)
{
newCommand.CanExecuteChanged += canExecuteChangedHandler;
}
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (this.Command != null)
{
RoutedCommand command = this.Command as RoutedCommand;
// If a RoutedCommand.
if (command != null)
{
if (command.CanExecute(CommandParameter, CommandTarget))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
// If a not RoutedCommand.
else
{
if (Command.CanExecute(CommandParameter))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
}
}
// If Command is defined, selecting a combo box item will invoke the command;
// Otherwise, combo box will behave normally.
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
if (this.Command != null)
{
RoutedCommand command = Command as RoutedCommand;
if (command != null)
{
command.Execute(CommandParameter, CommandTarget);
}
else
{
((ICommand)Command).Execute(CommandParameter);
}
}
}
// Keep a copy of the handler so it doesn't get garbage collected.
private static EventHandler canExecuteChangedHandler;
}
}