Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions MaterialDesignThemes.Wpf/ComboBoxPopup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,17 @@ public double ContentMinWidth
set { SetValue(ContentMinWidthProperty, value); }
}

public static readonly DependencyProperty RelativeHorizontalOffsetProperty
= DependencyProperty.Register(
nameof(RelativeHorizontalOffset), typeof(double), typeof(ComboBoxPopup),
new FrameworkPropertyMetadata(default(double)));

public double RelativeHorizontalOffset
{
get => (double)GetValue(RelativeHorizontalOffsetProperty);
set => SetValue(RelativeHorizontalOffsetProperty, value);
}

public ComboBoxPopup()
{
CustomPopupPlacementCallback = ComboBoxCustomPopupPlacementCallback;
Expand All @@ -230,7 +241,7 @@ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
private void SetupVisiblePlacementWidth(IEnumerable<DependencyObject> visualAncestry)
{
var parent = visualAncestry.OfType<Panel>().ElementAt(1);
VisiblePlacementWidth = TreeHelper.GetVisibleWidth((FrameworkElement)PlacementTarget, parent);
VisiblePlacementWidth = TreeHelper.GetVisibleWidth((FrameworkElement)PlacementTarget, parent, FlowDirection);
}

private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(
Expand All @@ -240,13 +251,13 @@ private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(

SetupVisiblePlacementWidth(visualAncestry);

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

if (ClassicMode
|| data.LocationX + data.PopupSize.Width - data.RealOffsetX > data.ScreenWidth
|| data.LocationX - data.RealOffsetX < 0
|| !preferUpIfSafe && data.LocationY - Math.Abs(data.NewDownY) < 0)
|| data.PopupLocationX + data.PopupSize.Width > data.ScreenWidth
|| data.PopupLocationX < 0
|| !preferUpIfSafe && data.LocationY + data.NewDownY < 0)
{
SetCurrentValue(PopupPlacementProperty, ComboBoxPopupPlacement.Classic);
return new[] { GetClassicPopupPlacement(this, data) };
Expand All @@ -272,7 +283,7 @@ private void SetChildTemplateIfNeed(ControlTemplate template)
}
}

private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualAncestry, Size popupSize, Size targetSize, Point offset)
private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualAncestry, Size popupSize, Size targetSize)
{
var locationFromScreen = PlacementTarget.PointToScreen(new Point(0, 0));

Expand All @@ -290,15 +301,11 @@ private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualA
var upVerticalOffsetIndependent = DpiHelper.TransformToDeviceY(mainVisual, UpVerticalOffset);
var newUpY = upVerticalOffsetIndependent - popupSize.Height + targetSize.Height;
var newDownY = DpiHelper.TransformToDeviceY(mainVisual, DownVerticalOffset);

double offsetX;
const int rtlHorizontalOffset = 20;

var offsetX = DpiHelper.TransformToDeviceX(mainVisual, RelativeHorizontalOffset);
if (FlowDirection == FlowDirection.LeftToRight)
offsetX = DpiHelper.TransformToDeviceX(mainVisual, offset.X);
offsetX = Round(offsetX);
else
offsetX = DpiHelper.TransformToDeviceX(mainVisual,
offset.X - targetSize.Width - rtlHorizontalOffset);
offsetX = Math.Truncate(offsetX - targetSize.Width);

return new PositioningData(
mainVisual, offsetX,
Expand All @@ -308,6 +315,8 @@ private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualA
screenHeight, screenWidth);
}

private static double Round(double val) => val < 0 ? (int)(val - 0.5) : (int)(val + 0.5);

private static PropertyChangedCallback CreateTemplatePropertyChangedCallback(ComboBoxPopupPlacement popupPlacement)
{
return delegate (DependencyObject d, DependencyPropertyChangedEventArgs e)
Expand Down Expand Up @@ -379,7 +388,7 @@ private struct PositioningData
public double OffsetX { get; }
public double NewUpY { get; }
public double NewDownY { get; }
public double RealOffsetX => (PopupSize.Width - TargetSize.Width) / 2.0;
public double PopupLocationX => LocationX + OffsetX;
public Size PopupSize { get; }
public Size TargetSize { get; }
public double LocationX { get; }
Expand All @@ -390,9 +399,9 @@ private struct PositioningData
public PositioningData(Visual mainVisual, double offsetX, double newUpY, double newDownY, Size popupSize, Size targetSize, double locationX, double locationY, double screenHeight, double screenWidth)
{
MainVisual = mainVisual;
OffsetX = offsetX;
NewUpY = newUpY;
NewDownY = newDownY;
OffsetX = Round(offsetX);
NewUpY = Round(newUpY);
NewDownY = Round(newDownY);
PopupSize = popupSize; TargetSize = targetSize;
LocationX = locationX; LocationY = locationY;
ScreenWidth = screenWidth; ScreenHeight = screenHeight;
Expand Down
16 changes: 10 additions & 6 deletions MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ComboBox.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
Height="{Binding ElementName=templateRoot, Path=ActualHeight}"/>
<Border Grid.Column="2" MinWidth="{StaticResource PopupLeftRightMargin}" Background="{Binding ElementName=PART_Popup, Path=Background}"/>
</Grid>
<Border Grid.Row="4" Height="{StaticResource PopupTopBottomMargin}"/>
<Border Grid.Row="4" Height="{StaticResource PopupTopBottomMargin}" Background="{Binding ElementName=PART_Popup, Path=Background}"/>
</Grid>
</Border>
</Grid>
Expand Down Expand Up @@ -399,6 +399,7 @@
<ControlTemplate x:Key="MaterialDesignFloatingHintComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot"
Background="{TemplateBinding Background}"
UseLayoutRounding="True"
SnapsToDevicePixels="True">
<AdornerDecorator>
<Grid x:Name="InnerRoot"
Expand Down Expand Up @@ -465,7 +466,7 @@
Opacity="{Binding Path=(wpf:HintAssist.HintOpacity), RelativeSource={RelativeSource TemplatedParent}}"
Text="{Binding Path=(wpf:TextFieldAssist.SuffixText), RelativeSource={RelativeSource TemplatedParent}}"
/>
<Button x:Name="PART_ClearButton" Height="Auto" Padding="2,0,-6,0" Style="{DynamicResource MaterialDesignToolButton}" Focusable="False">
<Button x:Name="PART_ClearButton" Height="Auto" Padding="2,-1,-1,-1" Style="{DynamicResource MaterialDesignToolButton}" Focusable="False">
<Button.Visibility>
<MultiBinding Converter="{StaticResource ClearTextConverter}">
<Binding ElementName="Hint" Path="IsContentNullOrEmpty" Converter="{StaticResource NotConverter}"/>
Expand All @@ -492,7 +493,8 @@
Grid.Column="0"
AllowsTransparency="True"
Focusable="False"
HorizontalOffset="-11.5"
HorizontalOffset="0"
RelativeHorizontalOffset="-23"
IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PlacementTarget="{Binding ElementName=templateRoot}"
SnapsToDevicePixels="True"
Expand All @@ -501,7 +503,7 @@
PopupAnimation="Fade"
VerticalOffset="0"
DefaultVerticalOffset="5"
DownVerticalOffset="-15.5"
DownVerticalOffset="-15"
UpVerticalOffset="15"
CornerRadius="2"
ContentMargin="6"
Expand Down Expand Up @@ -816,8 +818,10 @@
ClassicMode="True"
CornerRadius="0,0,2,2"
ContentMargin="6,0,6,6"
HorizontalOffset="-3"
VerticalOffset="-1"
HorizontalOffset="0"
RelativeHorizontalOffset="-6"
VerticalOffset="0"
DefaultVerticalOffset="-1"
ContentMinWidth="{Binding Path=ActualWidth, ElementName=templateRoot}"
UpContentTemplate="{StaticResource PopupContentUpTemplate}"
DownContentTemplate="{StaticResource PopupContentDownTemplate}"
Expand Down
4 changes: 3 additions & 1 deletion MaterialDesignThemes.Wpf/TreeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ namespace MaterialDesignThemes.Wpf
{
internal static class TreeHelper
{
public static double GetVisibleWidth(FrameworkElement element, UIElement parent)
public static double GetVisibleWidth(FrameworkElement element, FrameworkElement parent, FlowDirection flowDirection)
{
if (element == null) throw new ArgumentNullException(nameof(element));
if (parent == null) throw new ArgumentNullException(nameof(parent));

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

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