ApplicationCommands.Delete 的 TabControl 实现
TabControl implementation of ApplicationCommands.Delete
我正在尝试扩展 TabControl 以便我可以添加和删除项目,我之前通过向我的视图模型添加一个关闭命令来完成此操作,该命令引发事件并且 parent 视图模型中的订阅将被删除collection.
中的项目
我想让这种方法更通用,并且正在尝试实施 ApplicationCommands.Delete 命令。
ExtendedTabControl.cs
public class ExtendedTabControl : TabControl
{
public static readonly DependencyProperty CanUserDeleteTabsProperty = DependencyProperty.Register("CanUserDeleteTabs", typeof(bool), typeof(ExtendedTabControl), new PropertyMetadata(true, OnCanUserDeleteTabsChanged, OnCoerceCanUserDeleteTabs));
public bool CanUserDeleteTabs
{
get { return (bool)GetValue(CanUserDeleteTabsProperty); }
set { SetValue(CanUserDeleteTabsProperty, value); }
}
public static RoutedUICommand DeleteCommand
{
get { return ApplicationCommands.Delete; }
}
private IEditableCollectionView EditableItems
{
get { return (IEditableCollectionView)Items; }
}
private bool ItemIsSelected
{
get
{
if (this.SelectedItem != CollectionView.NewItemPlaceholder)
return true;
return false;
}
}
private static void OnCanExecuteDelete(object sender, CanExecuteRoutedEventArgs e)
{
((WorkspacesTabControl)sender).OnCanExecuteDelete(e);
}
private static void OnCanUserDeleteTabsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// The Delete command needs to have CanExecute run.
CommandManager.InvalidateRequerySuggested();
}
private static object OnCoerceCanUserDeleteTabs(DependencyObject d, object baseValue)
{
return ((WorkspacesTabControl)d).OnCoerceCanUserAddOrDeleteTabs((bool)baseValue, false);
}
private static void OnExecutedDelete(object sender, ExecutedRoutedEventArgs e)
{
((WorkspacesTabControl)sender).OnExecutedDelete(e);
}
static ExtendedTabControl()
{
Type ownerType = typeof(ExtendedTabControl);
DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(typeof(ExtendedTabControl)));
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(DeleteCommand, new ExecutedRoutedEventHandler(OnExecutedDelete), new CanExecuteRoutedEventHandler(OnCanExecuteDelete)));
}
protected virtual void OnCanExecuteDelete(CanExecuteRoutedEventArgs e)
{
// User is allowed to delete and there is a selection.
e.CanExecute = CanUserDeleteTabs && ItemIsSelected;
e.Handled = true;
}
#endregion
protected virtual void OnExecutedDelete(ExecutedRoutedEventArgs e)
{
if (ItemIsSelected)
{
object currentItem = SelectedItem;
int indexToSelect = Items.IndexOf(currentItem) - 1;
if (currentItem != CollectionView.NewItemPlaceholder)
EditableItems.Remove(currentItem);
// This should focus the row and bring it into view.
SetCurrentValue(SelectedItemProperty, Items[indexToSelect]);
}
e.Handled = true;
}
private bool OnCoerceCanUserAddOrDeleteTabs(bool baseValue, bool canUserAddTabsProperty)
{
// Only when the base value is true do we need to validate
// that the user can actually add or delete rows.
if (baseValue)
{
if (!this.IsEnabled)
{
// Disabled TabControls cannot be modified.
return false;
}
else
{
if ((canUserAddTabsProperty && !this.EditableItems.CanAddNew) || (!canUserAddTabsProperty && !this.EditableItems.CanRemove))
{
// The collection view does not allow the add or delete action.
return false;
}
}
}
return baseValue;
}
}
Generic.xaml
<!-- This template explains how to render a tab item with a close button. -->
<DataTemplate x:Key="CloseableTabItemHeader">
<DockPanel MinWidth="120">
<Button DockPanel.Dock="Right" Command="ApplicationCommands.Delete" Content="X" Cursor="Hand" Focusable="False" FontSize="10" FontWeight="Bold" Height="16" Width="16" />
<TextBlock Padding="0,0,10,0" Text="{Binding DisplayName}" VerticalAlignment="Center" />
</DockPanel>
</DataTemplate>
<Style x:Key="{x:Type local:ExtendedTabControl}" BasedOn="{StaticResource {x:Type TabControl}}" TargetType="{x:Type local:ExtendedTabControl}">
<Setter Property="ItemTemplate" Value="{StaticResource CloseableTabItemHeader}" />
</Style>
这几乎有效,我可以选择一个项目并通过点击关闭按钮或使用删除键将其删除。但是,如果我点击未选中项目的关闭按钮,它仍会删除所选项目。这种行为的原因很明显,但我不确定如何访问正确的 object 以进行删除?我还需要以更好的方式分配在 OnExecutedDelete 中找到的 indexToSelect,尽管我很乐意为此找到解决方案。
ExecutedRoutedEventArgs
有 属性 Parameter
。尝试将 TabItem
的 DataContext
设置为 CommandParameter
:
<DataTemplate x:Key="CloseableTabItemHeader">
<DockPanel MinWidth="120">
<Button DockPanel.Dock="Right" Command="ApplicationCommands.Delete" CommandParameter="{Binding .}" Content="X" Cursor="Hand" Focusable="False" FontSize="10" FontWeight="Bold" Height="16" Width="16" />
<TextBlock Padding="0,0,10,0" Text="{Binding DisplayName}" VerticalAlignment="Center" />
</DockPanel>
然后您可以在OnExecutedDelete
中访问DataContext
:
protected virtual void OnExecutedDelete(ExecutedRoutedEventArgs e)
{
if (ItemIsSelected)
{
object currentItem = e.Parameter ?? SelectedItem;
int indexToSelect = Items.IndexOf(currentItem) - 1;
...
}
e.Handled = true;
}
我刚刚在 http://dragablz.net
中做了非常相似的事情
管理选项卡添加和删除的两个签到:
https://github.com/ButchersBoy/Dragablz/commit/47bbc302f5ffeaa8c234269ab4ff11bc80f7fa10
https://github.com/ButchersBoy/Dragablz/commit/c1dce0435683db83f163a77ccb9a19b3218b3ca7
我正在尝试扩展 TabControl 以便我可以添加和删除项目,我之前通过向我的视图模型添加一个关闭命令来完成此操作,该命令引发事件并且 parent 视图模型中的订阅将被删除collection.
中的项目我想让这种方法更通用,并且正在尝试实施 ApplicationCommands.Delete 命令。
ExtendedTabControl.cs
public class ExtendedTabControl : TabControl
{
public static readonly DependencyProperty CanUserDeleteTabsProperty = DependencyProperty.Register("CanUserDeleteTabs", typeof(bool), typeof(ExtendedTabControl), new PropertyMetadata(true, OnCanUserDeleteTabsChanged, OnCoerceCanUserDeleteTabs));
public bool CanUserDeleteTabs
{
get { return (bool)GetValue(CanUserDeleteTabsProperty); }
set { SetValue(CanUserDeleteTabsProperty, value); }
}
public static RoutedUICommand DeleteCommand
{
get { return ApplicationCommands.Delete; }
}
private IEditableCollectionView EditableItems
{
get { return (IEditableCollectionView)Items; }
}
private bool ItemIsSelected
{
get
{
if (this.SelectedItem != CollectionView.NewItemPlaceholder)
return true;
return false;
}
}
private static void OnCanExecuteDelete(object sender, CanExecuteRoutedEventArgs e)
{
((WorkspacesTabControl)sender).OnCanExecuteDelete(e);
}
private static void OnCanUserDeleteTabsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// The Delete command needs to have CanExecute run.
CommandManager.InvalidateRequerySuggested();
}
private static object OnCoerceCanUserDeleteTabs(DependencyObject d, object baseValue)
{
return ((WorkspacesTabControl)d).OnCoerceCanUserAddOrDeleteTabs((bool)baseValue, false);
}
private static void OnExecutedDelete(object sender, ExecutedRoutedEventArgs e)
{
((WorkspacesTabControl)sender).OnExecutedDelete(e);
}
static ExtendedTabControl()
{
Type ownerType = typeof(ExtendedTabControl);
DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(typeof(ExtendedTabControl)));
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(DeleteCommand, new ExecutedRoutedEventHandler(OnExecutedDelete), new CanExecuteRoutedEventHandler(OnCanExecuteDelete)));
}
protected virtual void OnCanExecuteDelete(CanExecuteRoutedEventArgs e)
{
// User is allowed to delete and there is a selection.
e.CanExecute = CanUserDeleteTabs && ItemIsSelected;
e.Handled = true;
}
#endregion
protected virtual void OnExecutedDelete(ExecutedRoutedEventArgs e)
{
if (ItemIsSelected)
{
object currentItem = SelectedItem;
int indexToSelect = Items.IndexOf(currentItem) - 1;
if (currentItem != CollectionView.NewItemPlaceholder)
EditableItems.Remove(currentItem);
// This should focus the row and bring it into view.
SetCurrentValue(SelectedItemProperty, Items[indexToSelect]);
}
e.Handled = true;
}
private bool OnCoerceCanUserAddOrDeleteTabs(bool baseValue, bool canUserAddTabsProperty)
{
// Only when the base value is true do we need to validate
// that the user can actually add or delete rows.
if (baseValue)
{
if (!this.IsEnabled)
{
// Disabled TabControls cannot be modified.
return false;
}
else
{
if ((canUserAddTabsProperty && !this.EditableItems.CanAddNew) || (!canUserAddTabsProperty && !this.EditableItems.CanRemove))
{
// The collection view does not allow the add or delete action.
return false;
}
}
}
return baseValue;
}
}
Generic.xaml
<!-- This template explains how to render a tab item with a close button. -->
<DataTemplate x:Key="CloseableTabItemHeader">
<DockPanel MinWidth="120">
<Button DockPanel.Dock="Right" Command="ApplicationCommands.Delete" Content="X" Cursor="Hand" Focusable="False" FontSize="10" FontWeight="Bold" Height="16" Width="16" />
<TextBlock Padding="0,0,10,0" Text="{Binding DisplayName}" VerticalAlignment="Center" />
</DockPanel>
</DataTemplate>
<Style x:Key="{x:Type local:ExtendedTabControl}" BasedOn="{StaticResource {x:Type TabControl}}" TargetType="{x:Type local:ExtendedTabControl}">
<Setter Property="ItemTemplate" Value="{StaticResource CloseableTabItemHeader}" />
</Style>
这几乎有效,我可以选择一个项目并通过点击关闭按钮或使用删除键将其删除。但是,如果我点击未选中项目的关闭按钮,它仍会删除所选项目。这种行为的原因很明显,但我不确定如何访问正确的 object 以进行删除?我还需要以更好的方式分配在 OnExecutedDelete 中找到的 indexToSelect,尽管我很乐意为此找到解决方案。
ExecutedRoutedEventArgs
有 属性 Parameter
。尝试将 TabItem
的 DataContext
设置为 CommandParameter
:
<DataTemplate x:Key="CloseableTabItemHeader">
<DockPanel MinWidth="120">
<Button DockPanel.Dock="Right" Command="ApplicationCommands.Delete" CommandParameter="{Binding .}" Content="X" Cursor="Hand" Focusable="False" FontSize="10" FontWeight="Bold" Height="16" Width="16" />
<TextBlock Padding="0,0,10,0" Text="{Binding DisplayName}" VerticalAlignment="Center" />
</DockPanel>
然后您可以在OnExecutedDelete
中访问DataContext
:
protected virtual void OnExecutedDelete(ExecutedRoutedEventArgs e)
{
if (ItemIsSelected)
{
object currentItem = e.Parameter ?? SelectedItem;
int indexToSelect = Items.IndexOf(currentItem) - 1;
...
}
e.Handled = true;
}
我刚刚在 http://dragablz.net
中做了非常相似的事情管理选项卡添加和删除的两个签到:
https://github.com/ButchersBoy/Dragablz/commit/47bbc302f5ffeaa8c234269ab4ff11bc80f7fa10 https://github.com/ButchersBoy/Dragablz/commit/c1dce0435683db83f163a77ccb9a19b3218b3ca7