我试图实施这个问题:Closing TabItem by clicking middle button
但是,CloseCommandExecuted()的e.Source属性返回一个TabControl对象。这不能用于确定单击了哪个TabItem。 e.OriginalSource返回在datatemplate内部定义的Grid。最后,从原始源向上的父母后面永远不会导致TabItem对象。
如何获取用户点击的TabItem?
编辑:在我的情况下,我通过ItemsSource绑定对象。
//Xaml for the Window
<Window.Resources>
<DataTemplate x:Key="closableTabTemplate">
<Border x:Name="testBorder">
<Grid>
<Grid.InputBindings>
<MouseBinding Command="ApplicationCommands.Close" Gesture="MiddleClick" />
</Grid.InputBindings>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid>
<TextBlock Text="{Binding Headertext}"></TextBlock>
</Grid>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close" Executed="CloseCommandExecuted" CanExecute="CloseCommandCanExecute" />
</Window.CommandBindings>
<Grid>
<TabControl x:Name="MainTabControl" ItemTemplate="{StaticResource closableTabTemplate}" Margin="10">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<!--<Setter Property="Header" Value="{Binding ModelName}"/>-->
<Setter Property="Content" Value="{Binding Content}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</Grid>
我绑定的对象。它是针对此问题的特定样本
public class TabContent
{
public string Headertext { get; set; }
public FrameworkElement Content = null;
}
主窗口代码
public partial class MainWindow
{
public ObservableCollection<TabContent> MyCollection = new ObservableCollection<TabContent>();
public MainWindow()
{
MyCollection.Add(new TabContent { Headertext = "item1" });
MyCollection.Add(new TabContent { Headertext = "item2" });
MyCollection.Add(new TabContent { Headertext = "item3" });
InitializeComponent();
MainTabControl.ItemsSource = MyCollection;
MainTabControl.SelectedIndex = 0;
}
private void CloseCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
//Need some way to access the tab item or item bound to tab item
//if (tabitem != null)
//{
// //MainTabControl.Items.Remove(tabitem);
//}
}
private void CloseCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
答案 0 :(得分:2)
根据您添加到问题中的代码,您可以执行以下操作:
private void CloseCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
DependencyObject dep = (DependencyObject)e.OriginalSource;
// Traverse the visual tree looking for TabItem
while ((dep != null) && !(dep is TabItem))
dep = VisualTreeHelper.GetParent(dep);
if (dep == null)
{
// Didn't find TabItem
return;
}
TabItem tabitem = dep as TabItem;
if (tabitem != null)
{
TabContent content = tabitem.Header as TabContent;
if(content !=null)
MyCollection.Remove(content);
}
}
它为您提供了它所绑定的TabItem
和TabContent
对象。
答案 1 :(得分:0)
我只是将这个样本放在xaml中,空TabControl
<TabControl x:Name="MyTabControl">
</TabControl>
假设您在运行时加载TabItem
个对象,我将其添加到Window
public MainWindow()
{
InitializeComponent();
// Add some sample tabs to the tab control
for (int i = 0; i < 5; i++)
{
TabItem ti = new TabItem() { Header = String.Format("Tab {0}", i + 1) };
ti.PreviewMouseDown += ti_PreviewMouseDown;
MyTabControl.Items.Add(ti);
}
}
或如果您想在xaml中定义标签
<TabControl x:Name="MyTabControl">
<TabItem Header="Tab 1" PreviewMouseDown="ti_PreviewMouseDown"></TabItem>
<TabItem Header="Tab 2" PreviewMouseDown="ti_PreviewMouseDown"></TabItem>
<TabItem Header="Tab 3" PreviewMouseDown="ti_PreviewMouseDown"></TabItem>
<TabItem Header="Tab 4" PreviewMouseDown="ti_PreviewMouseDown"></TabItem>
<TabItem Header="Tab 5" PreviewMouseDown="ti_PreviewMouseDown"></TabItem>
</TabControl>
然后在事件处理程序
中 void ti_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
TabItem clickedTabItem = sender as TabItem;
if (clickedTabItem != null)
{
if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed)
{
// Do whatever you want to do with clickedTabItem here, I'm removing it from the TabControl
MyTabControl.Items.Remove(clickedTabItem);
}
}
}
答案 2 :(得分:0)
我认为MVVM这样做的方法是为每个标签添加一个按钮,当单击它时,会触发一个命令来关闭该标签或其他任何标签。例如,这是Chrome关闭标签时的工作方式。这不是您要求的“中间点击”解决方案,但可以通过一些额外的工作进行修改以实现这种方式。
无论如何,您可以向TabContent
类添加命令属性:
public class TabContent
{
...
public RelayCommand CloseTabCommand { get; private set; }
public TabContent()
{
CloseTabCommand = new RelayCommand(OnTabClosing);
}
public event EventHandler TabClosing;
void OnTabClosing()
{
var handler = TabClosing;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
这里我使用的是RelayCommand
,它只是一个实现ICommand
的类:我确定你已经看过或使用过类似的类型。执行命令时会触发一个事件。创建TabItem
对象时,需要注册以处理此事件:
public MainWindow()
{
MyCollection.Add(new TabContent { Headertext = "item1" });
MyCollection.Add(new TabContent { Headertext = "item2" });
MyCollection.Add(new TabContent { Headertext = "item3" });
...
foreach (TabContent tab in MyCollection)
{
tab.TabClosing += OnTabClosing;
}
}
然后,在事件处理程序中,sender
参数将引用触发事件的TabContent
对象:
void OnTabClosing(object sender, EventArgs e)
{
MyCollection.Remove(sender as TabContent);
}
这里我通过从集合中删除该标签来响应该事件。
在XAML方面,您只需要修改数据模板以添加一个按钮控件,该控件将通过绑定调用自定义命令:
<DataTemplate x:Key="closableTabTemplate">
<Button Command="{Binding CloseTabCommand}">
<TextBlock Text="{Binding Headertext}"></TextBlock>
</Button>
</DataTemplate>
显然,您可以更改按钮的模板以移除边框,使其看起来不像按钮,如果这是您想要的。
从您的角度来看,唯一的问题是按钮只会执行命令以响应单击左键。要使其与中键单击相反,您需要使用其他内容替换button元素(例如,Border
)并使用附加行为来处理鼠标按下事件并相应地执行操作。