internal void Initialize(Window window, FrameworkElement element = null) { if (window == null) { throw new ArgumentNullException(nameof(window)); } if (!window.IsInitialized) { throw new InvalidOperationException("Target Window has not been initialized."); } _targetWindow = window; _targetElement = element; ForbearScaling = WillForbearScalingIfUnnecessary && BuiltinFunction.IsScalingSupported(_targetWindow); if (IsPerMonitorDpiAware) { MonitorDpi = DpiChecker.GetDpiFromVisual(_targetWindow); if (MonitorDpi.Equals(SystemDpi) || ForbearScaling) { WindowDpi = MonitorDpi; } else { var newInfo = new WindowInfo { Dpi = MonitorDpi, Width = _targetWindow.Width * (double)MonitorDpi.X / SystemDpi.X, Height = _targetWindow.Height * (double)MonitorDpi.Y / SystemDpi.Y, }; Interlocked.Exchange(ref _dueInfo, newInfo); ChangeDpi(); } } else { MonitorDpi = SystemDpi; WindowDpi = MonitorDpi; } ColorProfilePath = ColorProfileChecker.GetColorProfilePath(_targetWindow); _targetSource = PresentationSource.FromVisual(_targetWindow) as HwndSource; _targetSource?.AddHook(WndProc); }
private void ChangeDpi(WindowStatus status = WindowStatus.None) { if (Interlocked.CompareExchange(ref _blocker, new object(), null) != null) { return; } try { // Take information which is to be tested from _dueInfo and set null in return. var testInfo = Interlocked.Exchange(ref _dueInfo, null); while (testInfo != null) { var testRect = new Rect(new Point(_targetWindow.Left, _targetWindow.Top), testInfo.Size); bool changesNow = true; switch (status) { case WindowStatus.None: case WindowStatus.SizeChanged: // None. break; case WindowStatus.LocationChanged: // Determine whether to reflect information now. var testDpi = DpiChecker.GetDpiFromRect(testRect); changesNow = testInfo.Dpi.Equals(testDpi); break; } if (changesNow) { // Update WindowDpi first so that it can provide new DPI during succeeding changes in target // Window. var oldDpi = WindowDpi; WindowDpi = testInfo.Dpi; switch (status) { case WindowStatus.None: case WindowStatus.LocationChanged: // Change location and size of target Window. Setting these properties may fire // LocationChanged and SizeChanged events twice in target Window. However, to use // SetWindowPos function, a complicated conversion of coordinates is required. // Also, it may cause a problem in applying styles if used in OnSourceInitialized // method. Debug.WriteLine($"Old Size: {_targetWindow.Width}-{_targetWindow.Height}"); _targetWindow.Left = testRect.Left; _targetWindow.Top = testRect.Top; _targetWindow.Width = testRect.Width; _targetWindow.Height = testRect.Height; Debug.WriteLine($"New Size: {_targetWindow.Width}-{_targetWindow.Height}"); break; case WindowStatus.SizeChanged: // None. break; } // Scale contents of target Window. var content = _targetElement ?? _targetWindow.Content as FrameworkElement; if (content != null) { content.LayoutTransform = (testInfo.Dpi.Equals(SystemDpi)) ? Transform.Identity : new ScaleTransform( (double)WindowDpi.X / SystemDpi.X, (double)WindowDpi.Y / SystemDpi.Y); } // Fire DpiChanged event last so that it can be utilized to supplement preceding changes // in target Window. DpiChanged?.Invoke(this, new DpiChangedEventArgs(oldDpi, WindowDpi)); // Take new information which is to be tested from _dueInfo again for the case where new // information has been stored during this operation. If there is new information, repeat // the operation. testInfo = Interlocked.Exchange(ref _dueInfo, null); } else { // Store old information which has been tested but determined not to be reflected now to // _dueInfo and take new information in return. If there is new information, repeat // the operation. If not, old information stored back may be overwritten by new information // later but has a chance to be tested again in the case where it is the last information // at this move/resize. In such case, the information may be tested at next move/resize. testInfo = Interlocked.Exchange(ref _dueInfo, testInfo); } } } finally { Interlocked.Exchange(ref _blocker, null); } }