2011-03-21 53 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