Skip to content

Commit 4a0912c

Browse files
committed
ComboBox uses VisibleWidth
I added the GetVisibleWidth method that returns a visible width of an element. So now ComboBoxPopup correctly covers an input control.
1 parent 4e86faf commit 4a0912c

File tree

3 files changed

+128
-24
lines changed

3 files changed

+128
-24
lines changed

MaterialDesignThemes.Wpf/ComboBoxPopup.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Windows;
44
using System.Windows.Controls;
55
using System.Windows.Controls.Primitives;
6+
using System.Windows.Media;
67

78
namespace MaterialDesignThemes.Wpf
89
{
@@ -68,6 +69,8 @@ public double DownVerticalOffset
6869
set { SetValue(DownVerticalOffsetProperty, value); }
6970
}
7071

72+
#region DefaultVerticalOffset
73+
7174
public static readonly DependencyProperty DefaultVerticalOffsetProperty
7275
= DependencyProperty.Register(nameof(DefaultVerticalOffset),
7376
typeof(double),
@@ -80,6 +83,24 @@ public double DefaultVerticalOffset
8083
set { SetValue(DefaultVerticalOffsetProperty, value); }
8184
}
8285

86+
#endregion
87+
88+
#region VisiblePlacementWidth
89+
90+
public double VisiblePlacementWidth
91+
{
92+
get { return (double)GetValue(VisiblePlacementWidthProperty); }
93+
set { SetValue(VisiblePlacementWidthProperty, value); }
94+
}
95+
96+
public static readonly DependencyProperty VisiblePlacementWidthProperty
97+
= DependencyProperty.Register(nameof(VisiblePlacementWidth),
98+
typeof(double),
99+
typeof(ComboBoxPopup),
100+
new PropertyMetadata(0.0));
101+
102+
#endregion
103+
83104
public ComboBoxPopup()
84105
{
85106
this.CustomPopupPlacementCallback = ComboBoxCustomPopupPlacementCallback;
@@ -101,7 +122,7 @@ private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(Size popupSi
101122
{
102123
var locationFromScreen = this.PlacementTarget.PointToScreen(new Point(0, 0));
103124

104-
var mainVisual = PlacementTarget.GetVisualAncestry().OfType<System.Windows.Media.Visual>().LastOrDefault();
125+
var mainVisual = PlacementTarget.GetVisualAncestry().OfType<Visual>().LastOrDefault();
105126
if (mainVisual == null) return new CustomPopupPlacement[0];
106127

107128
var screenWidth = (int) DpiHelper.TransformToDeviceX(mainVisual, SystemParameters.PrimaryScreenWidth);
@@ -124,6 +145,9 @@ private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(Size popupSi
124145
var defaultVerticalOffsetIndepent = DpiHelper.TransformToDeviceY(mainVisual, DefaultVerticalOffset);
125146
var upVerticalOffsetIndepent = DpiHelper.TransformToDeviceY(mainVisual, UpVerticalOffset);
126147
var downVerticalOffsetIndepent = DpiHelper.TransformToDeviceY(mainVisual, DownVerticalOffset);
148+
var parent = this.PlacementTarget.GetVisualAncestry().OfType<Panel>().ElementAt(1);
149+
150+
VisiblePlacementWidth = TreeHelper.GetVisibleWidth((FrameworkElement)PlacementTarget, parent);
127151

128152
if (locationX + popupSize.Width - realOffsetX > screenWidth
129153
|| locationX + realOffsetX < 0)

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ComboBox.xaml

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
Width="{StaticResource PopupLeftRightMargin}"
7373
Fill="{Binding ElementName=templateRoot, Path=Background}"/>
7474
<Grid Grid.Column="1"
75-
Width="{Binding ElementName=templateRoot, Path=ActualWidth}"
75+
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type wpf:ComboBoxPopup}}, Path=VisiblePlacementWidth}"
7676
Height="{Binding ElementName=templateRoot, Path=ActualHeight}"/>
7777
<Rectangle Grid.Column="2"
7878
MinWidth="{StaticResource PopupLeftRightMargin}"
@@ -126,7 +126,7 @@
126126
Fill="{Binding ElementName=templateRoot, Path=Background}"
127127
/>
128128
<Grid Grid.Column="1"
129-
Width="{Binding ElementName=templateRoot, Path=ActualWidth}"
129+
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type wpf:ComboBoxPopup}}, Path=VisiblePlacementWidth}"
130130
Height="{Binding ElementName=templateRoot, Path=ActualHeight}"/>
131131
<Rectangle Grid.Column="2"
132132
MinWidth="{StaticResource PopupLeftRightMargin}"
@@ -329,7 +329,7 @@
329329
<Setter.Value>
330330
<ControlTemplate TargetType="{x:Type ToggleButton}">
331331
<Grid>
332-
<Border x:Name="templateRoot"
332+
<Border x:Name="ToggleTemplateRoot"
333333
Background="{TemplateBinding Background}"
334334
BorderBrush="{TemplateBinding BorderBrush}"
335335
BorderThickness="{TemplateBinding BorderThickness}">
@@ -375,14 +375,14 @@
375375
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false" />
376376
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false" />
377377
</MultiDataTrigger.Conditions>
378-
<Setter TargetName="templateRoot" Property="BorderBrush" Value="{DynamicResource MaterialDesignCheckBoxDisabled}" />
378+
<Setter TargetName="ToggleTemplateRoot" Property="BorderBrush" Value="{DynamicResource MaterialDesignCheckBoxDisabled}" />
379379
</MultiDataTrigger>
380380
<MultiDataTrigger>
381381
<MultiDataTrigger.Conditions>
382382
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false" />
383383
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true" />
384384
</MultiDataTrigger.Conditions>
385-
<Setter TargetName="templateRoot" Property="BorderBrush" Value="{DynamicResource MaterialDesignCheckBoxDisabled}" />
385+
<Setter TargetName="ToggleTemplateRoot" Property="BorderBrush" Value="{DynamicResource MaterialDesignCheckBoxDisabled}" />
386386
<Setter TargetName="splitBorder" Property="BorderBrush" Value="{DynamicResource MaterialDesignCheckBoxDisabled}" />
387387
</MultiDataTrigger>
388388
</ControlTemplate.Triggers>
@@ -412,24 +412,28 @@
412412
Margin="{TemplateBinding Padding}"
413413
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
414414
UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
415-
<ContentPresenter x:Name="contentPresenter"
416-
Content="{TemplateBinding SelectionBoxItem}"
417-
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
418-
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
419-
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
420-
IsHitTestVisible="False"/>
421-
<TextBox x:Name="PART_EditableTextBox"
422-
IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
423-
Style="{StaticResource MaterialDesignComboBoxEditableTextBox}"
424-
Visibility="Collapsed" />
425-
<wpf:SmartHint x:Name="Hint"
426-
HintProxy="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static converters:HintProxyFabricConverter.Instance}}"
427-
FontSize="{TemplateBinding FontSize}"
428-
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
429-
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
430-
UseFloating="{Binding Path=(wpf:HintAssist.IsFloating), RelativeSource={RelativeSource TemplatedParent}}"
431-
HintOpacity="{Binding Path=(wpf:HintAssist.HintOpacity), RelativeSource={RelativeSource TemplatedParent}}"
432-
Hint="{TemplateBinding wpf:HintAssist.Hint}" />
415+
<Grid x:Name="InputRoot"
416+
HorizontalAlignment="Left">
417+
<ContentPresenter x:Name="contentPresenter"
418+
Content="{TemplateBinding SelectionBoxItem}"
419+
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
420+
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
421+
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
422+
IsHitTestVisible="False"/>
423+
<TextBox x:Name="PART_EditableTextBox"
424+
IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
425+
Style="{StaticResource MaterialDesignComboBoxEditableTextBox}"
426+
Visibility="Collapsed" />
427+
428+
<wpf:SmartHint x:Name="Hint"
429+
HintProxy="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static converters:HintProxyFabricConverter.Instance}}"
430+
FontSize="{TemplateBinding FontSize}"
431+
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
432+
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
433+
UseFloating="{Binding Path=(wpf:HintAssist.IsFloating), RelativeSource={RelativeSource TemplatedParent}}"
434+
HintOpacity="{Binding Path=(wpf:HintAssist.HintOpacity), RelativeSource={RelativeSource TemplatedParent}}"
435+
Hint="{TemplateBinding wpf:HintAssist.Hint}" />
436+
</Grid>
433437
</Grid>
434438
<wpf:Underline x:Name="Underline"
435439
Grid.ColumnSpan="2"

MaterialDesignThemes.Wpf/TreeHelper.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,89 @@
44
using System.Text;
55
using System.Threading.Tasks;
66
using System.Windows;
7+
using System.Windows.Controls;
78
using System.Windows.Controls.Primitives;
89
using System.Windows.Media;
910

1011
namespace MaterialDesignThemes.Wpf
1112
{
1213
internal static class TreeHelper
1314
{
15+
public static double GetVisibleWidth(FrameworkElement element, UIElement parent)
16+
{
17+
if (element == null) throw new ArgumentNullException(nameof(element));
18+
if (parent == null) throw new ArgumentNullException(nameof(parent));
19+
20+
var location = element.TransformToAncestor(parent).Transform(new Point(0, 0));
21+
22+
int width = (int) Math.Floor(element.ActualWidth);
23+
var hitTest = parent.InputHitTest(new Point(location.X + width, location.Y));
24+
25+
if (IsAncestorTill(hitTest as FrameworkElement, element, parent))
26+
{
27+
return width;
28+
}
29+
30+
//BinarySearch there
31+
int end = (int) Math.Floor(element.ActualWidth);
32+
int start = 0;
33+
34+
while (start < end)
35+
{
36+
width = (end + start)/2;
37+
hitTest = parent.InputHitTest(new Point(location.X + width, location.Y));
38+
39+
if (IsAncestorTill(hitTest as FrameworkElement, element, parent))
40+
{
41+
//Speed tweak
42+
hitTest = parent.InputHitTest(new Point(location.X + width + 1, location.Y));
43+
44+
if (IsAncestorTill(hitTest as FrameworkElement, element, parent))
45+
{
46+
start = width;
47+
}
48+
else
49+
{
50+
return width;
51+
}
52+
}
53+
else
54+
{
55+
end = width;
56+
}
57+
}
58+
59+
60+
//for (int width = (int) Math.Floor(element.ActualWidth); width >= 0; width--)
61+
//{
62+
// var hitTest = parent.InputHitTest(new Point(location.X + width, location.Y));
63+
//
64+
// if (hitTest == null) continue;
65+
//
66+
// if (IsAncestorTill(hitTest as FrameworkElement, element, parent))
67+
// {
68+
// return width;
69+
// }
70+
//}
71+
72+
return element.ActualWidth;
73+
}
74+
75+
private static bool IsAncestorTill(FrameworkElement element, object ancestor, object container)
76+
{
77+
if (element == null) return false;
78+
79+
FrameworkElement parent = element;
80+
81+
do
82+
{
83+
if (ReferenceEquals(parent, ancestor)) return true;
84+
if (ReferenceEquals(parent, container)) return false;
85+
} while ((parent = (parent.Parent ?? VisualTreeHelper.GetParent(parent)) as FrameworkElement) != null);
86+
87+
return false;
88+
}
89+
1490
public static Visual FindMainTreeVisual(Visual visual)
1591
{
1692
DependencyObject root = null;

0 commit comments

Comments
 (0)