2010-01-22 3 views
2

다음과 같이 tabcontrol의 itemtemplate에 대한 데이터 형식이 있습니다.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" />    

현재 tabitem을 삭제할 수 있도록 tabcontrol의 버튼을 제공하므로이 기능을 사용해도됩니다.

문제 나는 바인딩 할 Delete 명령이 tabcontrol의 모든 탭에서 모든 단추를 업데이트하는 canExecute 메서드를 가지고 있다는 것입니다. 난 단지 현재 탭 영향을 원한다.

내 명령에 포함시키고 자하는 CanDelete 속성이 있습니다. CommandParameters에 대한 좋은 예를 찾으려고 노력 중입니다. 이것이 제가해야 할 길입니다.

누구나 최선의 방법을 제안 했습니까?

감사합니다.

답변

3

나는 아직도 당신이 이것에 대한 도움이 필요하다고 생각하지 않지만, 어쨌든 나는 그것을 대답하는 데 균열이 있다고 생각했다.

이전에 해본 방법은 TabControl에 바인딩하는 항목의 컬렉션을 간단한 ViewModel 개체의 컬렉션으로 만드는 것입니다. 그렇게하면 TabControl 또는 뷰 전체 대신 탭 각각에 대해 CanXXX 논리를 구현할 수 있습니다.

이 예제에서는 Josh Smith's MVVM article에 표시된 RelayCommand 클래스를 사용하고 있습니다.

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

사람이 대답에 대해 여전히 관심이 있다면, 당신은 CommandParameter을 사용할 수 있습니다 현재 모델을 전달하기위한 바인딩 확장.

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

전달 된 개체는 탭 항목의 DataContext가 될 것입니다. 이 솔루션은 ICommand 구현이 주어진 매개 변수를 적절하게 처리해야합니다 (캐스팅 등). 또한 WPF는 탭에서 CanExecute 메서드를 언제 다시 쿼리해야하는지 알 수 없으므로 RequerySuggested 이벤트는 모델에서 수정 한 후에 발생해야합니다. 비동기 프로그래밍 모델을 사용할 때 염두에 두어야 할 또 다른 사항은 UI 스레드에서만 새로 고침 이벤트를 발생시키는 것입니다. 그렇지 않으면 아무것도 일어나지 않을 것입니다.