Microsoft 开发人员网络 > 论坛主页 > Windows Presentation Foundation (WPF) > How can I make the WPF Window show on the correct position described as pixels in 120DPI?
提出问题提出问题
 

已答复How can I make the WPF Window show on the correct position described as pixels in 120DPI?

  • 2007年10月12日 8:26Wei.Sun 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     

     

    Hi All,

       I need to show a window implemented by WPF when user clicks a button, and makes the left-top corner of the window set to the left-bottom corner of the button. (The button is not implemented by WPF.)

     

    as this:

     

    System.Windows.Window myWindow = new System.Windows.Window();

    myWindow.Left = x;

    myWindow.Top = y;

    myWindow.Show();

     

    the (x, y) is the point of the left-bottom corner of the button (in pixels)

    When the display mode is set to 96dpi, the codes works very well. But when it is 120DPI, the window will leave away the button because of the WPF's device independecy.

     

    Who can tell me how can I convert the x,y to the correct value and make the window show to the coorrect position? Thanks.

答案

  • 2007年10月12日 12:14Tim DawsonMVP用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     已答复

    It's actually slightly easier than Dr WPF alluded to. You can get the DPI settings in WPF once you have a PresentationSource, which you can get from any visual being presented.

     

    You can also use the TransformToDevice and TransformFromDevice properties to calculate the WPF "pixel" location for a window, given a win32 (device) pixel location. That's a slightly neater way that calculating it manually using the dpi, although obviously it's going to be the same calculation. So that's how you position a WPF window over a win32 window.

     

    PresentationSource source = PresentationSource.FromVisual(this);

    if (source != null)

    {

    dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;

    dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;

    }

     

  • 2007年10月12日 16:16Dr. WPF 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     已答复

    Thx Tim!  Much better solution if you have WPF visuals and makes total sense that the transform methods exist on CompositionTarget. Smile

     

    There still remains the problem that Wei does not yet have any WPF visuals (at least that's what it sounds like based on the original post).

     

    Wei, if you don't yet have any WPF visuals, you will need to do a little trickery if you use Tim's approach.  Unfortunately, the CompositionTarget won't exist until the HwndSource is created, which for a Window doesn't happen until the first time it is shown.  There are a few ways you could do it. 

     

    1) create a hidden window and use its CompositionTarget to determine the initial Left and Top coordinates for your window (although this may cause a momentary flash when the hidden window becomes active),

     

    2) Initially position your window off of the viewable screen, and then add code to the Loaded event that repositions it based on where you really want it,

     

    3) use my earlier approach from your other app to get the DPI settings and determine the initial coordinates for the window  (and if your other app is Win32, then you can just call the unmanaged functions directly without p/invoke).

全部回复

  • 2007年10月12日 9:41AlexTre 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     

    try this:   

    win.WindowStartupLocation = WindowStartupLocation.Manual;
                    win.Left = this.Left + x
                    win.Top = this.Top +y;

                         win.show();

     

    it works to me

  • 2007年10月12日 9:47Dr. WPF 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     

    I don't think your solution will work, Alex.  According to Wei's original post, the invoking button is *not* a WPF button (which likely means the host app is not WPF either) so I'm guessing they don't have any coordinates in device independent pixels.

     

    To convert pixels to device independent "pixels" (dips), which aren't really pixels at all, you use the following equation:

     

    dips = pixels * 96 / dpi

     

    It is rather unfortunate that Microsoft did not give us a SystemParameters setting for DPI (or at least an interop helper function).  As such, to programmatically determine the DPI setting, you must make a few unmanaged calls.  Namely, you must obtain a DC and call GetDeviceCaps to query the LOGPIXELSX (or LOGPIXELSY) setting.

     

    The following code will do the trick:

     

    Code Block

     

    [DllImport("User32.dll")]

    private static extern IntPtr GetDC(HandleRef hWnd);

     

    [DllImport("User32.dll")]

    private static extern int ReleaseDC(HandleRef hWnd, HandleRef hDC);

     

    [DllImport("GDI32.dll")]

    private static extern int GetDeviceCaps(HandleRef hDC, int nIndex);

     

    private static int _dpi = 0;

    public static int DPI

    {

        get

        {

            if (_dpi == 0)

            {

                HandleRef desktopHwnd = new HandleRef(null, IntPtr.Zero);

                HandleRef desktopDC = new HandleRef(null, GetDC(desktopHwnd));

                _dpi = GetDeviceCaps(desktopDC, 88 /*LOGPIXELSX*/);

                ReleaseDC(desktopHwnd, desktopDC);

            }

            return _dpi;

        }

    }

     

    public static double ConvertPixelsToDIPixels(int pixels)

    {

        return (double)pixels * 96 / DPI;

    }

     

     

     

  • 2007年10月12日 12:14Tim DawsonMVP用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     已答复

    It's actually slightly easier than Dr WPF alluded to. You can get the DPI settings in WPF once you have a PresentationSource, which you can get from any visual being presented.

     

    You can also use the TransformToDevice and TransformFromDevice properties to calculate the WPF "pixel" location for a window, given a win32 (device) pixel location. That's a slightly neater way that calculating it manually using the dpi, although obviously it's going to be the same calculation. So that's how you position a WPF window over a win32 window.

     

    PresentationSource source = PresentationSource.FromVisual(this);

    if (source != null)

    {

    dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;

    dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;

    }

     

  • 2007年10月12日 16:16Dr. WPF 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     已答复

    Thx Tim!  Much better solution if you have WPF visuals and makes total sense that the transform methods exist on CompositionTarget. Smile

     

    There still remains the problem that Wei does not yet have any WPF visuals (at least that's what it sounds like based on the original post).

     

    Wei, if you don't yet have any WPF visuals, you will need to do a little trickery if you use Tim's approach.  Unfortunately, the CompositionTarget won't exist until the HwndSource is created, which for a Window doesn't happen until the first time it is shown.  There are a few ways you could do it. 

     

    1) create a hidden window and use its CompositionTarget to determine the initial Left and Top coordinates for your window (although this may cause a momentary flash when the hidden window becomes active),

     

    2) Initially position your window off of the viewable screen, and then add code to the Loaded event that repositions it based on where you really want it,

     

    3) use my earlier approach from your other app to get the DPI settings and determine the initial coordinates for the window  (and if your other app is Win32, then you can just call the unmanaged functions directly without p/invoke).