2011-03-21 51 views
2

我已經定義了一個控制模板/風格對我的標籤項目如下:然而TabItem的標題點擊

<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}"> 
     <Setter Property="Header" Value="{Binding Content.DataContext.Header, RelativeSource={RelativeSource Self}}" /> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type TabItem}">      
        <Grid Width="Auto" Height="Auto" x:Name="TabItemRoot" Margin="10,0,10,0">      
         <Button Command="{Binding Content.DataContext.HeaderClickedCommand}"> 
          <ContentPresenter Margin="13,5,13,5" 
               x:Name="Content" 
               ContentSource="Header" 
               RecognizesAccessKey="True"> 
          </ContentPresenter> 
         </Button> 
        </Grid> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 

當我在選項卡上單擊頁眉綁定按鈕調用的命令行,單擊事件似乎已經吃掉了SelectionChanged事件,因此標籤頁不會改變。

有沒有更好的方法來實現這個,所以我可以調用VM方法,並仍然得到標籤頁來改變?

更新爲根據註釋

的想法是,如果用戶點擊當前活動標籤的標題是通過更新在VM的變化主動標籤的內容。謝謝

+1

你想要一個實際的按鈕作爲tabheader,或者你只是想執行一個命令tabchange嗎? – 2011-03-21 18:32:56

+0

嗨,我打算在標題中放一個按鈕。這個想法是,如果用戶單擊標題,它會通過虛擬機中的更改來更新標籤內容。謝謝 – Sidebp 2011-03-21 19:58:44

+0

該按鈕對此功能不太好,最好使用MouseLeftButtonDown事件。我的答案必須幫助你理解我的意思。 – vorrtex 2011-03-21 20:50:28

回答

4

這是我實現這種功能的方式,但是不管它好不好 - 這是品味的問題。

主要XAML看起來如此:

<TabControl ItemsSource="{Binding TabItems}"> 
     <TabControl.ItemTemplate> 
      <DataTemplate> 
       <Grid> 
        <TextBlock Text="{Binding Title}"/> 
        <i:Interaction.Triggers> 
         <i:EventTrigger EventName="MouseLeftButtonDown"> 
          <local:ExecuteCommandAction Command="{Binding HeaderClickCommand}"/> 
         </i:EventTrigger> 
        </i:Interaction.Triggers> 
       </Grid> 
      </DataTemplate> 
     </TabControl.ItemTemplate> 
    </TabControl> 

沒有ControlTemplate,只是DataTemplate與附加屬性Interaction.Triggers,其中前綴i在這個字符串的定義:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

這個庫可以在MS Blend SDK或庫mvvm燈(在codeplex上)中找到。

此外,不要混淆EventTriggers具有前綴i和通用EventTriggers,他們在某一點不同,但我不知道兩者的區別是什麼,除了從自定義庫工作EventTriggerSilverlight

在我的示例中,觸發器訂閱了事件MouseLeftButtonDown,並在每次引發事件時調用特殊操作類。 這個動作類是一個自定義類,它是在代碼中定義:

/// <summary> 
/// Behaviour helps to bind any RoutedEvent of UIElement to Command. 
/// </summary> 
[DefaultTrigger(typeof(UIElement), typeof(System.Windows.Interactivity.EventTrigger), "MouseLeftButtonDown")] 
public class ExecuteCommandAction : TargetedTriggerAction<UIElement> 
{ 
    /// <summary> 
    /// Dependency property represents the Command of the behaviour. 
    /// </summary> 
    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", 
         typeof(object), typeof(ExecuteCommandAction), new FrameworkPropertyMetadata(null)); 

    /// <summary> 
    /// Dependency property represents the Command parameter of the behaviour. 
    /// </summary> 
    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", 
         typeof(ICommand), typeof(ExecuteCommandAction), new FrameworkPropertyMetadata(null)); 

    /// <summary> 
    /// Gets or sets the Commmand. 
    /// </summary> 
    public ICommand Command 
    { 
     get 
     { 
      return (ICommand)this.GetValue(CommandProperty); 
     } 
     set 
     { 
      this.SetValue(CommandProperty, value); 
     } 
    } 

    /// <summary> 
    /// Gets or sets the CommandParameter. 
    /// </summary> 
    public object CommandParameter 
    { 
     get 
     { 
      return this.GetValue(CommandParameterProperty); 
     } 
     set 
     { 
      this.SetValue(CommandParameterProperty, value); 
     } 
    } 

    /// <summary> 
    /// Invoke method is called when the given routed event is fired. 
    /// </summary> 
    /// <param name="parameter"> 
    /// Parameter is the sender of the event. 
    /// </param> 
    protected override void Invoke(object parameter) 
    { 
     if (this.Command != null) 
     { 
      if (this.Command.CanExecute(this.CommandParameter)) 
      { 
       this.Command.Execute(this.CommandParameter); 
      } 
     } 
    } 
} 

這是所有。現在該命令不會阻止tabcontrol選擇項目。 代碼隱藏來測試此XAML:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     var items = new ObservableCollection<TabItemViewModel> 
     { 
      new TabItemViewModel("Item 1"), new TabItemViewModel("Item 2"), new TabItemViewModel("Item 3") 
     }; 
     this.DataContext = new MainViewModel(){TabItems = items}; 
    } 


} 

public class MainViewModel 
{ 
    public ObservableCollection<TabItemViewModel> TabItems { get; set; } 
} 

public class TabItemViewModel 
{ 
    public TabItemViewModel(string title) 
    { 
     this.Title = title; 
     this.HeaderClickCommand = new RelayCommand(() => MessageBox.Show("Clicked "+this.Title)); 
    } 
    public string Title { get; set; } 
    public RelayCommand HeaderClickCommand { get; set; } 
} 

要呼叫選擇了一個項目,只有當該命令,改變此代碼:

<local:ExecuteCommandAction Command="{Binding HeaderClickCommand}" 
    CommandParameter="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType=TabItem}}"/> 

而這個(第二個參數是CanExecute代表,它支票IsSelected == true):

this.HeaderClickCommand = new RelayCommand<bool>(b => {/*???*/}, b => b == true); 
+0

工作得很好!謝謝。我想嘗試和實現的唯一的補充是僅從激活選項卡中激發命令,即當selectionchanged被激發時也不會觸發命令(我可能只是將目標作爲命令參數傳遞)。我是否認爲Expression庫中有一個ExecuteCommand行爲(或者它可能是MVVMlight)? – Sidebp 2011-03-21 22:13:01

+0

@Sidebp我編輯了我的答案,並在最後添加了兩個代碼塊,現在只有在選擇了選項卡項目時纔可以執行該命令。 Expression庫只包含基類。 MVVM Light包含類似的EventToCommand類,可能最好使用現有的類,但是我以不同的方式完成了它,並且對於更改已經太晚了。怎麼樣ExecuteCommandBehavior,我找不到一個這個名字的類,也許你的意思是不同的。 – vorrtex 2011-03-21 22:34:38

+1

另外,IsSelected屬性可以綁定到ViewModel的一個屬性,並且它將允許執行不帶參數的命令。 – vorrtex 2011-03-21 22:39:16