elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


 


Tema destacado:


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Crear control personalizado, para mostrar una regla con zoom y pan.
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Crear control personalizado, para mostrar una regla con zoom y pan.  (Leído 2,417 veces)
Harold23

Desconectado Desconectado

Mensajes: 7


Ver Perfil
Crear control personalizado, para mostrar una regla con zoom y pan.
« en: 6 Julio 2017, 22:35 »

Hola, muy buenas tardes;

Estoy intentando crear un control personalizado en VS2017, que me permita mostrar una regla en los bordes superior e izquierdo con marcas de texto, basado en un sustema de unidades, sea milímetros o metros. He empeza tratando de crear los efectos ZOOM y PAN, dibujando un rectángulo de ejemplo, y funciona bien.

Mi idea es obtener esto(obvien el rectángulo, que lo dibujé para probar el ZOOM y PAN, pero si las líneas de referencia del cursor):



Para ello en VS he creado un proyecto de tipo UserControl, y el código que he usado es el siguiente:

Código
  1. ''' <summary>
  2. ''' Control personalizado para mostrar gráficos vectoriales.
  3. ''' </summary>
  4. Public Class Canvas
  5.  
  6.    Inherits System.Windows.Forms.UserControl
  7.  
  8. #Region "Constructor"
  9.  
  10.    ''' <summary>
  11.    ''' Constructor predeterminado del control de usuario.
  12.    ''' </summary>
  13.    Public Sub New()
  14.  
  15.        ' Esta llamada es exigida por el diseñador.
  16.        InitializeComponent()
  17.  
  18.        ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
  19.  
  20.        'Se asigna ciertas propiedades de inicio para el control.
  21.        '---------
  22.  
  23.        'El control se mostrará acoplado al panel donde se desee insertar.
  24.        Me.Dock = DockStyle.None
  25.        'Dado que es un control para dibujar, se adiciona la propiedad de DoubleBuffer para reducir el parpadeo.
  26.        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
  27.        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
  28.        Me.SetStyle(ControlStyles.UserPaint, True)
  29.        Me.Cursor = Cursors.Cross
  30.    End Sub
  31.  
  32. #End Region
  33.  
  34. #Region "Variables"
  35.    ''' <summary>
  36.    ''' Verifica si un botón del Mouse se ha presionado dentro de un control.
  37.    ''' </summary>
  38.    Private _mousePress As Boolean = False
  39.    ''' <summary>
  40.    ''' Variable que guarda posición del Mouse en el evento Down de un control. Esto
  41.    ''' ocurre cuando se presiona un botón del Mouse en un control.
  42.    ''' </summary>
  43.    Private _mouseDown_Location As PointF
  44.    ''' <summary>
  45.    ''' Posición actual del mouse cuando se activa el evento MouseMove.
  46.    ''' </summary>
  47.    Private _mousePositionMove As PointF
  48.    Private _TraslationX_Graphics_old As Single = 0F
  49.    Private _TraslationY_Graphics_old As Single = 0F
  50.    ''' <summary>
  51.    ''' Variable para guardar el valor que debe trasladarse el Objeto Graphics en el eje X, durante el Zoom
  52.    ''' para que siempre el foco del zoom sea de acuerdo a la posición del punntero del Mouse.
  53.    ''' </summary>
  54.    Private _TraslationX_Graphics As Single = 0F
  55.    ''' <summary>
  56.    ''' Variable para guardar el valor que debe trasladarse el Objeto Graphics en el eje Y, durante el Zoom
  57.    ''' para que siempre el foco del zoom sea de acuerdo a la posición del punntero del Mouse.
  58.    ''' </summary>
  59.    Private _TraslationY_Graphics As Single = 0F
  60.    ''' <summary>
  61.    ''' Escala de zoom que debe aplicarse en ScaleTransform del Graphics, el cual se aciva con el evento MouseWheel.
  62.    ''' </summary>
  63.    Private _zoom As Single = 1.0F
  64.    ''' <summary>
  65.    ''' Escala del zoom inmediatamente anterior del que ahora tiene la variable zoom. Esta variable siempre se calcula
  66.    ''' en el evento MouseWheel.
  67.    ''' </summary>
  68.    Private _zoom_old As Single = 1.0F
  69.    ''' <summary>
  70.    ''' Incremento del zoom que se realiza en el evento MouseWheel.
  71.    ''' </summary>
  72.    Const _zoom_increment As Single = 0.1F
  73.    ''' <summary>
  74.    ''' Mínimo zoom que se puede aplicar a los dibujos.
  75.    ''' </summary>
  76.    Const _zoom_min As Single = 0.1F
  77.    ''' <summary>
  78.    ''' Máximo zoom que se puede aplicar a los dibujos.
  79.    ''' </summary>
  80.    Const _zoom_max As Single = 5.0F
  81.  
  82.  
  83. #End Region
  84.  
  85. #Region "Eventos"
  86.  
  87.    Protected Overrides Sub OnPaint(e As PaintEventArgs)
  88.        MyBase.OnPaint(e)
  89.  
  90.        Dim g As Graphics = e.Graphics
  91.  
  92.        'Envío el objeto Graphics, para que dibuje teniendo en cuenta las transformaciones.
  93.        TransformarGraphics(g)
  94.        DrawLineReferencieMouse(Me._mousePositionMove, g)
  95.  
  96.    End Sub
  97.  
  98.    Protected Overrides Sub OnMouseDown(e As MouseEventArgs)
  99.        MyBase.OnMouseDown(e)
  100.        'Se obtiene la posición actual del Mouse.
  101.        Me._mousePositionMove = e.Location
  102.  
  103.        'Se verifica que ha presionado el botón izquierdo del Mouse.
  104.        If e.Button = MouseButtons.Left Then
  105.            '
  106.            'Se verifica que no se tiene g
  107.            If Not Me._mousePress = True Then
  108.  
  109.                Me._mousePress = True
  110.                '
  111.                'Obtiene las coordenadas del puntero del Mouse cuando se presionó el Mouse.
  112.                Me._mouseDown_Location = e.Location
  113.                Me._TraslationX_Graphics_old = Me._TraslationX_Graphics
  114.                Me._TraslationY_Graphics_old = Me._TraslationY_Graphics
  115.            End If
  116.  
  117.            Me.Invalidate(False)
  118.  
  119.        End If
  120.  
  121.    End Sub
  122.  
  123.    Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
  124.        MyBase.OnMouseMove(e)
  125.  
  126.        'Se obtiene la posición actual del Mouse.
  127.        Me._mousePositionMove = e.Location
  128.  
  129.        'Se verifica que ha presionado el botón izquierdo del Mouse.
  130.        If e.Button = MouseButtons.Left Then
  131.            '
  132.            'Se cambia el icono del cursor.
  133.            Dim ms As MemoryStream = New MemoryStream(My.Resources.hold_1)
  134.            Me.Cursor = New Cursor(ms)
  135.            '
  136.            'Posición actual del puntero del mouse.
  137.            Dim mousePosNow As PointF = e.Location
  138.            'Variable para saber cuanto debe desplazarse el objeto gráfico de acuerdo a la
  139.            'posición guardada en el evento MouseDown y la actual en este evento.
  140.            Dim deltaX, deltaY As Single
  141.            'Se haya los valores de desplazamiento.
  142.            deltaX = mousePosNow.X - Me._mouseDown_Location.X
  143.            deltaY = mousePosNow.Y - Me._mouseDown_Location.Y
  144.            '
  145.            'Se obtiene DE NUEVO el desplazamiento que debe realizarse en el Objeto Graphics, teniendo en cuenta
  146.            'el valor pasado de dichos traslados obtenidos en el el evento MouseDown+ el nuevo delta de desplazamiento
  147.            'teniendo en cuenta de dividir dicho valor con el valor actual del zoom.
  148.            Me._TraslationX_Graphics = (Me._TraslationX_Graphics_old + (deltaX / Me._zoom))
  149.            Me._TraslationY_Graphics = (Me._TraslationY_Graphics_old + (deltaY / Me._zoom))
  150.            '
  151.            '
  152.            'Obligo a redibujar el control.
  153.            Me.Invalidate(False)
  154.  
  155.        End If
  156.  
  157.    End Sub
  158.  
  159.  
  160.    Protected Overrides Sub OnMouseUp(e As MouseEventArgs)
  161.        MyBase.OnMouseUp(e)
  162.        '
  163.        'Este evento ocurre cuando el puntero del Mouse se encuentra sobre el control
  164.        'y soltó un botón del Mouse.
  165.        '
  166.        'Se indica que ya no se encuentra presionado el botón del Mouse.
  167.        Me._mousePress = False
  168.        'Se cambia el icono del cursor, cuando se suelta el botón del mouse que actualmente se presionaba.
  169.        Me.Cursor = Cursors.Cross
  170.        Me.Invalidate(False)
  171.    End Sub
  172.  
  173.    Protected Overrides Sub OnMouseWheel(e As MouseEventArgs)
  174.  
  175.        MyBase.OnMouseWheel(e)
  176.        '
  177.        'Actualizo el valor del zoom actual, antes de que se calcule el nuevo en este evento.
  178.        Me._zoom_old = Me._zoom
  179.  
  180.        'Verifico que el paso de la muesca del mouse se hace hacia arriba.(>zoom).
  181.        If e.Delta > 0 Then
  182.            '
  183.            'Se incrementa el zoom, de acuerdo a la variable constante de la clase. Se
  184.            'tiene en cuenta que no se sobrepase del mayor valor del zoom.
  185.            Me._zoom = Math.Min(Me._zoom + _zoom_increment, _zoom_max)
  186.        Else
  187.            '
  188.            'Para el caso de disminución del Mouse, establezco un mínimo valor para no tener
  189.            'problemas de OverFlow. El mínimo valor del zoom
  190.            Me._zoom = Math.Max(Me._zoom - _zoom_increment, _zoom_min)
  191.        End If
  192.        '
  193.        'Se obtiene la posición actual del Mouse.
  194.        Dim mousePosNow As PointF = e.Location
  195.        'Variables para saber el valor de los deltas entre la posición actual del mouse
  196.        'y la ubicación de la parte superior izquierda del PictureBox.
  197.        Dim deltaX, deltaY As Single
  198.        deltaX = mousePosNow.X - Me.Location.X
  199.        deltaY = mousePosNow.Y - Me.Location.Y
  200.        'Variable que guardan el valor teniendo en cuenta el zoom inmediatamente
  201.        'anterior de los desplazamientos que sufrió el Graphics.
  202.        Dim oldGraphicsX As Single
  203.        Dim oldGraphicsY As Single
  204.        oldGraphicsX = ((deltaX / Me._zoom_old))
  205.        oldGraphicsY = ((deltaY / Me._zoom_old))
  206.        'Variable para guardar los nuevos desplazamientos que debe sufrir el Graphics,
  207.        'para que el centro del foco del Zoom sea la ubicación actual del Mouse.
  208.        Dim newGraphicsX As Single
  209.        Dim newGraphicsY As Single
  210.        newGraphicsX = ((deltaX / Me._zoom))
  211.        newGraphicsY = ((deltaY / Me._zoom))
  212.        '
  213.        'Los nuevos valores de Traslado del objeto Graphics se obtiene.
  214.        Me._TraslationX_Graphics = newGraphicsX - oldGraphicsX + Me._TraslationX_Graphics
  215.        Me._TraslationY_Graphics = newGraphicsY - oldGraphicsY + Me._TraslationY_Graphics
  216.  
  217.        'Se obtiene la posición actual del Mouse.
  218.        Me._mousePositionMove = mousePosNow
  219.  
  220.        'Se obliga a redibujar el control.
  221.        Me.Invalidate(False)
  222.  
  223.    End Sub
  224.  
  225. #End Region
  226.  
  227. #Region "Métodos"
  228.  
  229.    ''' <summary>
  230.    ''' Realiza las transformaciones del Canvas, en este un escalamiento(zoom) y un traslado
  231.    ''' de coordendas para que el zoom sea respecto a una posición en específica.
  232.    ''' </summary>
  233.    ''' <param name="canvas"></param>
  234.    Private Sub TransformarGraphics(canvas As Graphics)
  235.  
  236.        'Borramos la superficie de dibujo y aplicacmos un color de fondo.
  237.        canvas.Clear(Color.Black)
  238.        'Suavizado de los pixeles para disminuir el parpadeo al redibujar.
  239.        'canvas.PixelOffsetMode = Drawing2D.PixelOffsetMode.Half
  240.        'canvas.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
  241.        canvas.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
  242.        '
  243.        'Se aplica el escalado actual del onjeto gráfico. Este valor se ajusta en el evento MouseWheel del Control de dibujado.
  244.        canvas.ScaleTransform(Me._zoom, Me._zoom)
  245.        '
  246.        'Se traslada el origen de las coordenadas, para que el centro del zoom coincida
  247.        'con la posición del mouse actual.
  248.        canvas.TranslateTransform(Me._TraslationX_Graphics, _TraslationY_Graphics)
  249.        '
  250.        'Pindel para dibujado de los objetos.
  251.        Dim pen As New Pen(Color.Red)
  252.        'Establezco un ancho de la línea siempre de 1.5 pix, sin importar el zoom.
  253.        pen.Width = 1.0F / Me._zoom
  254.        'Patrón del tipo de línea a usar del Pen, con 20 pix de línea espaciadas cada 10 pix.
  255.        Dim tipo_linea() As Single = {20, 10}
  256.        'Asigno el tipo de línea personalizada.
  257.        pen.DashStyle = Drawing2D.DashStyle.Custom
  258.        'Asigno el tipo de línea.
  259.        pen.DashPattern = tipo_linea
  260.        pen.LineJoin = Drawing2D.LineJoin.Bevel
  261.        'Dibuja un rectángulo de ejemplo para probar ZOOM y PAN.
  262.        canvas.DrawRectangle(pen, 100, 150, 500, 400)
  263.  
  264.        Me.Invalidate(False)
  265.  
  266.    End Sub
  267.  
  268.    Private Sub DrawLineReferencieMouse(positionMouse As PointF, graphics As Graphics)
  269.  
  270.        graphics.PageUnit = GraphicsUnit.Pixel
  271.  
  272.        Dim pencil As New Pen(Color.White)
  273.        Dim tipolinea As Single() = New Single() {10, 15}
  274.        pencil.Width = 1 / Me._zoom
  275.        pencil.ScaleTransform(Me._zoom, Me._zoom)
  276.  
  277.        pencil.DashPattern = tipolinea
  278.        Dim width As Single = graphics.ClipBounds.Width
  279.        Dim heigth As Single = graphics.ClipBounds.Height
  280.  
  281.  
  282.        'Se dibuja la linea vertical
  283.        graphics.DrawLine(pencil, (positionMouse.X) / Me._zoom - Me._TraslationX_Graphics, -Me._TraslationY_Graphics, (positionMouse.X) / Me._zoom - Me._TraslationX_Graphics, heigth - Me._TraslationY_Graphics)
  284.        'Se dibuja la linea HORIZONTAL
  285.        graphics.DrawLine(pencil, -Me._TraslationX_Graphics, positionMouse.Y / Me._zoom - Me._TraslationY_Graphics, width - Me._TraslationX_Graphics, positionMouse.Y / Me._zoom - Me._TraslationY_Graphics)
  286.  
  287.        Me.Invalidate(False)
  288.  
  289.    End Sub
  290.  
  291. #End Region
  292. End Class

El problema es que cuando INTENTO correr el control personalizado en el proyecto de creación, no me muestra las propiedades a mano derecha y empieza a parpadear. Sé que esto pasa por el evento Paint, pero pues no se conceptualmente como solucionarlo.



De igual forma pasa, cuando intento usar el control en un proyecto de prueba, al agregarlo en el Form, se observa que parpadea, y cuando intento correr el Test, ni si quiera arranca.



Podrían ayudarme a solucionar esto. Mi idea es crea posteriormente un control para dibujar con GDI+ con sobre él, y quiero es mostrar unas reglas en dicho control en unidades varias, hacer el zoom y el pan, éstos dos últimos que ya funcionan en el ejemplo que acabé de pasar.

¿Podrían guiarme para crear dicho control personalizado, de una forma correcta?

Muchas gracias.

MOD: Etiqueta GeSHi. Imagenes adaptadas a lo permitido.


« Última modificación: 6 Julio 2017, 22:39 por MCKSys Argentina » En línea

Maurice_Lupin


Desconectado Desconectado

Mensajes: 353

GPS


Ver Perfil WWW
Re: Crear control personalizado, para mostrar una regla con zoom y pan.
« Respuesta #1 en: 7 Julio 2017, 19:22 »

Hola,  Harold23

No he probado tu código, puedo comentarte en cuanto al parpadeo, cuando pintas directamente el control es posible notar el pintado de píxeles (por eso se ve un feo parpadeo), para evitarlo se pinta en memoria primero y luego se vuelca esto en el método paint, se le llama la técnica del double buffer.

Me parece que en vb.net es más sencillo, se puede habilitar.
https://msdn.microsoft.com/es-es/library/3t7htc9c(v=vs.110).aspx

Aquí un ejemplo completo, está en C# pero es casi lo mismo en vb.net
https://www.codeproject.com/Articles/12870/Don-t-Flicker-Double-Buffer

Saludos.


En línea

Un error se comete al equivocarse.
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.709



Ver Perfil
Re: Crear control personalizado, para mostrar una regla con zoom y pan.
« Respuesta #2 en: 8 Julio 2017, 16:26 »

Citar
El problema es que cuando INTENTO correr el control personalizado en el proyecto de creación, no me muestra las propiedades a mano derecha y empieza a parpadear. Sé que esto pasa por el evento Paint, pero pues no se conceptualmente como solucionarlo.

Fíjate bien en lo que haces, cuando se dispara por primera vez el evento Control.Paint de tu UserControl, entras en un círculo vicioso volviendo a disparar el mismo evento una y otra vez como un búcle infinito, esto es por culpa de las llamadas que estás haciendo a Control.Invalidate() dentro del bloque de los métodos TransformarGraphics() y DrawLineReferencieMouse(), literálmente hablando estás colapsando la cola de mensajes del control/la aplicación/el diseñador de Visual Studio, todo.

Para evitarlo sencillamente elimina las llamadas a Control.Invalidate(), a simple vista no parece ser realmente necesario, y en caso de necesitarlo pues simplemente crea una variable booleana que sirva como señal para determinar si el control necesita invalidación, de esta forma evitar entrar en ese "búcle infinito"...

Código
  1. Private needsValidation As Boolean = True
  2.  
  3. Private Sub TransformarGraphics(g As Graphics)
  4.    ' ...
  5.    ' Aquí no es necesario hacer nada, solamente invalidarías al terminar todas las operaciones de dibujado, en el método DrawLineReferencieMouse.
  6.    ' ...
  7. End Sub
  8.  
  9. Private Sub DrawLineReferencieMouse(pt As PointF, g As Graphics)
  10.    ' ...
  11.    If (Me.needsValidation) Then
  12.        Me.needsValidation = False
  13.        Me.Invalidate(invalidateChildren:=False)
  14.    Else
  15.        Me.needsValidation = True
  16.    End If
  17.    ' ...
  18. End Sub

...Tan solo te muestro cual sería una solución a ese tipo de problema, pero como ya he comentado no necesitas invalidar nada... al menos a simple vista.

Ten en cuenta también la propiedad Component.DesignMode, mediante esta propiedad deberías evaluar si tu control está siendo usado en tiempo de diseño o en tiempo de ejecución para evitar operaciones de dibujado que resulten innecesarias...

Código
  1. If Not MyBase.DesignMode Then
  2.    ' DIBUJAR
  3. Else
  4.    ' NO DIBUJAR
  5. End If

Con respecto al flickering o parapadeo, bueno, las constantes e infinitas invalidaciones que haces sobre el control es lo que causa ese parpadeo, una vez lo hayas arreglado deberías dejar de sufrir más parpadeos, pero en general te diré que el doble búfer es tan solo una ayuda para reducir (que no eliminar) el flickering. No existe ninguna forma posible para erradicar por completo la posibilidad de producir flickering en WinForms/GDI/GDI+, cuanto más complejas sean las operaciones de dibujado, más alta será la probabilidad, es inevitable, aunque según en que escenario y si haces las cosas bien entonces se puede reducir el flickering hasta que parezca casi imperceptible; si eso te preocupa entonces lo más sensato que podrías hacer es migrar a la tecnología WPF, allí no tendrás ese problema.

EDITO: Ah, por cierto... en tu código no estás liberando objetos administrados que internamente generan recursos no administrados (residuos), como por ejemplo las instancias de la clase Pen.

Saludos!
« Última modificación: 9 Julio 2017, 11:07 por Eleкtro » En línea


Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines