自定义ClosableTab/TabControl的SelectedItem属性随机异常求助
自定义ClosableTabControl标签选中异常求助
我们实现了类似浏览器标签页的自定义ClosableTabControl,将用户控件分组到可创建、切换、关闭的标签页中,整体运行正常,但偶尔会出现诡异的标签选中异常:SelectedIndex属性值始终正确,但交互当前标签内任意UI元素时,界面会跳转到某个固定的“随机”标签,直到用户新建并删除一个标签后,异常才会重置。
上下文
- 应用基于WPF+C#开发,目标框架为.NET Framework 4.7.2
- 未遵循MVVM模式,但已尽量分离业务逻辑、数据结构与UI层
- 该问题已存在3年多,始终未找到根因
相关代码
主窗口TabControl定义(MainWindow.xaml)
<Window ... <Grid> <TabControl x:Name="tcMain" Margin="10,10,10.286,9.714" SelectionChanged="tcMain_SelectionChanged"> <TabItem> <TabItem.Header> <Tools:AddTabHeader/> </TabItem.Header> </TabItem> </TabControl> </Grid> </Window>
主窗口逻辑代码(MainWindow.xaml.cs)
public void AddItem(Item page) { if (!page.ErrorClose) { tcMain.Items.Insert(tcMain.Items.Count - 1, page.TabPage); tcMain.SelectedItem = page.TabPage; page.SetLanguage(); } } public TabItem GetActual() { return tcMain.SelectedItem as TabItem; } public void SetActual(TabItem item) { tcMain.SelectedItem = item; } private void tcMain_SelectionChanged(object sender, SelectionChangedEventArgs e) { //ak je len jedna zalozka, nerob nic if (tcMain.Items.Count <= 1) return; //ak je aktivna posledna treba zmenit if (tcMain.SelectedIndex == (tcMain.Items.Count - 1)) tcMain.SelectedIndex = tcMain.Items.Count - 2; //prejdi zalozky a oznac, ktora je aktivna foreach (TabItem tab in tcMain.Items) { if (tab.Content is Item) { Item itemTab = tab.Content as Item; itemTab.Active = tcMain.SelectedContent == itemTab; itemTab.ChangeState(); } } }
自定义TabItem实现
Item.cs(业务控件基类)
public class Item : UserControl, INotifyPropertyChanged { private ClosableTab page = null; public DeviceList devices; public DispatcherTimer dispatcherTimer; public int Communication = 0; public bool Run { get; set; } = false; public bool Active { get; set; } = true; public bool ErrorClose { get; set; } = true; public Item(string name, bool isFile = false) { //vytvor stranku page = new ClosableTab(); page.CloseTab += CloseTab; page.DataContext = this; devices = new DeviceList(); if (isFile) file = name; else { file = name; Title = name; } //timer dispatcherTimer = new DispatcherTimer(); dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 500); } ... }
ClosableTab.cs(自定义可关闭TabItem)
public class ClosableTab : TabItem { // Constructor public ClosableTab() { // Create an instance of the usercontrol CloseableHeader closableTabHeader = new CloseableHeader(); // Assign the usercontrol to the tab header this.Header = closableTabHeader; // Attach to the CloseableHeader events // (Mouse Enter/Leave, Button Click, and Label resize) closableTabHeader.button_close.MouseEnter += new MouseEventHandler(button_close_MouseEnter); closableTabHeader.button_close.MouseLeave += new MouseEventHandler(button_close_MouseLeave); closableTabHeader.button_close.Click += new RoutedEventHandler(button_close_Click); } // Property - Set the Title of the Tab // Override OnSelected - Show the Close Button // Override OnUnSelected - Hide the Close Button // Override OnMouseEnter - Show the Close Button // Override OnMouseLeave - Hide the Close Button (If it is NOT selected) // Button MouseEnter - When the mouse is over the button - change color to Red // Button MouseLeave - When mouse is no longer over button - change color back to black // Button Close Click - Remove the Tab - (or raise an event indicating a "CloseTab" event has occurred)
控件使用示例(Search标签页)
private void cm_Connect_Click(object sender, RoutedEventArgs e) { MainWindow.AppWindow.AddItem(new Search()); } /// <summary> /// Interaction logic for Search.xaml /// </summary> public partial class Search { public ObservableCollection<ComPort> AvailablePorts { get; set; } public Search() { InitializeComponent(); //daj ze mozem otvorit ErrorClose = false; //nastav priradenie AvailablePorts = new ObservableCollection<ComPort>(); dispatcherTimer = new DispatcherTimer(); dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 5); Run = true; dispatcherTimer.Start(); dispatcherTimer_Tick(null, null); Image = ToolFuncs.GetIcon("IcoConnect"); comboBox_ConnectionType.SelectedIndex = 0; comboBox_Port.ItemsSource = AvailablePorts; } ... }
已排查的无效方案
- 排查UI事件冒泡机制,未发现异常
- 尝试强制设置SelectedItem/SelectedIndex,无法修复异常
内容的提问来源于stack exchange,提问作者Marek Sýkorka
相关产品推荐
相关产品推荐

