2014-09-13 62 views
1

我有一个Windows.UI.Xaml.Controls.ContentDialog的问题。当System.Windows.Input.ICommand.CanExecute返回false时,我想禁用它的主键。但它似乎ContentDialog不订阅ICommand.CanExecuteChanged事件。我不知道为什么。这是我的代码: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

我已经放弃并改用解决方法。也许它还没有在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; 
     } 
    } 
}