SORU
30 Mart 2011, ÇARŞAMBA


Nasıl genişletilmiş pencere çerçevesi sürükleyerek WPF pencere hareketli yapabilirim?

Windows Explorer ve Internet Explorer gibi uygulamalarda, bir başlık çubuğu altında genişletilmiş çerçeve alanları kap ve pencere etrafında sürükleyin.

İçin Win uygulamalarında, formlar ve denetimler gibi yakın yerel Win32 API olarak elde edebilirsiniz; biri sadece geçersiz kılma WndProc() işleyicisi form, işlem WM_NCHITTEST uyarı ve hile sistemi içine düşünce bir tıklama çerçeve alanı gerçekten bir tıklayın başlık çubuğu döndürerek HTCAPTION. Güzel bir etki için kendi Win uygulamalarım böyle bir şey yapmadım.

WPF de WndProc() benzer bir yöntemi uygulamak ve benim WPF penceresi istemci alanının içine pencere çerçevesi uzatırken kolu bu bağlar, bu gibi olabilir:

// In MainWindow
// For use with window frame extensions
private IntPtr hwnd;
private HwndSource hsource;

private void Window_SourceInitialized(object sender, EventArgs e)
{
    try
    {
        if ((hwnd = new WindowInteropHelper(this).Handle) == IntPtr.Zero)
        {
            throw new InvalidOperationException("Could not get window handle for the main window.");
        }

        hsource = HwndSource.FromHwnd(hwnd);
        hsource.AddHook(WndProc);

        AdjustWindowFrame();
    }
    catch (InvalidOperationException)
    {
        FallbackPaint();
    }
}

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case DwmApiInterop.WM_NCHITTEST:
            handled = true;
            return new IntPtr(DwmApiInterop.HTCAPTION);

        default:
            return IntPtr.Zero;
    }
}

Sorun körü körüne handled = true ayarı HTCAPTION tıklayarak dönüyorum beriher yerdeama pencere simgesini veya kontrol düğmeleri pencere sürüklenmesine neden olur. Yani, her şeyi aşağıdaki kırmızı vurgulanan sürüklemeye neden olur. Bu bile yeniden boyutlandırma pencere olmayan istemci alanı), yanlarda kolları içerir. WPF denetimleri, yani metin kutuları ve sekme denetimi, aynı zamanda bir sonucu olarak tıklama almayı durdurmak için:

İstediğim içindir

  1. başlık çubuğu, ve
  2. istemci alanının bölgeler...
  3. ... benim kontroller tarafından işgal değil

sürüklenebilir. Yani, ben sadece bu kırmızı bölgeler sürüklenebilir olmasını (istemci alan başlık çubuğu):

Nasıl WndProc() benim yöntemi ve benim penceresinin XAML geri kalanını değiştir/HTCAPTION dönmelidir belirlemek için arka plan Kodu, ve hangi gerekmiyor mu? Düşünüyorum birşeyler boyunca çizgiler kullanarak Points kontrol etmek için konumu tıklatın karşı konumlarını benim kontroller, ama emin değilim nasıl WPF arazi.

DÜZENLEME [4/24]:bu konuda basit bir şekilde görünmez bir kontrol veya pencerenin kendisi bile, pencerede DragMove() (Ross's answer) çağırarak MouseLeftButtonDown yanıt. Sorun nedense DragMove() eğer pencere Windows 7 Aero Snap ile güzel çalmıyor yani ekranı çalışmıyor. Windows 7 entegrasyonu için gidiyorum beri, benim durumumda kabul edilebilir bir çözüm değil.

CEVAP
31 Mart 2011, PERŞEMBE


Örnek kod

Bu sabah aldığım bir e-posta sayesinde, çalışan örnek bir uygulama bu çok işlevsellik gösteren yapmak için istenir oldu. Şimdi yaptım; http://wpfdraggableframe.codeplex.com de bulabilirsiniz. Sadece indirin en son sürümüKaynak Kodusekme, Visual Studio açın ve çalıştırın.

Tam uygulama okumalıdır. MIT lisanslı, ama olacak muhtemelen getiriyoruz ve koyarak bit kodunu etrafında kendi yerine kullanarak uygulama kod tam değil lisans durak yapıyor o da. Uygulamanın ana penceresinin tasarım her yerde wireframes yakın benzer yukarıda olmadığını biliyorum da, bunu soru olarak yöneltilen aynıdır.

Bu birileri yardımcı olur umarım!

Adım adım çözüm

Ben nihayet çözüldü. Doğru yönde beni işaret için Jeffrey L Whitledge için teşekkürler!Onun yerine bir cevap değilse bir çözüm bulmak için asla beceremezdim çünkü kabul edildi.DÜZENLEME [9/8]:bu cevap şimdi daha tam olarak kabul edilmez, Jeffrey onun yardımı yerine kocaman bir ödül veriyorum.

Gelecek nesillerin iyiliği için, ben yaptım oldu ne (ben Jeffrey cevabı ile ilgili alıntı):

Fare konumu (her iki, belki de yapın?), ve oluşturmak için bunu kullanın Point (muhtemelen koordine bir çeşit dönüşüm?).

Bu Bilgi WM_NCHITTEST mesaj lParam elde edilebilir. İmleci alt sıra sözcük. x-koordinatı ve y-koordinatı ise imleci yüksek sıralı sözcük, MSDN describes.

Tüm ekran göreceli koordinatlar beri, pencereme Visual.PointFromScreen() pencere alana göre koordinatları dönüştürmek için aramam gerek.

Sonra yapılan statik yöntem VisualTreeHelper.HitTest(Visual,Point) this Point geçen arayın. Dönüş değeri en yüksek Z-Sırası ile kontrol gösterir.

Görsel olarak üst düzey Grid kontrol yerine this geçmek noktası karşı test etmem gerekiyordu. Aynı şekilde sonuç null yerine eğer penceresi varsa kontrol olup olmadığını kontrol etmem gerekiyordu. Eğer boş ise, imleç ızgaranın alt denetimlerin hiç vuramadık — diğer bir deyişle, boş pencere çerçevesi bölgeyi vurdu. Her neyse, önemli olan VisualTreeHelper.HitTest() yöntemi kullanın.

Şimdi, bunu söyledikten sonra, eğer benim adımı takip ediyorsanız sizin için geçerli olan iki uyarılar vardır:

  1. Eğer tüm pencere kapağı ve kısmen pencere çerçevesi uzatmak yerine herhangi eğer, müşteri alan bir dolgu maddesi olarak pencere çerçevesi ile dolu değil o dikdörtgenin üzerine bir kontrol yerleştirmek zorunda.

    Benim durumumda, sekme kontrolüm içerik alanı diyagramlar gösterildiği gibi dikdörtgen bir alan gayet iyi uyuyor. Uygulamanızda, Rectangle bir şekil veya Panel kontrol etmek için kullanılır ve uygun renge boya. Bu şekilde kontrol vurmak olacak.

    İstemci alanı dolgu ile ilgili bu sorun, bir sonraki yol açar:

  2. Eğer ya da üst düzey kılavuz diğer denetim genişletilmiş pencere çerçevesi üzerinde arka plan doku veya degrade varsa,tüm ızgara alanıhit çok iyi yanıt verir, arka plan tamamen şeffaf herhangi bölgelerine (Hit Testing in the Visual Layer) bile. Bu durumda, kılavuz kendisine karşı vurur göz ardı etmek isteyeceksiniz, ve sadece içindeki denetimleri dikkat edin.

Bu nedenle:

// In MainWindow
private bool IsOnExtendedFrame(int lParam)
{
    int x = lParam << 16 >> 16, y = lParam >> 16;
    var point = PointFromScreen(new Point(x, y));

    // In XAML: <Grid x:Name="windowGrid">...</Grid>
    var result = VisualTreeHelper.HitTest(windowGrid, point);

    if (result != null)
    {
        // A control was hit - it may be the grid if it has a background
        // texture or gradient over the extended window frame
        return result.VisualHit == windowGrid;
    }

    // Nothing was hit - assume that this area is covered by frame extensions anyway
    return true;
}

Şimdi pencere ve pencerenin sadece boş alanlara tıklayıp sürükleyerek taşınabilir.

Ama hepsi bu değil. Olmayan istemci alanı pencerenin sınırları oluşturan da bu pencere artık yeniden boyutlandırılabilir oldu HTCAPTION etkilenen ilk resimde hatırlayın.

Bunu düzeltmek için, imleç istemci alanı veya olmayan istemci alanının isabet olup olmadığını kontrol etmem gerekiyordu. Bu kontrol etmek için DefWindowProc() işlevini kullanın ve eğer HTCLIENT iade aranıyor:

// In my managed DWM API wrapper class, DwmApiInterop
public static bool IsOnClientArea(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam)
{
    if (uMsg == WM_NCHITTEST)
    {
        if (DefWindowProc(hWnd, uMsg, wParam, lParam).ToInt32() == HTCLIENT)
        {
            return true;
        }
    }

    return false;
}

// In NativeMethods
[DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);

Son olarak, burada son pencere yordamı yöntemi

// In MainWindow
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case DwmApiInterop.WM_NCHITTEST:
            if (DwmApiInterop.IsOnClientArea(hwnd, msg, wParam, lParam)
                && IsOnExtendedFrame(lParam.ToInt32()))
            {
                handled = true;
                return new IntPtr(DwmApiInterop.HTCAPTION);
            }

            return IntPtr.Zero;

        default:
            return IntPtr.Zero;
    }
}

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Bogdan Gabriel

    Bogdan Gabri

    5 Aralık 2006
  • Pituvision

    Pituvision

    11 Mart 2006
  • waterfairy17

    waterfairy17

    9 Aralık 2007