I had exactly the same problem as yours. Not willing to remove the icons from the actual UI, I just disabled them with event handlers
Here is how I worked:
To remove those Hide and AutoHide commands:
I added the following handler:
CommandManager.AddPreviewExecutedHandler(this, new ExecutedRoutedEventHandler(this.ContentClosing))
This is what ContentClosing
looks like:
/// <summary>
/// Handler called when user clicked on one of the three buttons in a DockablePane
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ContentClosing(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command == ApplicationCommands.Close || e.Command == DockablePaneCommands.Close)
{
e.Handled = true;
DockManager source = sender as DockManager;
if (source.ActiveContent != null)
{
source.ActiveContent.Close();
}
}
else if (e.Command == DockablePaneCommands.Hide || e.Command == DockablePaneCommands.ToggleAutoHide)
{
e.Handled = true;
}
}
This handler was here to actually close the right content. Fore some reason, sometimes AvalonDock
will close another content because it has the focus (clicking on the cross won't give focus to your content, and thus it will close the currently focused content...)
As you can see, I just override the events and close manually my components
Unfortunately, this does not cover all the cases. I also had for some reason (hello buggy AvalonDock) to actually catch the click on the close button because there is an edge case: If you remove the last component, you won't be able to add a new component, because AvalonDock
will remove the last remaining panel. Moreover, if you close a DockableContent
with many tabs in it, AvalonDock
will close all the tabs, so I had to implement something to just close the current tab (which makes more sense imho) I had to add a mouse down handler to each content added to catch this event. With the following trick, I could do a workaround to avoid this bug:
/// <summary>
/// Handler called when a DockableContent state changed.
/// We need it to define a PreviewMouseDownHandler for each DockablePane
/// possibly created (which are usually created upon docking a floating window
/// to a new position) in order to handle single DockableContent closing
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void NewContent_StateChanged(object sender, RoutedEventArgs e)
{
DockableContent source = sender as DockableContent;
if (source.State == DockableContentState.Docked && source.Parent is DockablePane)
{
DockablePane parent = source.Parent as DockablePane;
parent.PreviewMouseDown -= mouseHandler;
parent.PreviewMouseDown += mouseHandler;
}
}
/// <summary>
/// Handler called on mouse down on a DockablePane.
/// It is designed to detect where did the user click, and
/// if he clicked on Close, only the current DockableContent will be closed
/// (unlike the native behavior which requires us to close the entire DockablePane
/// upon clicking on Close...)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DockablePaneMouseDown(object sender, MouseButtonEventArgs e)
{
DockablePane source = sender as DockablePane;
if (e.OriginalSource is AvalonDock.ImageEx)
{
//User clicked on one of the three icons on the top-right corner of the DockablePane
if ((e.OriginalSource as AvalonDock.ImageEx).Source.ToString().Contains("PinClose"))
{
RemoveFromUI(source.SelectedItem as DockableContent);
e.Handled = true;
}
}
}
/// <summary>
/// Removes a DockableContent from the currently displayed UI
/// (called when the original DockableItemsSource changed)
/// </summary>
/// <param name="content">The content to be removed</param>
private void RemoveFromUI(DockableContent content)
{
if (content == null)
{
return;
}
DockablePane parent = content.Parent as DockablePane;
if (this.ActiveContent == parent.SelectedItem)
{
this.ActiveContent = null;
}
(parent.SelectedItem as DockableContent).Close();
//// If the current DockablePane is left empty, we ensure to close it
if (parent.Items.Count == 0)
{
//This case is needed if we are trying to remove the last DockablePane from a DockingManager
//Native behavior will NOT set the Content property if you remove the last DockablePane:
//it will therefore consider this CLOSED DockablePane as the current ActiveContent,
//and will try to add new contents in this closed pane, which seems rather disturbing.
//Here we explicitly set the Content property to null if we are removing the last element,
//so next time user adds a tab, it will be added as the new Content!
if (parent == this.Content)
{
this.Content = null;
}
parent.Close();
parent.Visibility = Visibility.Hidden;
}
}
Again, this is an incredible big work for such small matters, but unfortunately, this AvalonDock
is far from being production-environment ready and I had to tweak such things to make it work.
Hope it'll work for you as well and save yourself some headaches I've already given to this problem!