Skip to content

Commit f90f160

Browse files
Fix ComboBox popup location (#1963)
* Fix ComboBox popup location * Fixes if FlowDirection is different from parent's Co-authored-by: kamei <[email protected]>
1 parent 11c3c0d commit f90f160

File tree

3 files changed

+39
-24
lines changed

3 files changed

+39
-24
lines changed

MaterialDesignThemes.Wpf/ComboBoxPopup.cs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,17 @@ public double ContentMinWidth
209209
set { SetValue(ContentMinWidthProperty, value); }
210210
}
211211

212+
public static readonly DependencyProperty RelativeHorizontalOffsetProperty
213+
= DependencyProperty.Register(
214+
nameof(RelativeHorizontalOffset), typeof(double), typeof(ComboBoxPopup),
215+
new FrameworkPropertyMetadata(default(double)));
216+
217+
public double RelativeHorizontalOffset
218+
{
219+
get => (double)GetValue(RelativeHorizontalOffsetProperty);
220+
set => SetValue(RelativeHorizontalOffsetProperty, value);
221+
}
222+
212223
public ComboBoxPopup()
213224
{
214225
CustomPopupPlacementCallback = ComboBoxCustomPopupPlacementCallback;
@@ -230,7 +241,7 @@ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
230241
private void SetupVisiblePlacementWidth(IEnumerable<DependencyObject> visualAncestry)
231242
{
232243
var parent = visualAncestry.OfType<Panel>().ElementAt(1);
233-
VisiblePlacementWidth = TreeHelper.GetVisibleWidth((FrameworkElement)PlacementTarget, parent);
244+
VisiblePlacementWidth = TreeHelper.GetVisibleWidth((FrameworkElement)PlacementTarget, parent, FlowDirection);
234245
}
235246

236247
private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(
@@ -240,13 +251,13 @@ private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(
240251

241252
SetupVisiblePlacementWidth(visualAncestry);
242253

243-
var data = GetPositioningData(visualAncestry, popupSize, targetSize, offset);
254+
var data = GetPositioningData(visualAncestry, popupSize, targetSize);
244255
var preferUpIfSafe = data.LocationY + data.PopupSize.Height > data.ScreenHeight;
245256

246257
if (ClassicMode
247-
|| data.LocationX + data.PopupSize.Width - data.RealOffsetX > data.ScreenWidth
248-
|| data.LocationX - data.RealOffsetX < 0
249-
|| !preferUpIfSafe && data.LocationY - Math.Abs(data.NewDownY) < 0)
258+
|| data.PopupLocationX + data.PopupSize.Width > data.ScreenWidth
259+
|| data.PopupLocationX < 0
260+
|| !preferUpIfSafe && data.LocationY + data.NewDownY < 0)
250261
{
251262
SetCurrentValue(PopupPlacementProperty, ComboBoxPopupPlacement.Classic);
252263
return new[] { GetClassicPopupPlacement(this, data) };
@@ -272,7 +283,7 @@ private void SetChildTemplateIfNeed(ControlTemplate template)
272283
}
273284
}
274285

275-
private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualAncestry, Size popupSize, Size targetSize, Point offset)
286+
private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualAncestry, Size popupSize, Size targetSize)
276287
{
277288
var locationFromScreen = PlacementTarget.PointToScreen(new Point(0, 0));
278289

@@ -290,15 +301,11 @@ private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualA
290301
var upVerticalOffsetIndependent = DpiHelper.TransformToDeviceY(mainVisual, UpVerticalOffset);
291302
var newUpY = upVerticalOffsetIndependent - popupSize.Height + targetSize.Height;
292303
var newDownY = DpiHelper.TransformToDeviceY(mainVisual, DownVerticalOffset);
293-
294-
double offsetX;
295-
const int rtlHorizontalOffset = 20;
296-
304+
var offsetX = DpiHelper.TransformToDeviceX(mainVisual, RelativeHorizontalOffset);
297305
if (FlowDirection == FlowDirection.LeftToRight)
298-
offsetX = DpiHelper.TransformToDeviceX(mainVisual, offset.X);
306+
offsetX = Round(offsetX);
299307
else
300-
offsetX = DpiHelper.TransformToDeviceX(mainVisual,
301-
offset.X - targetSize.Width - rtlHorizontalOffset);
308+
offsetX = Math.Truncate(offsetX - targetSize.Width);
302309

303310
return new PositioningData(
304311
mainVisual, offsetX,
@@ -308,6 +315,8 @@ private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualA
308315
screenHeight, screenWidth);
309316
}
310317

318+
private static double Round(double val) => val < 0 ? (int)(val - 0.5) : (int)(val + 0.5);
319+
311320
private static PropertyChangedCallback CreateTemplatePropertyChangedCallback(ComboBoxPopupPlacement popupPlacement)
312321
{
313322
return delegate (DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -379,7 +388,7 @@ private struct PositioningData
379388
public double OffsetX { get; }
380389
public double NewUpY { get; }
381390
public double NewDownY { get; }
382-
public double RealOffsetX => (PopupSize.Width - TargetSize.Width) / 2.0;
391+
public double PopupLocationX => LocationX + OffsetX;
383392
public Size PopupSize { get; }
384393
public Size TargetSize { get; }
385394
public double LocationX { get; }
@@ -390,9 +399,9 @@ private struct PositioningData
390399
public PositioningData(Visual mainVisual, double offsetX, double newUpY, double newDownY, Size popupSize, Size targetSize, double locationX, double locationY, double screenHeight, double screenWidth)
391400
{
392401
MainVisual = mainVisual;
393-
OffsetX = offsetX;
394-
NewUpY = newUpY;
395-
NewDownY = newDownY;
402+
OffsetX = Round(offsetX);
403+
NewUpY = Round(newUpY);
404+
NewDownY = Round(newDownY);
396405
PopupSize = popupSize; TargetSize = targetSize;
397406
LocationX = locationX; LocationY = locationY;
398407
ScreenWidth = screenWidth; ScreenHeight = screenHeight;

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ComboBox.xaml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
Height="{Binding ElementName=templateRoot, Path=ActualHeight}"/>
8282
<Border Grid.Column="2" MinWidth="{StaticResource PopupLeftRightMargin}" Background="{Binding ElementName=PART_Popup, Path=Background}"/>
8383
</Grid>
84-
<Border Grid.Row="4" Height="{StaticResource PopupTopBottomMargin}"/>
84+
<Border Grid.Row="4" Height="{StaticResource PopupTopBottomMargin}" Background="{Binding ElementName=PART_Popup, Path=Background}"/>
8585
</Grid>
8686
</Border>
8787
</Grid>
@@ -399,6 +399,7 @@
399399
<ControlTemplate x:Key="MaterialDesignFloatingHintComboBoxTemplate" TargetType="{x:Type ComboBox}">
400400
<Grid x:Name="templateRoot"
401401
Background="{TemplateBinding Background}"
402+
UseLayoutRounding="True"
402403
SnapsToDevicePixels="True">
403404
<AdornerDecorator>
404405
<Grid x:Name="InnerRoot"
@@ -465,7 +466,7 @@
465466
Opacity="{Binding Path=(wpf:HintAssist.HintOpacity), RelativeSource={RelativeSource TemplatedParent}}"
466467
Text="{Binding Path=(wpf:TextFieldAssist.SuffixText), RelativeSource={RelativeSource TemplatedParent}}"
467468
/>
468-
<Button x:Name="PART_ClearButton" Height="Auto" Padding="2,0,-6,0" Style="{DynamicResource MaterialDesignToolButton}" Focusable="False">
469+
<Button x:Name="PART_ClearButton" Height="Auto" Padding="2,-1,-1,-1" Style="{DynamicResource MaterialDesignToolButton}" Focusable="False">
469470
<Button.Visibility>
470471
<MultiBinding Converter="{StaticResource ClearTextConverter}">
471472
<Binding ElementName="Hint" Path="IsContentNullOrEmpty" Converter="{StaticResource NotConverter}"/>
@@ -492,7 +493,8 @@
492493
Grid.Column="0"
493494
AllowsTransparency="True"
494495
Focusable="False"
495-
HorizontalOffset="-11.5"
496+
HorizontalOffset="0"
497+
RelativeHorizontalOffset="-23"
496498
IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
497499
PlacementTarget="{Binding ElementName=templateRoot}"
498500
SnapsToDevicePixels="True"
@@ -501,7 +503,7 @@
501503
PopupAnimation="Fade"
502504
VerticalOffset="0"
503505
DefaultVerticalOffset="5"
504-
DownVerticalOffset="-15.5"
506+
DownVerticalOffset="-15"
505507
UpVerticalOffset="15"
506508
CornerRadius="2"
507509
ContentMargin="6"
@@ -816,8 +818,10 @@
816818
ClassicMode="True"
817819
CornerRadius="0,0,2,2"
818820
ContentMargin="6,0,6,6"
819-
HorizontalOffset="-3"
820-
VerticalOffset="-1"
821+
HorizontalOffset="0"
822+
RelativeHorizontalOffset="-6"
823+
VerticalOffset="0"
824+
DefaultVerticalOffset="-1"
821825
ContentMinWidth="{Binding Path=ActualWidth, ElementName=templateRoot}"
822826
UpContentTemplate="{StaticResource PopupContentUpTemplate}"
823827
DownContentTemplate="{StaticResource PopupContentDownTemplate}"

MaterialDesignThemes.Wpf/TreeHelper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ namespace MaterialDesignThemes.Wpf
66
{
77
internal static class TreeHelper
88
{
9-
public static double GetVisibleWidth(FrameworkElement element, UIElement parent)
9+
public static double GetVisibleWidth(FrameworkElement element, FrameworkElement parent, FlowDirection flowDirection)
1010
{
1111
if (element == null) throw new ArgumentNullException(nameof(element));
1212
if (parent == null) throw new ArgumentNullException(nameof(parent));
1313

1414
var location = element.TransformToAncestor(parent).Transform(new Point(0, 0));
15+
if (flowDirection != parent.FlowDirection)
16+
location.X -= element.ActualWidth;
1517

1618
int width = (int)Math.Floor(element.ActualWidth);
1719
var hitTest = parent.InputHitTest(new Point(location.X + width, location.Y));

0 commit comments

Comments
 (0)