以我的經驗來說,要讓TemplatedControl支持Command的需求不會很多,大部分情況用附加屬性解決這個需求會更便利些,譬如UWPCommunityToolkit的 "HyperlinkExtensions" 。 如果正在從頭設計自定義控制項並真的需要提供命令支持,可以參考這篇文章。支持Co ...
以我的經驗來說,要讓TemplatedControl支持Command的需求不會很多,大部分情況用附加屬性解決這個需求會更便利些,譬如UWPCommunityToolkit的HyperlinkExtensions。
如果正在從頭設計自定義控制項並真的需要提供命令支持,可以參考這篇文章。支持Command的步驟比較簡單,所以這篇文章比較簡短。
要實現Command支持,控制項中要執行如下步驟:
- 定義Command和CommandParameter屬性。
- 監視Command的CanExecuteChanged事件。
- 在CanExecuteChanged的事件處理函數及CommandParameter的PropertyChangedCallback中,根據Command.CanExecute(CommandParameter)的結果設置控制項的IsEnabled屬性。
- 在某個事件(Click或者ValueChanged)中執行Command。
MenuItem是實現了Command支持的示例,重載了OnPointerPressed並且在其中執行Command:
public class MenuItem : Control
{
/// <summary>
/// 標識 Command 依賴屬性。
/// </summary>
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(MenuItem), new PropertyMetadata(null, OnCommandChanged));
private static void OnCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
MenuItem target = obj as MenuItem;
ICommand oldValue = (ICommand)args.OldValue;
ICommand newValue = (ICommand)args.NewValue;
if (oldValue != newValue)
target.OnCommandChanged(oldValue, newValue);
}
/// <summary>
/// 標識 CommandParameter 依賴屬性。
/// </summary>
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(MenuItem), new PropertyMetadata(null, OnCommandParameterChanged));
private static void OnCommandParameterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
MenuItem target = obj as MenuItem;
object oldValue = (object)args.OldValue;
object newValue = (object)args.NewValue;
if (oldValue != newValue)
target.OnCommandParameterChanged(oldValue, newValue);
}
public MenuItem()
{
this.DefaultStyleKey = typeof(MenuItem);
}
public event RoutedEventHandler Click;
/// <summary>
/// 獲取或設置Command的值
/// </summary>
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
/// <summary>
/// 獲取或設置CommandParameter的值
/// </summary>
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
protected virtual void OnCommandParameterChanged(object oldValue, object newValue)
{
UpdateIsEnabled();
}
protected virtual void OnCommandChanged(ICommand oldValue, ICommand newValue)
{
if (oldValue != null)
oldValue.CanExecuteChanged -= OnCanExecuteChanged;
if (newValue != null)
newValue.CanExecuteChanged += OnCanExecuteChanged;
UpdateIsEnabled();
}
protected virtual void UpdateVisualState(bool useTransitions)
{
if (IsEnabled)
{
VisualStateManager.GoToState(this, "Normal", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Disabled", useTransitions);
}
}
protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
base.OnPointerPressed(e);
Click?.Invoke(this, new RoutedEventArgs());
if ((null != Command) && Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
private void OnCanExecuteChanged(object sender, EventArgs e)
{
UpdateIsEnabled();
}
private void UpdateIsEnabled()
{
IsEnabled = (null == Command) || Command.CanExecute(CommandParameter);
UpdateVisualState(true);
}
}
以下是使用示例,作用是當TextBox的Text不為空時可以點擊MenuItem,並且將Text作為MessageDialog的內容輸出:
<StackPanel>
<TextBox x:Name="TextElement"/>
<local:MenuItem Command="{Binding}" CommandParameter="{Binding ElementName=TextElement,Path=Text}"/>
</StackPanel>
public MenuItemSamplePage()
{
this.InitializeComponent();
var command = new DelegateCommand<object>(Click, CanExecute);
this.DataContext = command;
}
private void Click(object parameter)
{
MessageDialog dialog = new MessageDialog(parameter.ToString());
dialog.ShowAsync();
}
private bool CanExecute(object parameter)
{
string text = parameter as string;
return string.IsNullOrWhiteSpace(text) == false;
}
這裡用到的DelegateCommand也是UWPCommunityToolkit中的類 :DelegateCommand