2012-06-18 11 views
27

Este es un código que recogí e intenté implementar. Su objetivo es crear una capa de formulario que sea transparente, de pantalla completa, sin bordes, de clic y siempre sobre otras ventanas. Luego, le permite dibujar usando Directx sobre la parte superior que de otro modo sería transparente.Capa transparente de la ventana que es click-through y siempre permanece en la parte superior

Las partes que no funcionan son la parte de clic y la renderización de directx. Cuando lo ejecuto, básicamente tengo un campo de fuerza invisible frente a todas las demás ventanas y tengo que desplazarme hacia el visual studio para presionar ALT F5 rápidamente y finalizar la depuración (de modo que siempre funciona la transparencia y siempre arriba). He estado tratando de descubrir por qué esas partes no funcionan, pero mis habilidades de novato C# me fallan. con suerte alguien puede descubrir por qué y proporcionar una modificación.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Globalization; 
using Microsoft.DirectX; 
using Microsoft.DirectX.Direct3D; 
using System.Threading; 


namespace MinimapSpy 
{ 
public partial class Form1 : Form 
{ 

    private Margins marg; 

    //this is used to specify the boundaries of the transparent area 
    internal struct Margins 
    { 
     public int Left, Right, Top, Bottom; 
    } 

    [DllImport("user32.dll", SetLastError = true)] 

    private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); 

    [DllImport("user32.dll")] 

    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); 

    [DllImport("user32.dll")] 

    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); 

    public const int GWL_EXSTYLE = -20; 

    public const int WS_EX_LAYERED = 0x80000; 

    public const int WS_EX_TRANSPARENT = 0x20; 

    public const int LWA_ALPHA = 0x2; 

    public const int LWA_COLORKEY = 0x1; 

    [DllImport("dwmapi.dll")] 
    static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMargins); 

    private Device device = null; 



    public Form1() 
    { 

     //Make the window's border completely transparant 
     SetWindowLong(this.Handle, GWL_EXSTYLE, 
       (IntPtr)(GetWindowLong(this.Handle, GWL_EXSTYLE)^WS_EX_LAYERED^WS_EX_TRANSPARENT)); 

     //Set the Alpha on the Whole Window to 255 (solid) 
     SetLayeredWindowAttributes(this.Handle, 0, 255, LWA_ALPHA); 

     //Init DirectX 
     //This initializes the DirectX device. It needs to be done once. 
     //The alpha channel in the backbuffer is critical. 
     PresentParameters presentParameters = new PresentParameters(); 
     presentParameters.Windowed = true; 
     presentParameters.SwapEffect = SwapEffect.Discard; 
     presentParameters.BackBufferFormat = Format.A8R8G8B8; 

     this.device = new Device(0, DeviceType.Hardware, this.Handle, 
     CreateFlags.HardwareVertexProcessing, presentParameters); 


     Thread dx = new Thread(new ThreadStart(this.dxThread)); 
     dx.IsBackground = true; 
     dx.Start(); 
     InitializeComponent(); 

    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     //Create a margin (the whole form) 
     marg.Left = 0; 
    marg.Top = 0; 
     marg.Right = this.Width; 
     marg.Bottom = this.Height; 

     //Expand the Aero Glass Effect Border to the WHOLE form. 
     // since we have already had the border invisible we now 
     // have a completely invisible window - apart from the DirectX 
     // renders NOT in black. 
    DwmExtendFrameIntoClientArea(this.Handle, ref marg); 

    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 
    private void dxThread() 
    { 
     while (true) 
     { 
      //Place your update logic here 
      device.Clear(ClearFlags.Target, Color.FromArgb(0, 0, 0, 0), 1.0f, 0); 
      device.RenderState.ZBufferEnable = false; 
      device.RenderState.Lighting = false; 
      device.RenderState.CullMode = Cull.None; 
      device.Transform.Projection = Matrix.OrthoOffCenterLH(0, this.Width, this.Height, 0, 0, 1); 
      device.BeginScene(); 

      //Place your rendering logic here 

      device.EndScene(); 
      //device.Present(); 
     } 

     this.device.Dispose(); 
     Application.Exit(); 
    } 

} 
+0

No creo que pueda hacer clic en WinForms, es posible que tenga que habilitarlo/deshabilitarlo bajo demanda – fenix2222

+0

Estoy bastante seguro de que puede hacerlo. – user1166981

+0

¿Puedes probar esto: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx – fenix2222

Respuesta

22

Aquí hay un código de refinado completo de la muestra para hacer una ventana superior - hacer clic a través de - transparente (= mezcla alfa). La muestra hace una rueda de color rotativa que se procesa con DirectX, o en realidad con XNA 4.0, porque creo que Microsoft ha dejado de desarrollar el directx gestionado y favorece a XNA en la actualidad.

using System; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 
using Microsoft.Xna.Framework.Graphics; 

namespace ClickThroughXNA 
{ 
    public partial class Form1 : Form 
    { 
     // Directx graphics device 
     GraphicsDevice dev = null;   
     BasicEffect effect = null;  

     // Wheel vertexes 
     VertexPositionColor[] v = new VertexPositionColor[100]; 

     // Wheel rotation 
     float rot = 0; 

     public Form1() 
     { 
      InitializeComponent(); 

      StartPosition = FormStartPosition.CenterScreen; 
      Size = new System.Drawing.Size(500, 500); 
      FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // no borders 

      TopMost = true;  // make the form always on top      
      Visible = true;  // Important! if this isn't set, then the form is not shown at all 

      // Set the form click-through 
      int initialStyle = GetWindowLong(this.Handle, -20); 
      SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20); 

      // Create device presentation parameters 
      PresentationParameters p = new PresentationParameters(); 
      p.IsFullScreen = false; 
      p.DeviceWindowHandle = this.Handle; 
      p.BackBufferFormat = SurfaceFormat.Vector4; 
      p.PresentationInterval = PresentInterval.One; 

      // Create XNA graphics device 
      dev = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, p); 

      // Init basic effect 
      effect = new BasicEffect(dev); 

      // Extend aero glass style on form init 
      OnResize(null); 
     } 


     protected override void OnResize(EventArgs e) 
     { 
      int[] margins = new int[] { 0, 0, Width, Height }; 

      // Extend aero glass style to whole form 
      DwmExtendFrameIntoClientArea(this.Handle, ref margins); 
     } 


     protected override void OnPaintBackground(PaintEventArgs e) 
     { 
      // do nothing here to stop window normal background painting 
     } 


     protected override void OnPaint(PaintEventArgs e) 
     {     
      // Clear device with fully transparent black 
      dev.Clear(new Microsoft.Xna.Framework.Color(0, 0, 0, 0.0f)); 

      // Rotate wheel a bit 
      rot+=0.1f; 

      // Make the wheel vertexes and colors for vertexes 
      for (int i = 0; i < v.Length; i++) 
      {      
       if (i % 3 == 1) 
        v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + rot) * (Math.PI * 2f/(float)v.Length)), (float)Math.Cos((i + rot) * (Math.PI * 2f/(float)v.Length)), 0); 
       else if (i % 3 == 2) 
        v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + 2 + rot) * (Math.PI * 2f/(float)v.Length)), (float)Math.Cos((i + 2 + rot) * (Math.PI * 2f/(float)v.Length)), 0); 

       v[i].Color = new Microsoft.Xna.Framework.Color(1 - (i/(float)v.Length), i/(float)v.Length, 0, i/(float)v.Length); 
      } 

      // Enable position colored vertex rendering 
      effect.VertexColorEnabled = true; 
      foreach (EffectPass pass in effect.CurrentTechnique.Passes) pass.Apply(); 

      // Draw the primitives (the wheel) 
      dev.DrawUserPrimitives(PrimitiveType.TriangleList, v, 0, v.Length/3, VertexPositionColor.VertexDeclaration); 

      // Present the device contents into form 
      dev.Present(); 

      // Redraw immediatily 
      Invalidate();    
     } 


     [DllImport("user32.dll", SetLastError = true)] 
     static extern int GetWindowLong(IntPtr hWnd, int nIndex); 

     [DllImport("user32.dll")] 
     static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 

     [DllImport("dwmapi.dll")] 
     static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref int[] pMargins); 

    } 
} 
+0

La propiedad 'TransparentKey' del formulario debería poder hacer que cierto color en el formulario sea transparente –

+0

Sí, pero como yo entendía, quería que el formulario fuera un clic, incluso en áreas donde la ventana representa algo. – Jaska

+0

Solo afecta el aspecto, no cómo funciona –

12

Una pequeña extensión/modificación al código de Jaska, que la forma es transparente

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 

     this.TopMost = true; // make the form always on top 
     this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // hidden border 
     this.WindowState = FormWindowState.Maximized; // maximized 
     this.MinimizeBox = this.MaximizeBox = false; // not allowed to be minimized 
     this.MinimumSize = this.MaximumSize = this.Size; // not allowed to be resized 
     this.TransparencyKey = this.BackColor = Color.Red; // the color key to transparent, choose a color that you don't use 
    } 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      CreateParams cp = base.CreateParams; 
      // Set the form click-through 
      cp.ExStyle |= 0x80000 /* WS_EX_LAYERED */ | 0x20 /* WS_EX_TRANSPARENT */; 
      return cp; 
     } 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 
     // draw what you want 
     e.Graphics.FillEllipse(Brushes.Blue, 30, 30, 100, 100); 
    } 
} 
+0

Esto es solo para chicos de GDI, según el código original que estaba buscando para el dibujo de directx. Al final lo resolví, así que si alguno de ustedes todavía quiere los puntos, simplemente actualice sus respuestas para directx para que una respuesta sea archivada para futuros usuarios. Esperaré hasta después de los 7 días y luego publicaré mi respuesta si no. Gracias. – user1166981

+1

Sí, 'CreateParams' es una forma mucho mejor de controlar estilos que con' SetWindowLong'. –

+0

@BenVoigt Sí. Si alguien quiere saber más, ver: http://stackoverflow.com/q/13986363/1386111 –

1

cambiar su estilo de ventana extendida sólo a WS_EX_LAYERED, estilo de ventana con sólo el WS_POPUP (NO WS_SIZEBOX) y asegúrese de use DwmExtendFrameIntoClientArea con todos -1 y esto producirá ventanas transparentes con soporte en capas: a la baja es necesario realizar una bltbit con GDI desde una representación de DirectX fuera de pantalla. No es óptimo, pero funciona. Esto proporciona clics del mouse + renderizado de directx + transparencia. Lo malo es que tendrá que informar a GDI en cualquier momento, extraer el búfer de DirectX (todo o solo las partes dañadas) y escribirlas en el screem con bltbit.

Estableciendo el estilo de ventana extendida en WS_EX_COMPOSITED y DwmExtendedFrameIntoClientArea con todos -1 (similar a lo anterior, WS_POPUP en el estilo de ventana normal). De esto puede ejecutar directx pero sin clics de mouse. En este punto, puede definir rutas irregulares para la máscara de acceso y pasarla a Windows, no es perfecta, pero si conoce un área general (no regular) que puede pasar, funcionará.

Todavía estoy tratando de encontrar una forma real de usar opengl/directx en plataformas Mac o Windows que pueden pasar a través de clics del mouse sin tener que hacer un bitblt a un sistema de renderizado heredado.

Cuestiones relacionadas