HandyControl
HandyControl copied to clipboard
TabControl Overflow Button & Context Menu Exceptions
I am getting the following exceptions thrown in my application. I am creating hc:TabControl TabItems programmatically because I want to be able to keep them in sync with my ListBox selection. I have a NavButton custom control defined for items in my ListBox. I am attempting to keep track of hc:TabItems in private HashSet<string> openTabIdentifiers = new HashSet<string>();
The Context Menu exception occurs after I have adjusted the index of an hc:TabItem in my NewWindow_Closed
method.
I have attached videos to demonstrate the problems. Thank you for any assistance.
System.NullReferenceException HResult=0x80004003 Message=Object reference not set to an instance of an object. Source=HandyControl StackTrace: at HandyControl.Controls.TabItem.<.ctor>b__49_3(Object s, CanExecuteRoutedEventArgs e)
System.InvalidOperationException Logical tree depth exceeded while traversing the tree. This could indicate a cycle in the tree.
MainWindow.xaml
<ListBox Grid.Row="1" Grid.ColumnSpan="2" x:Name="sidebar" Margin="0,0,5.5,0" SelectionMode="Single"
SelectionChanged="CreateFrame_Click" HorizontalAlignment="Stretch" BorderThickness="0"
ItemsSource="{Binding NavigationButtons}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:NavButton NavLink="{Binding NavLink}" NavType="{Binding NavType}" Content="{Binding Content}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid x:Name="contentGrid" Grid.Row="1" Grid.Column="2">
<hc:TabControl x:Name="tabControl" IsAnimationEnabled="True" ShowOverflowButton="True" IsTabFillEnabled="False"
ShowContextMenu="True" ShowCloseButton="True" IsDraggable="True" CanBeClosedByMiddleButton="False"/>
</Grid>
MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private HashSet<string> openTabIdentifiers = new HashSet<string>();
public MainWindow()
{
ConfigHelper.Instance.SetLang("en");
InitializeComponent();
DataContext = this;
}
public ObservableCollection<NavButton> NavigationButtons { get; set; } = new ObservableCollection<NavButton>
{
new NavButton { NavLink = new Uri("/Pages/Categories.xaml", UriKind.Relative), NavType = typeof(UserControls.Categories), Content = "Categories" },
new NavButton { NavLink = new Uri("/Pages/Customers.xaml", UriKind.Relative), NavType = typeof(UserControls.Customers), Content = "Customers" },
new NavButton { NavLink = new Uri("/Pages/Employees.xaml", UriKind.Relative), NavType = typeof(UserControls.Employees), Content = "Employees" },
new NavButton { NavLink = new Uri("/Pages/EmployeeTerritories.xaml", UriKind.Relative), NavType = typeof(UserControls.EmployeeTerritories), Content = "Employee Territories" },
new NavButton { NavLink = new Uri("/Pages/OrderDetails.xaml", UriKind.Relative), NavType = typeof(UserControls.OrderDetails), Content = "Order Details" },
new NavButton { NavLink = new Uri("/Pages/Orders.xaml", UriKind.Relative), NavType = typeof(UserControls.Orders), Content = "Orders" },
new NavButton { NavLink = new Uri("/Pages/Products.xaml", UriKind.Relative), NavType = typeof(UserControls.Products), Content = "Products" },
new NavButton { NavLink = new Uri("/Pages/Shippers.xaml", UriKind.Relative), NavType = typeof(UserControls.Shippers), Content = "Shippers" },
new NavButton { NavLink = new Uri("/Pages/Suppliers.xaml", UriKind.Relative), NavType = typeof(UserControls.Suppliers),Content = "Suppliers" },
new NavButton { NavLink = new Uri("/Pages/Territories.xaml", UriKind.Relative), NavType = typeof(UserControls.Territories), Content = "Territories" }
};
private void CreateFrame_Click(object sender, SelectionChangedEventArgs e)
{
var selected = sidebar.SelectedItem as NavButton;
if (selected == null) return;
if (selected.Content.ToString() != null && openTabIdentifiers.Contains(selected.Content.ToString())) return;
HandyControl.Controls.TabItem existingTabItem = null;
foreach (HandyControl.Controls.TabItem tabItem in tabControl.Items.OfType<HandyControl.Controls.TabItem>())
{
if (string.Equals(tabItem.Header.ToString(), selected.Content.ToString()))
{
existingTabItem = tabItem;
break;
}
}
if (existingTabItem != null)
{
tabControl.SelectedItem = existingTabItem;
}
else
{
Grid tabContentGrid = new Grid();
tabContentGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(2, GridUnitType.Star) });
tabContentGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
tabContentGrid.RowDefinitions.Add(new RowDefinition());
Frame pageFrame = new Frame { NavigationUIVisibility = NavigationUIVisibility.Hidden };
pageFrame.Navigate(new Uri(selected.NavLink.ToString(), UriKind.Relative));
Grid.SetRow(pageFrame, 0);
GridSplitter gridSplitter = new GridSplitter
{
Height = 6,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Center,
Background = System.Windows.Media.Brushes.DarkGray
};
Grid.SetRow(gridSplitter, 1);
var userControlInstance = (UserControl)Activator.CreateInstance(selected.NavType);
Grid.SetRow(userControlInstance, 2);
tabContentGrid.Children.Add(pageFrame);
tabContentGrid.Children.Add(gridSplitter);
tabContentGrid.Children.Add(userControlInstance);
HandyControl.Controls.TabItem newTabItem = new HandyControl.Controls.TabItem
{
Header = selected.Content.ToString(),
Content = tabContentGrid,
Tag = Guid.NewGuid().ToString()
};
newTabItem.MouseDoubleClick += TabItem_MouseDoubleClick;
tabControl.Items.Add(newTabItem);
tabControl.SelectedItem = newTabItem;
}
}
private void TabItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
if (sender is HandyControl.Controls.TabItem tabItem)
{
openTabIdentifiers.Add(tabItem.Header.ToString());
var tabContentGrid = tabItem.Content;
tabItem.Content = null;
int tabIndex = tabControl.Items.IndexOf(tabItem);
tabControl.Items.Remove(tabItem);
Window newWindow = new Window
{
Content = tabContentGrid,
Title = tabItem.Header.ToString(),
Width = 800,
Height = 450,
WindowStartupLocation = WindowStartupLocation.CenterScreen
};
newWindow.Tag = new Tuple<HandyControl.Controls.TabItem, int>(tabItem, tabIndex);
newWindow.Closed += NewWindow_Closed;
newWindow.Owner = this;
newWindow.Show();
}
}
}
private void NewWindow_Closed(object? sender, EventArgs e)
{
if (sender is Window window && window.Tag is Tuple<HandyControl.Controls.TabItem, int> tuple)
{
int originalIndex = tuple.Item2;
HandyControl.Controls.TabItem originalTabItem = tuple.Item1;
if (originalIndex < 0 || originalIndex > tabControl.Items.Count)
{
originalIndex = tabControl.Items.Count;
}
tabControl.Items.Insert(originalIndex, originalTabItem);
originalTabItem.Content = window.Content;
openTabIdentifiers.Remove(originalTabItem.Header.ToString());
tabControl.SelectedItem = originalTabItem;
}
}
NavButton.cs
public class NavButton : ListBoxItem
{
static NavButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NavButton), new FrameworkPropertyMetadata(typeof(NavButton)));
}
public Uri NavLink
{
get { return (Uri)GetValue(NavLinkProperty); }
set { SetValue(NavLinkProperty, value); }
}
public static readonly DependencyProperty NavLinkProperty =
DependencyProperty.Register("NavLink", typeof(Uri), typeof(NavButton), new PropertyMetadata(null));
public Type NavType
{
get { return (Type)GetValue(NavTypeProperty); }
set { SetValue(NavTypeProperty, value); }
}
public static readonly DependencyProperty NavTypeProperty =
DependencyProperty.Register("NavType", typeof(Type), typeof(NavButton), new PropertyMetadata(null));
}
Themes/Generic.xaml
<Style TargetType="{x:Type local:NavButton}">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NavButton}">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#d0ebff"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#d0ebff"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
https://github.com/HandyOrg/HandyControl/assets/58941995/14a57e7a-4b5d-4560-96d3-fa0378543da0
https://github.com/HandyOrg/HandyControl/assets/58941995/bccc4f63-aca5-4388-829c-5ba115049af9