2011-06-28 35 views
5

में कुछ हद तक सोच रहा है, लेकिन मुझे लगता है कि मुझे कुछ बुनियादी समझ की कमी है कि विंडोज़ और डब्ल्यूपीएफ आंतरिक रूप से इसे कैसे समझने के लिए काम करते हैं।विंडो में एचडब्ल्यूएचएचओस्ट तत्व

मैं एक खिड़की है कि मुझे एक हवाई शीर्षक पट्टी (कार्यालय) की तरह पर WPF नियंत्रण आकर्षित देना चाहिए बनाया:

समस्या यह है। यह ठीक काम करता है जब तक कि मैं खिड़की में एक ह्वांडहोस्ट तत्व नहीं जोड़ता, इस मामले में जब भी मैं इसे फ्रेम का आकार बदलता हूं और HwndHost बहुत बुरी तरह झिलमिलाहट शुरू कर देता है (अन्य तत्व ठीक से प्रस्तुत करने लगते हैं)। मैंने WPF Shell Integration library से कस्टम फ्रेम विंडो कार्यान्वयन का उपयोग करने का भी प्रयास किया और नतीजा वही है, इसलिए मैं सोच रहा हूं कि यह पूरी तरह से मेरी गलती नहीं है।

निम्नलिखित कोड एक सरल संकलन योग्य प्रोग्राम है जो समस्या को पुन: उत्पन्न करता है। नमूना सी # में है लेकिन उत्तर होना आवश्यक नहीं है।

using System; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Interop; 
using System.Windows.Media; 
using System.Windows.Threading; 

namespace DwmTest { 
    class Program { 
     [STAThread] 
     static void Main() { 
      var w = new CustomFrameWindow{ Content = new WindowHost() }; 
      w.Show(); 
      ((Border)VisualTreeHelper.GetChild(w, 0)).Margin = new Thickness(11, 33, 11, 11); 
      Dispatcher.Run(); 
     } 
    } 

    public class CustomFrameWindow : Window { 

     const int resizeFrameWidth = 11; 
     const int captionHeight = 33; 

     public enum HT { CLIENT = 1, CAPTION = 2, LEFT = 10, RIGHT, TOP, TOPLEFT, TOPRIGHT, BOTTOM, BOTTOMLEFT, BOTTOMRIGHT } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct Margins { public int left, right, top, bottom; } 

     [DllImport("user32.dll")] 
     public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags); 

     [DllImport("dwmapi.dll")] 
     public static extern bool DwmDefWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, out IntPtr result); 

     [DllImport("dwmapi.dll", PreserveSig = false)] 
     public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Margins pMarInset); 

     protected override void OnSourceInitialized(EventArgs e) { 
      base.OnSourceInitialized(e); 

      var hWndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); 
      hWndSource.CompositionTarget.BackgroundColor = Colors.Transparent; 

      var nonClientArea = new Margins{ 
       left = resizeFrameWidth, top = captionHeight, bottom = resizeFrameWidth, right = resizeFrameWidth 
      }; 
      DwmExtendFrameIntoClientArea(hWndSource.Handle, ref nonClientArea); 

      hWndSource.AddHook(WndProc); 

      // FRAMECHANGED | NOMOVE | NOSIZE 
      SetWindowPos(hWndSource.Handle, new IntPtr(), 0, 0, 0, 0, 0x0020 | 0x0002 | 0x0001); 
     } 

     private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { 

      switch(msg) { 
       case 0x0083: // NCCALCSIZE 
        if(wParam != IntPtr.Zero) handled = true; 
        break; 
       case 0x0084: // NCHITTEST 
        handled = true; 

        IntPtr dwmHitTest; 
        if(DwmDefWindowProc(hwnd, msg, wParam, lParam, out dwmHitTest)) { 
         return dwmHitTest; 
        } 

        var mousePosition = PointFromScreen(new Point(lParam.ToInt32() & 0xFFFF, lParam.ToInt32() >> 16)); 

        var isTop = mousePosition.Y <= resizeFrameWidth; 
        var isBottom = mousePosition.Y >= ActualHeight - resizeFrameWidth; 
        var isLeft = mousePosition.X <= resizeFrameWidth; 
        var isRight = mousePosition.X >= ActualWidth - resizeFrameWidth; 

        var hitTest = HT.CLIENT; 
        if(isTop) { 
         if(isLeft) hitTest = HT.TOPLEFT; 
         else if(isRight) hitTest = HT.TOPRIGHT; 
         else hitTest = HT.TOP; 
        } 
        else if(isBottom) { 
         if(isLeft) hitTest = HT.BOTTOMLEFT; 
         else if(isRight) hitTest = HT.BOTTOMRIGHT; 
         else hitTest = HT.BOTTOM; 
        } 
        else if(isLeft) hitTest = HT.LEFT; 
        else if(isRight) hitTest = HT.RIGHT; 
        else if(mousePosition.Y <= captionHeight) hitTest = HT.CAPTION; 

        return new IntPtr((int)hitTest); 
      } 
      return IntPtr.Zero; 
     } 
    } 

    public class WindowHost : HwndHost { 
     [DllImport("user32.dll", SetLastError = true)] 
     static extern IntPtr CreateWindowEx(IntPtr exStyle, string lpClassName,string lpWindowName,int dwStyle,int x,int y,int nWidth,int nHeight,IntPtr hWndParent,IntPtr hMenu,IntPtr hInstance,IntPtr lpParam); 

     protected override HandleRef BuildWindowCore(HandleRef hWndParent) { 
      return new HandleRef(this, CreateWindowEx(IntPtr.Zero, "static", "", 0x40000000, 0, 0, 200, 200, hWndParent.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)); 
     } 
     protected override void DestroyWindowCore(HandleRef hwnd) { } 
    } 
} 

उत्तर

0

ठीक है, मैं अंत में एक समाधान मिल जाने, लेकिन मैं काला जादू पर लगता है कि यह एक सीमाओं ... वैसे भी यह पता चला है कि किसी भी रेक्ट खिड़की की तुलना में छोटे के साथ WM_NCCALCSIZE का जवाब समस्या का हल। तो उदाहरण के लिए नीचे की तरह दिखने के लिए हैंडलर को झिलमिलाहट को हटा दें!

  case WM.NCCALCSIZE: 
       if(wParam != IntPtr.Zero) { 
        handled = true; 
        var client = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); 
        client.Bottom -= 1; 
        Marshal.StructureToPtr(client, lParam, false); 
       } 
       break; 

मुझे पता नहीं क्यों यह काम कर रहा है है और मुझे यकीन है कि एक saner समाधान मौजूद है हूँ, इसलिए यदि किसी ने मुझे प्रबुद्ध सकता है मैं खुशी होगी।

+0

आप कभी भी किसी भी वैकल्पिक समाधान के रूप में मिला WS_CLIPCHILDREN जोड़कर हल? – Seth

+0

@ सेठ नहीं, लेकिन ईमानदार होने के बाद मैंने यह पाया कि मुझे यह पता चल गया है .. अगर आप कुछ समझते हैं तो मुझे बताएं! – Roald

2

मैं इसे एक शैली जब CreatWindowEx

protected override HandleRef BuildWindowCore(HandleRef hwndParent) 
{ 
    _hwndHost = Win32Api.CreateWindowEx(0, "Static", "", 
         (int) (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN), 
         0, 0, 
         _hostWidth, _hostHeight, 
         hwndParent.Handle, 
         IntPtr.Zero, 
         IntPtr.Zero, 
         0); 

}