diff --git a/src/DynamoCoreWpf/Controls/InPortContextMenu.xaml b/src/DynamoCoreWpf/Controls/InPortContextMenu.xaml
index 25af62e9991..a1b674e3e47 100644
--- a/src/DynamoCoreWpf/Controls/InPortContextMenu.xaml
+++ b/src/DynamoCoreWpf/Controls/InPortContextMenu.xaml
@@ -139,13 +139,13 @@
diff --git a/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs
index b81d91c0bcf..d5dc7d73e1c 100644
--- a/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs
@@ -375,6 +375,8 @@ internal void SetupPortContextMenuPlacement(Popup popup)
///
private void ConfigurePopupPlacement(Popup popup, double zoom)
{
+ var dpiScale = GetDpiScale();
+
popup.CustomPopupPlacementCallback = (popupSize, targetSize, offset) =>
{
double x;
@@ -382,11 +384,11 @@ private void ConfigurePopupPlacement(Popup popup, double zoom)
if (this is InPortViewModel)
{
- x = -popupSize.Width + proxyPortContextMenuOffset * zoom;
+ x = -popupSize.Width + proxyPortContextMenuOffset * zoom * dpiScale;
}
else
{
- x = targetSize.Width - proxyPortContextMenuOffset * zoom;
+ x = targetSize.Width - proxyPortContextMenuOffset * zoom * dpiScale;
}
return new[] { new CustomPopupPlacement(new Point(x, y), PopupPrimaryAxis.None) };
@@ -398,6 +400,7 @@ private CustomPopupPlacement[] PlacePortContextMenu(Size popupSize, Size targetS
// The actual zoom here is confusing
// What matters is the zoom factor measured from the scaled : unscaled node size
var zoom = node.WorkspaceViewModel.Zoom;
+ var dpiScale = GetDpiScale();
double x;
var scaledWidth = autocompletePopupSpacing * targetSize.Width / node.ActualWidth;
@@ -421,13 +424,31 @@ private CustomPopupPlacement[] PlacePortContextMenu(Size popupSize, Size targetS
var rowOffset = PortModel.Index * PortModel.Height * zoom;
var customNodeOffset = NodeModel.CustomNodeTopBorderHeight * zoom;
- var y = popupHeightOffset + headerHeightOffset + portHalfHeight + rowOffset + customNodeOffset;
+ // popupSize.Height is already DPI-scaled (in screen pixels), so we do NOT apply dpiScale to it
+ // All other layout values are in logical units and must be multiplied by dpiScale for correct placement
+ var y = popupHeightOffset + (headerHeightOffset + portHalfHeight + rowOffset + customNodeOffset) * dpiScale;
var placement = new CustomPopupPlacement(new Point(x, y), PopupPrimaryAxis.None);
return new[] { placement };
}
+ private static double GetDpiScale()
+ {
+ double dpiScale = 1.0;
+ try
+ {
+ var source = PresentationSource.FromVisual(Application.Current.MainWindow);
+ dpiScale = source?.CompositionTarget?.TransformToDevice.M22 ?? 1.0;
+ }
+ catch
+ {
+ // fallback to default DPI scale of 1.0 if anything fails
+ }
+
+ return dpiScale;
+ }
+
private void WorkspacePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)