Como puedo semitransparentar un picturebox o parte de un form?

Iniciado por okik, 16 Marzo 2015, 19:40 PM

0 Miembros y 1 Visitante están viendo este tema.

okik

Hola
Llevo tiempo intentando semitransparentar un picture o una sólo una parte de un formulario. A día de hoy no lo he conseguido. No me importa que sea con vb6 o vb.net


He hecho un poquito de trampa, para simular el efecto. Aquí dejo una muestra de lo que quiero. Pero repito, que sólo simula el efecto.

1 Formulario: Form1
1 Formulario: Form2 (establecer como BorderStyle=0)
1 PictureBox en Form1
1 PictureBox en Form2
1 Control Timer en el Form1: Timer1


En un MÓDULO:

Código (vb) [Seleccionar]

Option Explicit
Private Declare Function EnableWindow Lib "user32" (ByVal hwnd As Long, ByVal fEnable As Long) As Long
'Obtiene posición de una ventana u objeto
Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Type RECT
 Left As Long
 Top As Long
 Right As Long
 Bottom As Long
End Type

Private Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hwnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_EXSTYLE = (-20)
Private Const LWA_ALPHA = &H2
Private Const WS_EX_LAYERED = &H80000

 
Public Function CreateObjectTransparent(ByVal frmMain As Form, ByVal frmObj As Form, objPic As PictureBox, ByVal Alpha As Integer)
Dim i
frmObj.Show
frmObj.ZOrder 1
Transparencia frmObj.hwnd, Alpha
objPic.BackColor = &HFF00FF
AnularColor frmMain.hwnd, &HFF00FF
EnableWindow frmObj.hwnd, 0

End Function
'Función que aplica la transparencia, se le pasa el hwnd del form y un valor de 0 a 100
Public Function Transparencia(ByVal hwnd As Long, NivelTrans As Integer) As Long
Dim X As Long
On Error Resume Next
NivelTrans = (NivelTrans * 255) / 100

  X = GetWindowLong(hwnd, GWL_EXSTYLE)
  X = X Or WS_EX_LAYERED
  SetWindowLong hwnd, GWL_EXSTYLE, X
  'Establece la transparencia
  SetLayeredWindowAttributes hwnd, 0, NivelTrans, LWA_ALPHA

If Err Then
  Transparencia = 2
End If
End Function
'Función que aplica la transparencia, se le pasa el hwnd del form y un valor de 0 a 255
Public Function AnularColor(ByVal hwnd As Long, Color As Long) As Long
Dim X As Long
On Error Resume Next
      X = GetWindowLong(hwnd, GWL_EXSTYLE)
      X = X Or WS_EX_LAYERED
      SetWindowLong hwnd, GWL_EXSTYLE, X
      'Establece la transparencia
      SetLayeredWindowAttributes hwnd, Color, 0, &H1
      AnularColor = 0
If Err Then
AnularColor = 2
End If
End Function


Public Sub ResizeOjb(ByVal objPic As PictureBox, ByVal frmObj As Form)
Dim r As RECT, s As RECT, N
N = GetWindowRect(objPic.hwnd, r)
frmObj.Move r.Left * 15 - 200, r.Top * 15 - 200, objPic.Width + 400, objPic.Height + 400
End Sub




En un FORMULARIO:

Código (vb) [Seleccionar]

Private Sub Form_Load()
Form2.Picture1.BackColor = vbBlack
CreateObjectTransparent Form1, Form2, Picture1, 50
End Sub

Private Sub Form_Unload(Cancel As Integer)
Unload Form2
End Sub

Private Sub Timer1_Timer()
ResizeOjb Picture1, Form2
End Sub



Lo que hago es transparentar completamente el Picture1 del Form1 y luego semitransparentar el Form2 que contiene otro Picture. Obtengo la posición del Picture1 del Form1 y muevo y redimiensiono el Form2 al Picture1 del Form1. Coloco el Form2 debajo del Form1 y de esta manera creo el efecto de semitransperancia tan solo en un parte del Form1. Ademas he agrandado un poco el Form2 con respecto al Picture1 del Form1 para que al mover el formulario no se formen huecos ya que al mover muy rápido no le da tiempo a recolocar el Form2, lo bastante como para que se vean huecos si se mueve demasaido rápido

Este método que he utilizado NO me permite interactuar con el PictureBox semitransparente. Si que es posible quitando "EnableWindow frmObj.hwnd, 0" pero entonces al hacer clic en el Picture del Form2, éste se coloca delante del Form1. Ya he probado Zorder. No sirve queda un poco chapucero.

Esto  lo pongo como ejemplo pero no es lo que quiero. Lo ideal sería poder solo semitranspertentar una parte del Form o un Picture.



Saludos.


==============
Esto es una imagen que encontré googleando. Algo así me hiría de miedo. Como se ve en la imagen los controles no son transparentes pero el form sí.





RIVASBROTHERS

#1
Antes que todo disculparme por que la respuesta esta en C#, pero para lograr su objetivo puede hacer uso de la propiedad WS_EX_LAYERED, algo como esto:

Código (csharp) [Seleccionar]


public const int WS_EX_LAYERED = 0x00080000;

protected override CreateParams CreateParams
{
   get
   {
       CreateParams createParams = base.CreateParams;

       if (DesignMode) return createParams;

       //Habilita el modo transparente del formulario
       createParams.ExStyle |= WS_EX_LAYERED;
       return createParams;
   }
}



Y luego dibujar los grafico sobre un bitmap y dibujar este bitmap sobre el formulario a través de un metodo de dibujo especial tal como se muestra en este articulo https://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C, puede hacerlo de esta manera:

Código (csharp) [Seleccionar]


//Ejecuta el evento al cargar el formulario
protected override void OnLoad(EventArgs e)
{
   base.OnLoad(e);
   HandledPaintEvent();
}

private void HandledPaintEvent()
{
   var rect = new Rectangle(Point.Empty, this.Size);
   //Crea un objeto bitmap y obtiene los graficos
   var bmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
   using (Graphics g = Graphics.FromImage(bmp))
   {
       //Dibuja el fondo del form
       using (LinearGradientBrush br = new LinearGradientBrush(ClientRectangle, Color.Red, Color.Transparent, LinearGradientMode.Vertical))
       {
           g.FillRectangle(br, rect);
       }
   }

   //Dibuja los controles sobre el bitmap
   foreach (Control c in this.Controls)
   {
       if (c.Visible)
       {
           c.DrawToBitmap(bmp, c.Bounds);
       }
   }

   //Metodo para dibujar el bitmap sobre el form  (Mas Info: https://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C)
   this.SetBitmap(bmp, 100);
}



De esta manera obtiene el mismo resultado de la imagen que encontro en google:



Los problemas que se presentan son los siguientes:

*Se debe redibujar el bitmap cada vez que el formulario cambia de tamaño o ejecute un evento importante que requiera redibujo.
*Los controles quedan estaticos a menos que actualice el bitmap cada vez que se ejecute un evento  mouseHover, mouseLeave o mouseDown sobre cada control.
*El metodo DrawToBitmap no soporta transparencia por ello aunque se asigna el fondo transparente a cada control siempre devuelve el fondo por defecto que tendria el formulario en el caso que no fuera WS_EX_LAYERED.

Saludos desde El Salvador.

Eleкtro

#2
Cita de: okik en 16 Marzo 2015, 19:40 PMLlevo tiempo intentando semitransparentar un picture o una sólo una parte de un formulario. A día de hoy no lo he conseguido. No me importa que sea con vb6 o vb.net

Vamos a ver. Empieza por intentar explicar con más detalle lo que que quieres hacer. Podrías mostrar una imagen para comprender mejor tus intenciones, puesto que "semi transparentar un picture" se puede interpretar de muchas maneras (y estoy hartísimo de repetir la falta de información al formular preguntas de programación), y no queda claro si al Form también quieres que se le aplique una transparencia o no.

En VB.NET puedes utilizar las propiedades Form.Opacity, Form.TransparencyKey y Form.Region/Control.Region para llevar a cabo funcionalidades de transparencia, todo ello sin necesidad de declarar cientos de p/invokes, aunque según para qué... si que podrías necesitar recurrir al uso de código no administrado, pero generálmente te debería sobrar haciendo uso de las propiedades mencionadas.

Si lo que quieres es volver semi transparente la imagen representada dentro de un PictureBox, pues sencillamente lo que debes hacer es eso, modificar la opacidad de los píxeles de la imagen (disminuir el valor del canal alpha del ARGB), y listo. Para ello puedes aplicarle una matríz de color (clase: ColorMatrix), o también puedes iterar los píxeles de la imagen mediante la metodología Bitmap.LockBits() o también mediante Bitmap.GetPixel().

Te muestro un ejemplo del resultado:



...Pero como ya he dicho al principio de este comentario, no sé si eso es realmente lo que querrás hacer, así que aquí finalizo mi respuesta a menos que se entregue información más elaborada por tu parte (una imagen vale más que mil palabras, como puedes ver).

¡Saludos!








Eleкtro

#3
Pf... qué tonto de mi, acabo de darme cuenta que este post se publicó en el año 2015. Puesto que okik es un usuario activo, no me fijé en la fecha de publicación.

@RIVASBROTHERS por favor la próxima vez revisa la fecha del post antes de revivirlo. Como comprenderás no tiene mucho sentido invertir tiempo y esfuerzo en escribirle un código a alguien para responderle dos años despues a un tema que probablemente ya habrá solucionado o habrá dejado de interesarle. Además, revivir temas antiguos está prohibido por las reglas.

Cierro el tema.