Skip to content

Commit 6be496b

Browse files
TabControl disconnects TabItem.DataContext for HorizontalContentAlignment="Stretch" (#2984)
* Added UI test reproducing the issue The issue is present when HorizontalContentAlignment is either omitted, or explicitly set to (the default) "Stretch" * Fixing issue with tab control See live stream recording, but the issue appears to be multiple Panels with IsItemsHost=True * Extended UI test with HasUniformTabWidth value as test input Ensuring all the triggers are "massaged" in the test(s) Co-authored-by: Kevin Bost <[email protected]>
1 parent a225b94 commit 6be496b

File tree

3 files changed

+106
-13
lines changed

3 files changed

+106
-13
lines changed

MaterialDesignThemes.UITests/WPF/TabControls/TabControlTests.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,79 @@ public async Task OnLoad_ThemeBrushesSet()
4646

4747
recorder.Success();
4848
}
49+
50+
[Description("Issue 2983")]
51+
[Theory]
52+
[InlineData("Center", true)]
53+
[InlineData("Center", false)]
54+
[InlineData("Left", true)]
55+
[InlineData("Left", false)]
56+
[InlineData("Right", true)]
57+
[InlineData("Right", false)]
58+
[InlineData("Stretch", true)]
59+
[InlineData("Stretch", false)]
60+
[InlineData("", true)]
61+
[InlineData("", false)]
62+
public async Task TabItem_ShouldKeepDataContext_WhenContextMenuOpens(string horizontalContentAlignment, bool hasUniformTabWidth)
63+
{
64+
await using var recorder = new TestRecorder(App);
65+
66+
string alignment = string.Empty;
67+
if (!string.IsNullOrEmpty(horizontalContentAlignment))
68+
{
69+
alignment = $"HorizontalContentAlignment=\"{horizontalContentAlignment}\"";
70+
}
71+
72+
//Arrange
73+
IVisualElement<StackPanel> stackPanel = await LoadXaml<StackPanel>(@$"
74+
<StackPanel Orientation=""Vertical"">
75+
<TabControl
76+
{alignment}
77+
materialDesign:TabAssist.HasUniformTabWidth=""{hasUniformTabWidth}""
78+
materialDesign:ColorZoneAssist.Mode=""PrimaryMid""
79+
Style=""{{StaticResource MaterialDesignFilledTabControl}}"">
80+
<system:String>aaaa</system:String>
81+
<system:String>bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</system:String>
82+
<TabControl.ItemTemplate>
83+
<DataTemplate DataType=""system:String"">
84+
<TextBlock Text=""{{Binding}}"" />
85+
</DataTemplate>
86+
</TabControl.ItemTemplate>
87+
<TabControl.ContentTemplate>
88+
<DataTemplate DataType=""system:String"">
89+
<TextBlock Text=""{{Binding}}"" />
90+
</DataTemplate>
91+
</TabControl.ContentTemplate>
92+
</TabControl>
93+
<Button Margin=""50"" Width=""200"" Content=""Button with context menu"">
94+
<Button.ContextMenu>
95+
<ContextMenu>
96+
<MenuItem Header=""Menu item"" />
97+
</ContextMenu>
98+
</Button.ContextMenu>
99+
</Button>
100+
</StackPanel>", ("system", typeof(string)));
101+
102+
IVisualElement<TabControl> tabControl = await stackPanel.GetElement<TabControl>();
103+
IVisualElement<Button> button = await stackPanel.GetElement<Button>();
104+
105+
// Assert initial data context
106+
IVisualElement<TabItem> tabItem = await tabControl.GetElement<TabItem>();
107+
object? dataContext = await tabItem.GetDataContext();
108+
Assert.Equal("aaaa", dataContext);
109+
110+
// Act
111+
await button.MoveCursorTo();
112+
await button.RightClick();
113+
await tabControl.MoveCursorTo();
114+
await tabControl.LeftClick(Position.TopLeft);
115+
await Task.Delay(50); // allow a little time for the disconnect to occur
116+
117+
// Assert data context still present
118+
tabItem = await tabControl.GetElement<TabItem>();
119+
dataContext = await tabItem.GetDataContext();
120+
Assert.Equal("aaaa", dataContext);
121+
122+
recorder.Success();
123+
}
49124
}

MaterialDesignThemes.Wpf/TabAssist.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
namespace MaterialDesignThemes.Wpf
1+
namespace MaterialDesignThemes.Wpf;
2+
3+
public static class TabAssist
24
{
3-
public static class TabAssist
4-
{
5-
public static readonly DependencyProperty HasFilledTabProperty = DependencyProperty.RegisterAttached(
6-
"HasFilledTab", typeof(bool), typeof(TabAssist), new PropertyMetadata(false));
5+
public static readonly DependencyProperty HasFilledTabProperty = DependencyProperty.RegisterAttached(
6+
"HasFilledTab", typeof(bool), typeof(TabAssist), new PropertyMetadata(false));
7+
8+
public static void SetHasFilledTab(DependencyObject element, bool value) => element.SetValue(HasFilledTabProperty, value);
79

8-
public static void SetHasFilledTab(DependencyObject element, bool value) => element.SetValue(HasFilledTabProperty, value);
10+
public static bool GetHasFilledTab(DependencyObject element) => (bool)element.GetValue(HasFilledTabProperty);
911

10-
public static bool GetHasFilledTab(DependencyObject element) => (bool)element.GetValue(HasFilledTabProperty);
12+
public static readonly DependencyProperty HasUniformTabWidthProperty = DependencyProperty.RegisterAttached(
13+
"HasUniformTabWidth", typeof(bool), typeof(TabAssist), new PropertyMetadata(false));
1114

12-
public static readonly DependencyProperty HasUniformTabWidthProperty = DependencyProperty.RegisterAttached(
13-
"HasUniformTabWidth", typeof(bool), typeof(TabAssist), new PropertyMetadata(false));
15+
public static void SetHasUniformTabWidth(DependencyObject element, bool value) => element.SetValue(HasUniformTabWidthProperty, value);
1416

15-
public static void SetHasUniformTabWidth(DependencyObject element, bool value) => element.SetValue(HasUniformTabWidthProperty, value);
17+
public static bool GetHasUniformTabWidth(DependencyObject element) => (bool)element.GetValue(HasUniformTabWidthProperty);
1618

17-
public static bool GetHasUniformTabWidth(DependencyObject element) => (bool)element.GetValue(HasUniformTabWidthProperty);
19+
internal static bool GetBindableIsItemsHost(DependencyObject obj)
20+
=> (bool)obj.GetValue(BindableIsItemsHostProperty);
1821

22+
internal static void SetBindableIsItemsHost(DependencyObject obj, bool value)
23+
=> obj.SetValue(BindableIsItemsHostProperty, value);
24+
25+
internal static readonly DependencyProperty BindableIsItemsHostProperty =
26+
DependencyProperty.RegisterAttached("BindableIsItemsHost", typeof(bool), typeof(TabAssist), new PropertyMetadata(false, OnBindableIsItemsHostChanged));
27+
28+
private static void OnBindableIsItemsHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
29+
{
30+
if (d is Panel panel)
31+
{
32+
panel.IsItemsHost = (bool)e.NewValue;
33+
}
1934
}
2035
}

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TabControl.xaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@
5050
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
5151
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
5252
Focusable="False"
53-
IsItemsHost="True"
53+
wpf:TabAssist.BindableIsItemsHost="{Binding IsVisible, RelativeSource={RelativeSource Self}}"
5454
KeyboardNavigation.TabIndex="1"
5555
Rows="1" />
5656
<VirtualizingStackPanel x:Name="HeaderPanel"
5757
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
5858
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
5959
Focusable="False"
60-
IsItemsHost="True"
60+
wpf:TabAssist.BindableIsItemsHost="{Binding IsVisible, RelativeSource={RelativeSource Self}}"
6161
KeyboardNavigation.TabIndex="1"
6262
Orientation="Horizontal" />
6363
</StackPanel>
@@ -86,6 +86,7 @@
8686
<Setter TargetName="CenteredHeaderPanel" Property="Visibility" Value="Visible" />
8787
<Setter TargetName="HeaderPanel" Property="Visibility" Value="Collapsed" />
8888
</Trigger>
89+
8990
<MultiTrigger>
9091
<MultiTrigger.Conditions>
9192
<Condition Property="HorizontalContentAlignment" Value="Center" />
@@ -127,6 +128,7 @@
127128
<Setter TargetName="CenteredHeaderPanel" Property="Visibility" Value="Visible" />
128129
</MultiTrigger.Setters>
129130
</MultiTrigger>
131+
130132
<MultiTrigger>
131133
<MultiTrigger.Conditions>
132134
<Condition Property="HorizontalContentAlignment" Value="Right" />
@@ -147,6 +149,7 @@
147149
<Setter TargetName="CenteredHeaderPanel" Property="Visibility" Value="Visible" />
148150
</MultiTrigger.Setters>
149151
</MultiTrigger>
152+
150153
<Trigger Property="TabStripPlacement" Value="Bottom">
151154
<Setter Property="wpf:ShadowAssist.ShadowEdges" Value="Top" />
152155
<Setter TargetName="PART_HeaderZone" Property="DockPanel.Dock" Value="Bottom" />

0 commit comments

Comments
 (0)