我在网上找到了一个在 WinForms 中使用 Magnification API 的库,它工作得很好。我试图让它与 WPF 一起工作,但没有运气,没有异常或错误,一切似乎都很好。我开始认为它不适用于 WPF。
WinForms 代码:
public class Magnifier : IDisposable
{
private Form form;
private IntPtr hwndMag;
private float magnification;
private bool initialized;
private RECT magWindowRect = new RECT();
private System.Windows.Forms.Timer timer;
public Magnifier(Form form)
{
if (form == null)
throw new ArgumentNullException("form");
magnification = 2.0f;
this.form = form;
this.form.Resize += new EventHandler(form_Resize);
this.form.FormClosing += new FormClosingEventHandler(form_FormClosing);
timer = new Timer();
timer.Tick += new EventHandler(timer_Tick);
initialized = NativeMethods.MagInitialize();
if (initialized)
{
SetupMagnifier();
timer.Interval = NativeMethods.USER_TIMER_MINIMUM;
timer.Enabled = true;
}
}
void form_FormClosing(object sender, FormClosingEventArgs e)
{
timer.Enabled = false;
}
void timer_Tick(object sender, EventArgs e)
{
UpdateMaginifier();
}
void form_Resize(object sender, EventArgs e)
{
ResizeMagnifier();
}
~Magnifier()
{
Dispose(false);
}
protected virtual void ResizeMagnifier()
{
if ( initialized && (hwndMag != IntPtr.Zero))
{
NativeMethods.GetClientRect(form.Handle, ref magWindowRect);
// Resize the control to fill the window.
NativeMethods.SetWindowPos(hwndMag, IntPtr.Zero,
magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, 0);
}
}
public virtual void UpdateMaginifier()
{
if ((!initialized) || (hwndMag == IntPtr.Zero))
return;
POINT mousePoint = new POINT();
RECT sourceRect = new RECT();
NativeMethods.GetCursorPos(ref mousePoint);
int width = (int)((magWindowRect.right - magWindowRect.left) / magnification);
int height = (int)((magWindowRect.bottom - magWindowRect.top) / magnification);
sourceRect.left = mousePoint.x - width / 2;
sourceRect.top = mousePoint.y - height / 2;
// Don't scroll outside desktop area.
if (sourceRect.left < 0)
{
sourceRect.left = 0;
}
if (sourceRect.left > NativeMethods.GetSystemMetrics(NativeMethods.SM_CXSCREEN) - width)
{
sourceRect.left = NativeMethods.GetSystemMetrics(NativeMethods.SM_CXSCREEN) - width;
}
sourceRect.right = sourceRect.left + width;
if (sourceRect.top < 0)
{
sourceRect.top = 0;
}
if (sourceRect.top > NativeMethods.GetSystemMetrics(NativeMethods.SM_CYSCREEN) - height)
{
sourceRect.top = NativeMethods.GetSystemMetrics(NativeMethods.SM_CYSCREEN) - height;
}
sourceRect.bottom = sourceRect.top + height;
if (this.form == null)
{
timer.Enabled = false;
return;
}
if (this.form.IsDisposed)
{
timer.Enabled = false;
return;
}
// Set the source rectangle for the magnifier control.
NativeMethods.MagSetWindowSource(hwndMag, sourceRect);
// Reclaim topmost status, to prevent unmagnified menus from remaining in view.
NativeMethods.SetWindowPos(form.Handle, NativeMethods.HWND_TOPMOST, 0, 0, 0, 0,
(int)SetWindowPosFlags.SWP_NOACTIVATE | (int)SetWindowPosFlags.SWP_NOMOVE | (int)SetWindowPosFlags.SWP_NOSIZE);
// Force redraw.
NativeMethods.InvalidateRect(hwndMag, IntPtr.Zero, true);
}
public float Magnification
{
get { return magnification; }
set
{
if (magnification != value)
{
magnification = value;
// Set the magnification factor.
Transformation matrix = new Transformation(magnification);
NativeMethods.MagSetWindowTransform(hwndMag, ref matrix);
}
}
}
protected void SetupMagnifier()
{
if (!initialized)
return;
IntPtr hInst;
hInst = NativeMethods.GetModuleHandle(null);
// Make the window opaque.
form.AllowTransparency = true;
form.TransparencyKey = Color.Empty;
form.Opacity = 255;
// Create a magnifier control that fills the client area.
NativeMethods.GetClientRect(form.Handle, ref magWindowRect);
hwndMag = NativeMethods.CreateWindow((int)ExtendedWindowStyles.WS_EX_CLIENTEDGE, NativeMethods.WC_MAGNIFIER,
"MagnifierWindow", (int)WindowStyles.WS_CHILD | (int)MagnifierStyle.MS_SHOWMAGNIFIEDCURSOR |
(int)WindowStyles.WS_VISIBLE,
magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, form.Handle, IntPtr.Zero, hInst, IntPtr.Zero);
if (hwndMag == IntPtr.Zero)
{
return;
}
// Set the magnification factor.
Transformation matrix = new Transformation(magnification);
NativeMethods.MagSetWindowTransform(hwndMag, ref matrix);
}
protected void RemoveMagnifier()
{
if (initialized)
NativeMethods.MagUninitialize();
}
protected virtual void Dispose(bool disposing)
{
timer.Enabled = false;
if (disposing)
timer.Dispose();
timer = null;
form.Resize -= form_Resize;
RemoveMagnifier();
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
在 WPF 代码中,我刚刚修改了 Magnifer 类:
public class Magnifier : IDisposable
{
private Window window;
private IntPtr hwnd;
private IntPtr hwndMag;
private float magnification;
private bool initialized;
private RECT magWindowRect = new RECT();
private DispatcherTimer timer;
public Magnifier(Window window)
{
if (window == null)
throw new ArgumentNullException("form");
hwnd = new WindowInteropHelper(window).Handle;
magnification = 2.0f;
this.window = window;
this.window.SizeChanged += form_Resize;
this.window.Closing += form_FormClosing;
timer = new DispatcherTimer();
timer.Tick += timer_Tick;
initialized = NativeMethods.MagInitialize();
if (initialized)
{
SetupMagnifier();
timer.Interval = TimeSpan.FromMilliseconds(NativeMethods.USER_TIMER_MINIMUM);
timer.IsEnabled = true;
}
}
void form_FormClosing(object sender, CancelEventArgs e)
{
timer.IsEnabled = false;
}
void timer_Tick(object sender, EventArgs e)
{
UpdateMaginifier();
}
void form_Resize(object sender, RoutedEventArgs e)
{
ResizeMagnifier();
}
~Magnifier()
{
Dispose(false);
}
protected virtual void ResizeMagnifier()
{
if (initialized && (hwndMag != IntPtr.Zero))
{
NativeMethods.GetClientRect(hwnd, ref magWindowRect);
// Resize the control to fill the window.
NativeMethods.SetWindowPos(hwndMag, IntPtr.Zero,
magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, 0);
}
}
public virtual void UpdateMaginifier()
{
if ((!initialized) || (hwndMag == IntPtr.Zero))
return;
POINT mousePoint = new POINT();
RECT sourceRect = new RECT();
NativeMethods.GetCursorPos(ref mousePoint);
int width = (int)((magWindowRect.right - magWindowRect.left) / magnification);
int height = (int)((magWindowRect.bottom - magWindowRect.top) / magnification);
sourceRect.left = mousePoint.x - width / 2;
sourceRect.top = mousePoint.y - height / 2;
// Don't scroll outside desktop area.
if (sourceRect.left < 0)
{
sourceRect.left = 0;
}
if (sourceRect.left > NativeMethods.GetSystemMetrics(NativeMethods.SM_CXSCREEN) - width)
{
sourceRect.left = NativeMethods.GetSystemMetrics(NativeMethods.SM_CXSCREEN) - width;
}
sourceRect.right = sourceRect.left + width;
if (sourceRect.top < 0)
{
sourceRect.top = 0;
}
if (sourceRect.top > NativeMethods.GetSystemMetrics(NativeMethods.SM_CYSCREEN) - height)
{
sourceRect.top = NativeMethods.GetSystemMetrics(NativeMethods.SM_CYSCREEN) - height;
}
sourceRect.bottom = sourceRect.top + height;
if (this.window == null)
{
timer.IsEnabled = false;
return;
}
//if (this.window.IsDisposed)
//{
// timer.IsEnabled = false;
// return;
//}
// Set the source rectangle for the magnifier control.
NativeMethods.MagSetWindowSource(hwndMag, sourceRect);
// Reclaim topmost status, to prevent unmagnified menus from remaining in view.
NativeMethods.SetWindowPos(hwnd, NativeMethods.HWND_TOPMOST, 0, 0, 0, 0,
(int)SetWindowPosFlags.SWP_NOACTIVATE | (int)SetWindowPosFlags.SWP_NOMOVE | (int)SetWindowPosFlags.SWP_NOSIZE);
// Force redraw.
NativeMethods.InvalidateRect(hwndMag, IntPtr.Zero, true);
}
public float Magnification
{
get { return magnification; }
set
{
if (magnification != value)
{
magnification = value;
// Set the magnification factor.
Transformation matrix = new Transformation(magnification);
NativeMethods.MagSetWindowTransform(hwndMag, ref matrix);
}
}
}
protected void SetupMagnifier()
{
if (!initialized)
return;
IntPtr hInst;
hInst = NativeMethods.GetModuleHandle(null);
// Make the window opaque.
//form.AllowTransparency = true; (done in xaml)
//window.Background = Brushes.Transparent; (done in xaml)
//window.Opacity = 255;
// Create a magnifier control that fills the client area.
NativeMethods.GetClientRect(hwnd, ref magWindowRect);
hwndMag = NativeMethods.CreateWindow((int)ExtendedWindowStyles.WS_EX_CLIENTEDGE, NativeMethods.WC_MAGNIFIER,
"MagnifierWindow", (int)WindowStyles.WS_CHILD | (int)MagnifierStyle.MS_SHOWMAGNIFIEDCURSOR |
(int)WindowStyles.WS_VISIBLE,
magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, hwnd, IntPtr.Zero, hInst, IntPtr.Zero);
if (hwndMag == IntPtr.Zero)
{
return;
}
// Set the magnification factor.
Transformation matrix = new Transformation(magnification);
NativeMethods.MagSetWindowTransform(hwndMag, ref matrix);
}
protected void RemoveMagnifier()
{
if (initialized)
NativeMethods.MagUninitialize();
}
protected virtual void Dispose(bool disposing)
{
timer.IsEnabled = false;
//if (disposing)
// timer.Dispose();
timer = null;
window.SizeChanged -= form_Resize;
RemoveMagnifier();
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
XAML:
<Window x:Class="WpfApplication11.MagnifierWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MagnifierWindow" Height="350" Width="525" AllowsTransparency="True" WindowStyle="None">
<Window.Background>
<SolidColorBrush />
</Window.Background>
- 要使用这个类,我只是在 Form/Window 的构造函数中初始化它。
- 我在 WPF 中所做的更改:
- 添加了“hwnd”字段,在放大镜构造函数中对其进行了初始化。
- 将 System.Windows.Forms.Timer 替换为 System.Windows.Threading.DispatcherTimer。
- 将 form.Resize 更改为 window.SizeChanged 并将 form.FormClosing 更改为 window.Closing。但这无关紧要吗?
- 在 UpdateMagnifier() 中注释了 if (this.window.IsDisposed),因为 Window 中没有 IsDisposed 属性。
- 在 Dispose() 中评论了一个处置检查。
- 由于字符限制,我无法添加本机方法和结构。
帮助将不胜感激,谢谢。