我对Windows.UI.Xaml.Controls.ContentDialog
有疑问。我想在System.Windows.Input.ICommand.CanExecute
返回false
时禁用主按钮。但似乎ContentDialog
未订阅ICommand.CanExecuteChanged
事件。我不知道为什么。这是我的代码:
NewTracingDialog.xaml
<ContentDialog
x:Class="RouterTracer.NewTracingDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RouterTracer"
xmlns:model="using:RouterTracer.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="NEW TRACING"
PrimaryButtonText="trace"
SecondaryButtonText="cancel"
PrimaryButtonCommand="{Binding}"
d:DataContext="{d:DesignInstance Type=model:NewTracingViewModel}">
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBox Name="destinationHost" Header="Destination Host" Text="{Binding Host, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="port" Header="UDP Port" InputScope="Number" Text="{Binding Port, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="hopLimit" Header="Hop Limit" InputScope="Number" Text="{Binding HopLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</ContentDialog>
NewTracingViewModel.cs
//-----------------------------------------------------------------------
// <copyright file="NewTracingViewModel.cs">
// Copyright (c) Putta Khunchalee.
// </copyright>
// <author>Putta Khunchalee</author>
//-----------------------------------------------------------------------
namespace RouterTracer.ViewModels
{
using System;
/// <summary>
/// View model for new tracing dialog.
/// </summary>
public sealed class NewTracingViewModel : ExecutableViewModel
{
private byte hopLimit;
private string host;
private ushort port;
/// <summary>
/// Initialize a new instance of the <see cref="NewTracingViewModel"/> class.
/// </summary>
public NewTracingViewModel()
{
}
/// <summary>
/// Gets or sets TTL or Hop Limit.
/// </summary>
/// <value>
/// TTL or Hop Limit.
/// </value>
public byte HopLimit
{
get { return this.hopLimit; }
set { this.SetProperty(ref this.hopLimit, value, "HopLimit"); }
}
/// <summary>
/// Gets or sets destination host.
/// </summary>
/// <value>
/// Hestination host.
/// </value>
public string Host
{
get { return this.host; }
set { this.SetProperty(ref this.host, value, "Host"); }
}
/// <summary>
/// Gets or sets destination port.
/// </summary>
/// <value>
/// Destination port.
/// </value>
public ushort Port
{
get { return this.port; }
set { this.SetProperty(ref this.port, value, "Port"); }
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to <c>null</c>.
/// </param>
public override void Execute(object parameter)
{
// This method get execute normally.
}
/// <summary>
/// Validate all properties to determine executable status.
/// </summary>
/// <returns>
/// <c>true</c> if instance can be execute; otherwise <c>false</c>.
/// </returns>
protected override bool ValidateProperties()
{
if (this.hopLimit == 0)
{
return false;
}
if (string.IsNullOrWhiteSpace(this.host))
{
return false;
}
if (this.port == 0)
{
return false;
}
return true;
}
}
}
ExecutableViewModel.cs
//-----------------------------------------------------------------------
// <copyright file="ExecutableViewModel.cs">
// Copyright (c) Putta Khunchalee.
// </copyright>
// <author>Putta Khunchalee</author>
//-----------------------------------------------------------------------
namespace RouterTracer.ViewModels
{
using System;
using System.Windows.Input;
/// <summary>
/// Base class for all View Model that executable via <see cref="ICommand"/>.
/// </summary>
public abstract class ExecutableViewModel : ViewModel, ICommand
{
private bool canExecute;
/// <summary>
/// Initialize a new instance of the <see cref="ExecutableViewModel"/> class.
/// </summary>
protected ExecutableViewModel()
{
}
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to <c>null</c>.
/// </param>
/// <returns>
/// <c>true</c> if this command can be executed; otherwise, <c>false</c>.
/// </returns>
public bool CanExecute(object parameter)
{
return this.canExecute;
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to <c>null</c>.
/// </param>
public abstract void Execute(object parameter);
/// <summary>
/// Invoked when status of <see cref="CanExecute(object)"/> has changed.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
var handlers = this.CanExecuteChanged;
if (handlers != null)
{
handlers(this, EventArgs.Empty);
}
}
/// <summary>
/// Invoked when value of model's property has been changed.
/// </summary>
/// <param name="name">
/// Name of the property that value has changed.
/// </param>
protected override void OnPropertyChanged(string name)
{
base.OnPropertyChanged(name);
this.UpdateCanExecuteFlag(ValidateProperties());
}
/// <summary>
/// Validate all properties to determine executable status.
/// </summary>
/// <returns>
/// <c>true</c> if instance can be execute; otherwise <c>false</c>.
/// </returns>
protected abstract bool ValidateProperties();
/// <summary>
/// Update status of <see cref="CanExecute(object)"/>.
/// </summary>
/// <param name="canExecute">
/// <c>true</c> if instance can be execute; otherwise <c>false</c>.
/// </param>
private void UpdateCanExecuteFlag(bool canExecute)
{
if (this.canExecute == canExecute)
{
return;
}
this.canExecute = canExecute;
this.OnCanExecuteChanged();
}
}
}
ViewModel.cs
//-----------------------------------------------------------------------
// <copyright file="ViewModel.cs">
// Copyright (c) Putta Khunchalee.
// </copyright>
// <author>Putta Khunchalee</author>
//-----------------------------------------------------------------------
namespace RouterTracer.ViewModels
{
using System.ComponentModel;
/// <summary>
/// Base class for all View Model.
/// </summary>
public abstract class ViewModel : INotifyPropertyChanged
{
/// <summary>
/// Initialize a new instance of the <see cref="ViewModel"/> class.
/// </summary>
protected ViewModel()
{
}
/// <summary>
/// Raise when value of model's property has been changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Invoked when value of model's property has been changed.
/// </summary>
/// <param name="name">
/// Name of the property that value has changed.
/// </param>
protected virtual void OnPropertyChanged(string name)
{
var handlers = this.PropertyChanged;
if (handlers != null)
{
handlers(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Change the value of the field that is property backed.
/// </summary>
/// <typeparam name="T">
/// Type of the field.
/// </typeparam>
/// <param name="field">
/// Field to change value.
/// </param>
/// <param name="value">
/// The new value.
/// </param>
/// <param name="name">
/// Name of backed property.
/// </param>
/// <returns>
/// <c>true</c> when value has been changed; otherwise <c>false</c>.
/// </returns>
protected bool SetProperty<T>(ref T field, T value, string name)
{
T previousValue;
// Check to see if value change is neccessary.
if (object.Equals(field, value))
{
return false;
}
// Change value.
previousValue = field;
field = value;
this.OnPropertyChanged(name);
return true;
}
}
}
很抱歉长代码。谢谢。
答案 0 :(得分:0)
我已放弃并使用变通方法。也许它还没有在ContentDialog
中实现。这是我的解决方法。
<强> NewTracingDialog.xaml.cs 强>
//-----------------------------------------------------------------------
// <copyright file="NewTracingDialog.xaml.cs">
// Copyright (c) Putta Khunchalee.
// </copyright>
// <author>Putta Khunchalee</author>
//-----------------------------------------------------------------------
namespace RouterTracer
{
using System;
using System.Windows.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
/// <summary>
/// Dialog for gather tracing information from user.
/// </summary>
public sealed partial class NewTracingDialog : ContentDialog
{
/// <summary>
/// Used to remove <see cref="OnDataContextCanExecuteChanged(object, EventArgs)"/> before
/// changing to the new data context.
/// </summary>
private ICommand currentDataContext;
/// <summary>
/// Initialize a new instance of the <see cref="NewTracingDialog"/> class.
/// </summary>
public NewTracingDialog()
{
this.InitializeComponent();
this.DataContextChanged += this.OnDataContextChanged;
}
/// <summary>
/// Invoke when executable flag of data context has changed.
/// </summary>
/// <param name="sender">
/// Data context that executable flag has changed.
/// </param>
/// <param name="e">
/// The empty <see cref="EventArgs"/>.
/// </param>
private void OnDataContextCanExecuteChanged(object sender, EventArgs e)
{
this.IsPrimaryButtonEnabled = ((ICommand)sender).CanExecute(null);
}
/// <summary>
/// Invoke when data context has been changed.
/// </summary>
/// <param name="sender">
/// The instance of <see cref="NewTracingDialog"/> that data context has changed.
/// </param>
/// <param name="args">
/// Event informations.
/// </param>
private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
// Get a new data context.
var command = (ICommand)args.NewValue;
if (command == null)
{
return;
}
// Disable primary button if data context is not executable.
this.IsPrimaryButtonEnabled = command.CanExecute(null);
// Subscribe to data context executable changed event.
if (this.currentDataContext != null)
{
this.currentDataContext.CanExecuteChanged -= this.OnDataContextCanExecuteChanged;
}
command.CanExecuteChanged += this.OnDataContextCanExecuteChanged;
this.currentDataContext = command;
}
}
}