Reputation: 7805
So I found this answered questions: Is it possible to rearrange tab items in tab control in wpf?
Using the information in that thread, I set it all up in my application:
<TabControl x:Name="tabControl">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="AllowDrop" Value="True"/>
<EventSetter Event="PreviewMouseMove" Handler="TabItem_Drag"/>
<EventSetter Event="Drop" Handler="TabItem_Drop"/>
</Style>
</TabControl.Resources>
</TabControl>
And the code:
private void TabItem_Drag(object sender, MouseEventArgs e)
{
var tabItem = e.Source as TabItem;
if (tabItem == null)
return;
if (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed)
DragDrop.DoDragDrop(tabItem, tabItem, DragDropEffects.All);
}
private void TabItem_Drop(object sender, DragEventArgs e)
{
var tabItemTarget = e.Source as TabItem;
var tabItemSource = e.Data.GetData(typeof(TabItem)) as TabItem;
if (!tabItemTarget.Equals(tabItemSource))
{
int sourceIndex = tabControl.Items.IndexOf(tabItemSource);
int targetIndex = tabControl.Items.IndexOf(tabItemTarget);
tabControl.Items.Remove(tabItemSource);
tabControl.Items.Insert(targetIndex, tabItemSource);
tabControl.Items.Remove(tabItemTarget);
tabControl.Items.Insert(sourceIndex, tabItemTarget);
tabControl.SelectedIndex = targetIndex;
}
}
The problem is, when I drop the tab, I get the following error at
if (!tabItemTarget.Equals(tabItemSource))
An exception of type 'System.NullReferenceException' occurred in Scoreboard Assistant.exe but was not handled in user code
Additional information: Object reference not set to an instance of an object.
When I click continue, I get the following error at
DragDrop.DoDragDrop(tabItem, tabItem, DragDropEffects.All);
An unhandled exception of type 'System.NullReferenceException' occurred in PresentationCore.dll
Additional information: Object reference not set to an instance of an object.
And then the program dies. What am I doing wrong?
* EDIT *
Okay, I figured out what the issue is; I just need help fixing it. If the tab item is created as follows it works perfectly fine:
<TabItem Header="TabItem"/>
However, my tabs are being created as follows:
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="images/text.png" />
<TextBlock Text="Text"/>
</StackPanel>
</TabItem.Header>
</TabItem>
As you can see, I am using a stackpanel to have an icon in the tab header. The problem seems to be that when I drag and drop the panel, instead of the e.Source
being read as the tabitem, its reading the textblock within the stackpanel of the tabitem. How would I fix this?
Upvotes: 2
Views: 8361
Reputation: 37770
Since the visual tree of TabItem
header can be rather complex, you can't guarantee, that drop target will be a TabItem
instance (that's what happening in your code).
But you can find TabItem
via exploring visual tree:
private TabItem GetTargetTabItem(object originalSource)
{
var current = originalSource as DependencyObject;
while (current != null)
{
var tabItem = current as TabItem;
if (tabItem != null)
{
return tabItem;
}
current = VisualTreeHelper.GetParent(current);
}
return null;
}
private void TabItem_Drop(object sender, DragEventArgs e)
{
var tabItemTarget = GetTargetTabItem(e.OriginalSource);
if (tabItemTarget != null)
{
var tabItemSource = (TabItem)e.Data.GetData(typeof(TabItem));
if (tabItemTarget != tabItemSource)
{
// the rest of your code
}
}
}
Upvotes: 3