2009-09-25 23 views
53

¿Hay alguna manera de recuperar la posición de un control en un formulario, cuando el control puede estar dentro de otros controles (como Paneles)?C# Obtener una posición de control en un formulario

Las propiedades izquierda y derecha del control me dan solo su posición dentro del control principal, pero ¿qué pasa si mi control está dentro de cinco paneles anidados y necesito su posición en el formulario?

Ejemplo rápido:

El botón btnA se encuentra en las coordenadas (10,10) dentro del panel PNLB.
El panel pnlB se encuentra en las coordenadas (15,15) dentro del formulario frmC.

Quiero la ubicación de btnA en frmC, que es (25,25).

¿Se puede obtener esta ubicación?

Respuesta

77

que suelen combinar PointToScreen y PointToClient:

Point locationOnForm = control.FindForm().PointToClient(
    control.Parent.PointToScreen(control.Location)); 
+5

Esta es la 'POSICIÓN REAL ABSOULUTE' http://stackoverflow.com/questions/4998076/getting-the-location-of-a-control-relative-to-the-entire-screen – PUG

+3

¿Cómo se diferencia esto de' control .PointToScreen (Point.Empty); '? – strongriley

+1

@strongriley No tengo idea (nunca lo intenté y no tengo env env disponible en este momento), pero no puedo ver ese comportamiento mencionado en la documentación y, de ser así, no hay garantía de que funcione en futuras versiones del framework. –

6

Puede acercarse a los padres, anotando su posición dentro de sus padres, hasta que llegue al Formulario.

Editar: Algo así (no probado):

public Point GetPositionInForm(Control ctrl) 
{ 
    Point p = ctrl.Location; 
    Control parent = ctrl.Parent; 
    while (! (parent is Form)) 
    { 
     p.Offset(parent.Location.X, parent.Location.Y); 
     parent = parent.Parent; 
    } 
    return p; 
} 
+0

arrggg ... golpearme :) – armannvg

+0

Sí, pensé en eso, pero me pareció una forma poco práctica de hacerlo, así que esperaba que hubiera otra manera. Si nadie tiene otra sugerencia, eso es lo que haré. –

+0

Esta es la manera de hacerlo, con una función recursiva simple. – MusiGenesis

11

Puede utilizar los controles PointToScreen método para obtener la posición absoluta con respecto a la pantalla.

Puede hacer el método Formas PointToScreen y, con matemática básica, obtener la posición del control.

+0

Sí, gracias. ¡Perfecto! –

+0

Eso no funcionará exactamente, a menos que tenga en cuenta la altura de la barra de título y el ancho del borde del formulario. – MusiGenesis

+0

¿Cómo ayuda esto a obtener la posición absoluta del control? Debe pasar un objeto Point a PointToScreen(), que no tiene sentido en este caso. – Tim

1

En mis pruebas, tanto las soluciones de Fredrik MORK de Hans Kesting y di la misma respuesta. Pero:

Encontré una discrepancia interesante en la respuesta usando los métodos de Raj More y Hans Kesting, y pensé que compartiría. Gracias a ambos por su ayuda; No puedo creer que ese método no esté incorporado en el marco.

Tenga en cuenta que Raj no escribió el código y, por lo tanto, mi implementación podría ser diferente de lo que quería decir.

La diferencia que encontré fue que el método de Raj More a menudo tendría dos píxeles más (tanto en X como en Y) que el método de Hans Kesting. Todavía no he determinado por qué ocurre esto. Estoy bastante seguro de que tiene algo que ver con el hecho de que parece haber un borde de dos píxeles alrededor del contenido de un formulario de Windows (como en, dentro de los bordes más externos del formulario). En mi prueba, que ciertamente no fue exhaustiva en ningún sentido, solo me he encontrado con controles anidados. Sin embargo, no todos los controles anidados lo exhiben. Por ejemplo, tengo un TextBox dentro de un GroupBox que muestra la discrepancia, pero un botón dentro del mismo GroupBox no lo hace. No puedo explicar por qué.

Tenga en cuenta que cuando las respuestas son equivalentes, consideran que el punto (0, 0) es dentro de el borde de contenido que mencioné anteriormente. Por lo tanto, creo que consideraré que las soluciones de Hans Kesting y Fredrik Mörk son correctas, pero no creo que confíe en la solución que implementé de Raj More's.

También me pregunté exactamente qué código habría escrito Raj More, ya que dio una idea pero no proporcionó el código.No entendí completamente el método PointToScreen() hasta que leí esta publicación: http://social.msdn.microsoft.com/Forums/en-US/netfxcompact/thread/aa91d4d8-e106-48d1-8e8a-59579e14f495

Aquí está mi método para probar. Tenga en cuenta que el 'Método 1' mencionado en los comentarios es ligeramente diferente al de Hans Kesting.

private Point GetLocationRelativeToForm(Control c) 
{ 
    // Method 1: walk up the control tree 
    Point controlLocationRelativeToForm1 = new Point(); 
    Control currentControl = c; 
    while (currentControl.Parent != null) 
    { 
    controlLocationRelativeToForm1.Offset(currentControl.Left, currentControl.Top); 
    currentControl = currentControl.Parent; 
    } 

    // Method 2: determine absolute position on screen of control and form, and calculate difference 
    Point controlScreenPoint = c.PointToScreen(Point.Empty); 
    Point formScreenPoint = PointToScreen(Point.Empty); 
    Point controlLocationRelativeToForm2 = controlScreenPoint - new Size(formScreenPoint); 

    // Method 3: combine PointToScreen() and PointToClient() 
    Point locationOnForm = c.FindForm().PointToClient(c.Parent.PointToScreen(c.Location)); 

    // Theoretically they should be the same 
    Debug.Assert(controlLocationRelativeToForm1 == controlLocationRelativeToForm2); 
    Debug.Assert(locationOnForm == controlLocationRelativeToForm1); 
    Debug.Assert(locationOnForm == controlLocationRelativeToForm2); 

    return controlLocationRelativeToForm1; 
} 
1
private Point FindLocation(Control ctrl) 
{ 
    if (ctrl.Parent is Form) 
     return ctrl.Location; 
    else 
    { 
     Point p = FindLocation(ctrl.Parent); 
     p.X += ctrl.Location.X; 
     p.Y += ctrl.Location.Y; 
     return p; 
    } 
} 
3

Curiosamente, PointToClient y PointToScreen no eran ideales para mi situación. Particularmente porque estoy trabajando con controles fuera de pantalla que no están asociados con ningún formulario. Encontré la solución de Sahin la más útil, pero eliminé la recursión y eliminé la terminación de Formulario. La siguiente solución funciona para cualquiera de mis controles visibles o no, contenida o no, IContainered o no. Gracias Sahim.

private static Point FindLocation(Control ctrl) 
{ 
    Point p; 
    for (p = ctrl.Location; ctrl.Parent != null; ctrl = ctrl.Parent) 
     p.Offset(ctrl.Parent.Location); 
    return p; 
} 
5

Supergeek, su función no recursiva no produjo el resultado correcto, pero la mía sí. Creo que el tuyo hace demasiadas adiciones.

private Point LocationOnClient(Control c) 
{ 
    Point retval = new Point(0, 0); 
    for (; c.Parent != null; c = c.Parent) 
    { retval.Offset(c.Location); } 
    return retval; 
} 
6

por lo general lo hago como esto .. funciona todo el tiempo ..

var loc = ctrl.PointToScreen(Point.Empty); 
+1

¡Esta debería ser la respuesta aceptada! – skamlet

0

Esto es lo que he hecho funciona como un encanto

  private static int _x=0, _y=0; 
     private static Point _point; 
     public static Point LocationInForm(Control c) 
     { 
      if (c.Parent == null) 
      { 
       _x += c.Location.X; 
       _y += c.Location.Y; 
       _point = new Point(_x, _y); 
       _x = 0; _y = 0; 
       return _point; 
      } 
      else if ((c.Parent is System.Windows.Forms.Form)) 
      { 
       _point = new Point(_x, _y); 
       _x = 0; _y = 0; 
       return _point; 
      } 
      else 
      { 
       _x += c.Location.X; 
       _y += c.Location.Y; 
       LocationInForm(c.Parent); 
      } 
      return new Point(1,1); 
     } 
Cuestiones relacionadas