2010-01-22 7 views
2

J'ai un datatemplate pour un itemtemplate d'un tabcontrol comme suit;Commande WPF datatemplate

<DataTemplate x:Key="TabItemTemplate"> 
     <DockPanel Width="120"> 
      <Button 
    Command="{Binding Path=DataContext.DeleteTimeTableCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" 
    Content="X" 
    Cursor="Hand" 
    DockPanel.Dock="Right" 
    Focusable="False" 
    Margin="0,1,0,0" 
    Padding="0" 
    VerticalContentAlignment="Bottom" 
    Width="16" Height="16" />    

Ceci est OK car il me donne un bouton dans la tabcontrol pour permettre la suppression de la TabItem actuelle.

Problème Je suis en train de constater que la commande Delete à laquelle je suis lié possède une méthode canExecute qui met à jour tous les boutons de tous les onglets du tabcontrol. Je veux juste que l'onglet actuel soit affecté.

J'ai la propriété CanDelete que je veux inclure dans ma commande. J'essaie de trouver un bon exemple sur CommandParameters car je pense que c'est la façon dont je dois aller.

Quelqu'un at-il une bonne suggestion pour la meilleure façon de le faire?

Merci.

Répondre

3

Je doute que vous ayez encore besoin d'aide pour cela, mais je me suis dit que je répondrais de toute façon.

La façon dont je l'ai fait dans le passé est de faire de la collection d'éléments que vous liez à votre TabControl une collection d'objets ViewModel simples. De cette façon, vous pouvez implémenter la logique CanXXX pour chacun des onglets au lieu du contrôle TabControl ou de la vue dans son ensemble.

Dans cet exemple, j'utilise la classe RelayCommand qui est affichée dans Josh Smith's MVVM article.

MainViewModel.cs

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Linq; 
using System.Text; 

namespace TabBinding.ViewModels 
{ 
    class MainViewModel : ViewModelBase 
    { 
     private ObservableCollection<TabViewModel> _Tabs; 
     public ObservableCollection<TabViewModel> Tabs 
     { 
      get { return _Tabs; } 
      set 
      { 
       _Tabs = value; 
       OnPropertyChanged(this, "Tabs"); 
      } 
     } 

     public MainViewModel() 
     { 
      var tabs = new ObservableCollection<TabViewModel>(); 
      tabs.Add(new TabViewModel() { TabHeader = "Tab1", Content="Content For Tab1" }); 
      tabs.Add(new TabViewModel() { TabHeader = "Tab2", Content = "Content For Tab2" }); 
      tabs.Add(new TabViewModel() { TabHeader = "Tab3", Content = "Content For Tab3" }); 
      tabs.Add(new TabViewModel() { TabHeader = "Tab4", Content = "Content For Tab4" }); 
      Tabs = tabs; 
     } 
    } 
} 

TabViewModel.cs

using System.Windows.Input; 
using System.Windows; 

namespace TabBinding.ViewModels 
{ 
    class TabViewModel : ViewModelBase 
    { 
     RelayCommand _CloseTabCommand; 

     private string _TabHeader; 
     public string TabHeader 
     { 
      get { return _TabHeader; } 
      set 
      { 
       _TabHeader = value; 
       OnPropertyChanged(this, "TabHeader"); 
      } 
     } 

     private string _Content; 
     public string Content 
     { 
      get { return _Content; } 
      set 
      { 
       _Content = value; 
       OnPropertyChanged(this, "Content"); 
      } 
     } 

     public ICommand CloseTabCommand 
     { 
      get 
      { 
       if (_CloseTabCommand == null) 
       { 
        _CloseTabCommand = new RelayCommand(
         param => this.CloseTab(), 
         param => this.CanCloseTab 
         ); 
       } 
       return _CloseTabCommand; 
      } 
     } 

     public void CloseTab() 
     { 
      MessageBox.Show("Close Me!"); 
     } 

     bool CanCloseTab 
     { 
      get { return (TabHeader == "Tab2" || TabHeader == "Tab4"); } 
     } 
    } 

} 

ViewModelBase.cs

using System.ComponentModel; 

namespace TabBinding.ViewModels 
{ 
    class ViewModelBase : INotifyPropertyChanged 
    { 
     public event PropertyChangedEventHandler PropertyChanged; 

     protected void OnPropertyChanged(object sender, string propertyName) 
     { 
      if (this.PropertyChanged != null) 
      { 
       PropertyChanged(sender, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
    } 
} 

RelayCommand.cs

using System; 
using System.Diagnostics; 
using System.Windows.Input; 

namespace TabBinding 
{ 
    /// <summary> 
    /// A command whose sole purpose is to 
    /// relay its functionality to other 
    /// objects by invoking delegates. The 
    /// default return value for the CanExecute 
    /// method is 'true'. 
    /// </summary> 
    public class RelayCommand : ICommand 
    { 
     #region Fields 

     readonly Action<object> _execute; 
     readonly Predicate<object> _canExecute;   

     #endregion // Fields 

     #region Constructors 

     /// <summary> 
     /// Creates a new command that can always execute. 
     /// </summary> 
     /// <param name="execute">The execution logic.</param> 
     public RelayCommand(Action<object> execute) 
      : this(execute, null) 
     { 
     } 

     /// <summary> 
     /// Creates a new command. 
     /// </summary> 
     /// <param name="execute">The execution logic.</param> 
     /// <param name="canExecute">The execution status logic.</param> 
     public RelayCommand(Action<object> execute, Predicate<object> canExecute) 
     { 
      if (execute == null) 
       throw new ArgumentNullException("execute"); 

      _execute = execute; 
      _canExecute = canExecute;   
     } 

     #endregion // Constructors 

     #region ICommand Members 

     [DebuggerStepThrough] 
     public bool CanExecute(object parameter) 
     { 
      return _canExecute == null ? true : _canExecute(parameter); 
     } 

     public event EventHandler CanExecuteChanged 
     { 
      add { CommandManager.RequerySuggested += value; } 
      remove { CommandManager.RequerySuggested -= value; } 
     } 

     public void Execute(object parameter) 
     { 
      _execute(parameter); 
     } 

     #endregion // ICommand Members 
    } 
} 

MainWindow.xaml

<Window x:Class="TabBinding.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:vm="clr-namespace:TabBinding.ViewModels" 
     Title="MainWindow" Height="360" Width="550"> 
    <Window.Resources> 
     <vm:MainViewModel x:Key="Data" /> 
    </Window.Resources> 
    <Grid DataContext="{StaticResource Data}"> 
     <TabControl 
      HorizontalAlignment="Left" 
      VerticalAlignment="Top" 
      Margin="10,10,10,10" 
      Width="500" 
      Height="300" 
      ItemsSource="{Binding Tabs}"> 
      <TabControl.ItemContainerStyle> 
       <Style TargetType="TabItem"> 
        <Setter Property="HeaderTemplate"> 
         <Setter.Value> 
          <DataTemplate> 
           <StackPanel Orientation="Horizontal"> 
            <Button Content="X" Margin="0,0,10,0" Command="{Binding CloseTabCommand}" /> 
            <TextBlock Text="{Binding TabHeader}"/> 
           </StackPanel> 
          </DataTemplate> 
         </Setter.Value> 
        </Setter> 
        <Setter Property="Content" Value="{Binding Content}"/> 
       </Style> 
      </TabControl.ItemContainerStyle> 
     </TabControl> 
    </Grid> 
</Window> 

App.xaml

<Application x:Class="TabBinding.App" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      StartupUri="Views/MainWindow.xaml"> 
    <Application.Resources> 

    </Application.Resources> 
</Application> 
0

Si quelqu'un est toujours intéressé à la réponse, vous pouvez utiliser la CommandParameter extension de liaison pour passer le modèle actuel.

<Button Command="{Binding Path=DataContext.DeleteTimeTableCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" CommandParameter="{Binding}" /> 

L'objet transmis sera le DataContext de l'élément d'onglet. La solution nécessite l'implémentation ICommand pour gérer correctement le paramètre donné (casting, etc.). En outre, l'événement RequerySuggested doit être déclenché après toute modification sur le modèle, car WPF ne peut pas déterminer quand actualiser les méthodes CanExecute sur les onglets. Une autre chose à garder à l'esprit lorsque vous utilisez des modèles de programmation asynchrones est de déclencher l'événement d'actualisation à partir du thread d'interface utilisateur uniquement. Rien ne va se passer autrement.