Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TabControl Overflow Button & Context Menu Exceptions #1562

Open
NicholasRicher opened this issue Apr 8, 2024 · 0 comments
Open

TabControl Overflow Button & Context Menu Exceptions #1562

NicholasRicher opened this issue Apr 8, 2024 · 0 comments

Comments

@NicholasRicher
Copy link

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>
2024-04-08.10-43-19.mp4
2024-04-08.10-42-21.mp4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant