admin管理员组文章数量:1418673
Issue:
Our application is built somewhat similiarly to a common web browsers, meaning that different user controls are grouped to Tabs, which can be created, switched between and closed. To do this we have created custom ClosableTab Item TabControl
. It generally works pretty well but every so often the selection of a tab misfires. The selectedIndex
property always stays correctly the same but when you interact with any UI in the tab it jumps to some specific Tab seemingly chosen at random. It always jumps to this same tab until user creates new tab and deletes it (somehow resetting the selection process ?!).
Context:
We are dealing with this issue for a good part of a 3+ years not beeing able to solve it. The app is written in WPF and C# currently targeting .NET Framework 4.7.2. We don´t really follow MVVM programming pattern, though we try to keep bussines logic / data structeres / ui separate just not in any standard way.
Code:
The main TabControl
is located at 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>
Coresponding code-behind to manipulate TabControl
/ TabItems
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();
}
}
}
The custom TabItem
is in Item.cs using ClosableTab.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);
}
...
}
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)
and finally some UI that uses this ClosableTab.cs:
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;
}
...
Do you have any Idea what could be causing this. We already tried to check if some event is not bubbling up the chain of UI, to forcefully set the selectedItem and so on but no luck.
Issue:
Our application is built somewhat similiarly to a common web browsers, meaning that different user controls are grouped to Tabs, which can be created, switched between and closed. To do this we have created custom ClosableTab Item TabControl
. It generally works pretty well but every so often the selection of a tab misfires. The selectedIndex
property always stays correctly the same but when you interact with any UI in the tab it jumps to some specific Tab seemingly chosen at random. It always jumps to this same tab until user creates new tab and deletes it (somehow resetting the selection process ?!).
Context:
We are dealing with this issue for a good part of a 3+ years not beeing able to solve it. The app is written in WPF and C# currently targeting .NET Framework 4.7.2. We don´t really follow MVVM programming pattern, though we try to keep bussines logic / data structeres / ui separate just not in any standard way.
Code:
The main TabControl
is located at 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>
Coresponding code-behind to manipulate TabControl
/ TabItems
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();
}
}
}
The custom TabItem
is in Item.cs using ClosableTab.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);
}
...
}
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)
and finally some UI that uses this ClosableTab.cs:
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;
}
...
Do you have any Idea what could be causing this. We already tried to check if some event is not bubbling up the chain of UI, to forcefully set the selectedItem and so on but no luck.
Share Improve this question asked Jan 29 at 16:15 Marek SýkorkaMarek Sýkorka 11 bronze badge 6- Is issue easy to reproduce? Then use "divide and conquer" method to find problematic place: copy complete solution somewhere, delete roughtly half of it, problem still? If no - restore and either delete other half, or delete less. Yes - continue dividing until you find a specific place or at least have a very nice minimal reproducible example to ask a question here. – Sinatr Commented Jan 29 at 16:43
- "Tabs" unload and reload as the selected Tab changes ("losing state"). Then there are all these questionable timers. (And it would be more clear to state: TimeSpan.FromSeconds(n), for example, than using only constructors). You need to goback, and restate the "User Requirements" and see if they got lost somewhere in the "engineering". – Gerry Schmitz Commented Jan 29 at 17:03
- @Sinatr No not really the issue does not happen consistently, or due to specific action. The only thing that we can be sure of is that it almost always happens with multiple "Device" tabs opened which are communicating with the devices. More of the tabs the higher chance of it happening. We sure tried to find the bug with this elimination method but never by excluding large parts of the application. I will try to look into that the next thing tomorrow morning when I get back to work. – Marek Sýkorka Commented Jan 29 at 17:20
- As it is large universal app to diagnose our devices it tends to increase in functionality by plastering new stuff on to the old stuff with very limited time and resources to testing or integration. Bugs that are not caught immedietely tend to become more tedius and infuriating with time. – Marek Sýkorka Commented Jan 29 at 17:22
- @GerrySchmitz If I understand correctly the UI of said tabs is unloaded and reloaded when selection changes ? The timers are used to keep the communication at specified frequency with our devices running in the background even tho the Tab is not focused. It is necessery as one Tab is dedicated to one device and we gather all sorts of diagnostic data, that are represented in graphs and so on. The way we did it might not be correct which I don´t really know but we sure can try to do it in a different way. – Marek Sýkorka Commented Jan 29 at 17:27
1 Answer
Reset to default 0so just to keep you all updated. I have been working on this issue this past week and it seems to be fixed, or better said .. hacked to work as intended.
I have not found what exactly is causing this issue, but with the help of third-party tool "Spoon" and Debug.WriteLine(Enviroment.StackTrace) I have been able to compare normal and abnormal behaviour of tabSelectionChanged. Every time the tab misfired it was by part caused by MouseCaptureLost on some part of our UI. Image from WinMerge compare between StackTrace of normal and abnormal tab behaviour.
To prevent this from happening I have built custom checking logic and sanitized the input of event handlers for our custom class ClosableTab.cs,
// Internal method to check for tab change validity
private bool EvaluateTabFocus()
{
_mouseOverTabFocus = IsMouseOver;
bool ret = _newTabFocus || _mouseOverTabFocus || _codeNavigationFocus;
_codeNavigationFocus = _newTabFocus = false;
return ret;
}
// Override OnSelected - Show the Close Button
protected override void OnSelected(RoutedEventArgs e)
{
// Check if the event was generated by the same type
if (e.OriginalSource.GetType() != this.GetType())
return;
// Check if the event is happening whilst the mouse is over top of the header or there is automatic navigation to this tab through code.
if (EvaluateTabFocus())
{
_selectionIsValid = true;
base.OnSelected(e);
((CloseableHeader)this.Header).button_close.Visibility = Visibility.Visible;
}
else
{
_selectionIsValid = false;
base.OnSelected(e);
}
}
I then use this _selectionIsValid inside MainWindow.xaml.cs private void tcMain_SelectionChanged(object sender, SelectionChangedEventArgs e) to check for valid tab changes and repeatadly discard the bad ones.
It seems to be working now so we will be monitoring the behaviour. If the issue appears again I may return but for now this cutom logic is doing fine. Thanks all to your helpfull insights.
本文标签: cRandom behaviour of SelectedItem property of our custom TabItemTabControlStack Overflow
版权声明:本文标题:c# - Random behaviour of SelectedItem property of our custom TabItemTabControl - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745291027a2651795.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论