Monday, December 23, 2013

TabControl - so many issues

I expect Microsoft controls to have limited functionality but to be bug free. At least, the bugs they have should be so subtle that the average developer will not be affected by them. But the version of TabControl that ships with WPF 4.0 and 4.5 (and probably all the other versions) is riddled with bugs. Here's a list of the issues I've found...

ClipToBounds does not work.

It doesn't matter what value you assign to ClipToBounds, the content of a TabControl is always clipped. Take a look at the following XAML for an example. The contents of column 1 slide into column 0 because of the negative left margin. But the contents of column2 are truncated because they are inside a TabControl. No combination of values for ClipToBounds will change this. I've even tried a translate RenderTransform but nothing works.


<window height="350" title="MainWindow" width="525" x:class="MainWindow" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <grid showgridlines="True">
        <grid .columndefinitions="">
            <columndefinition>
            <columndefinition>
            <columndefinition>
        </grid>
        <label content="Column 1" grid.column="1" margin="-25,0,0,0">
        <tabcontrol cliptobounds="False" grid.column="2" name="tc">
            <tabitem header="Header">
                <label content="Column 2" margin="-25,0,0,0">
            </tabitem>
        </tabcontrol>
    </grid>
</window>

No PreviewSelectionChanged event

An essential feature of all navigation controls is the ability to cancel navigation in code. For example, you don't want a user to leave a page that has unsaved data on it without asking the user first. However the TabControl has no cancellable navigation event. The best you can do is undo the navigation after it has happened. This is nasty. But wait - it gets worse.

If you undo a navigation (by changing the SelectedIndex back to it's original value) in the SelectionChanged event, the SelectionChanged event will call itself. After doing this two or three times the SelectionChanged event is no longer called. This is probably due to some other bug in TabControl. The solution (see my earlier blog) is to use the dispatcher to undo to Navigation on a different thread. This is a ridiculously complex solution to what should have been a trivial and common requirement.

Routed Commands don't disable TabControl

One of the cool features of routed commands is that they can enable/disable the controls they are bound to as they become executable or not. But there's a problem.

The problem here is not unique to the TabControl, it's more of a limitation of the RoutedCommand. There is no way to bind the IsEnabled property of a control to the CanExecute status of a command when the control cannot be executed.

This is particularly annoying in the case of a TabControl when we may need to disable an entire tab when a particular routed command is not executable, even though we don't expect the command to be executed if the tab is selected.

The only solution is to explicitly enable/disable the control in the CanExecute event. Obviously this approach breaks as soon as you try to write generic RoutedEvent handlers.

myCheckBox.IsEnabled = e.CanExecute;


No comments:

Post a Comment