Ответ 1
Я обычно объединяю PointToScreen
и PointToClient
:
Point locationOnForm = control.FindForm().PointToClient(
control.Parent.PointToScreen(control.Location));
Есть ли способ получить позицию управления в форме, когда элемент управления может находиться внутри других элементов управления (например, панелей)?
Элементы управления Left и Top дают мне только позицию в нем родительского элемента управления, но что, если мой элемент управления находится внутри пяти вложенных панелей, и мне нужна его позиция в форме?
Быстрый пример:
Кнопка btnA расположена в координатах (10,10) внутри панели pnlB.
Панель pnlB расположена в координатах (15,15) внутри формы frmC.
Я хочу местоположение btnA на frmC, которое (25,25).
Могу ли я получить это местоположение?
Я обычно объединяю PointToScreen
и PointToClient
:
Point locationOnForm = control.FindForm().PointToClient(
control.Parent.PointToScreen(control.Location));
Вы можете использовать метод управления PointToScreen
, чтобы получить абсолютное положение относительно экрана.
Вы можете использовать метод Forms PointToScreen
и с базовой математикой получить контрольную позицию.
Вы могли бы пройти через родителей, отметив их положение в своем родителе, пока не прибудете в форму.
Изменить: что-то вроде (untested):
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;
}
Я обычно делаю это так. Работает каждый раз..
var loc = ctrl.PointToScreen(Point.Empty);
Supergeek, ваша нерекурсивная функция не произвела правильного результата, но моя делает. Я считаю, что у вас слишком много дополнений.
private Point LocationOnClient(Control c)
{
Point retval = new Point(0, 0);
for (; c.Parent != null; c = c.Parent)
{ retval.Offset(c.Location); }
return retval;
}
Как ни странно, PointToClient и PointToScreen не были идеальными для моей ситуации. Особенно потому, что я работаю с контрольными элементами, которые не связаны ни с какой формой. Я нашел решение sahin наиболее полезным, но вынул рекурсию и исключил завершение формы. Нижеприведенное решение работает для любого из моих элементов управления, видимых или нет, содержащихся в форме или нет, IContainered или нет. Спасибо Сахим.
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;
}
В моем тестировании решения Ганса Кестинга и Фредрика Мёрка дали тот же ответ. Но:
Я нашел интересное несоответствие в ответе, используя методы Раджа Мора и Ганса Кестинга, и подумал, что поделюсь. Спасибо, хотя и за их помощь; Я не могу поверить, что такой метод не встроен в структуру.
Обратите внимание, что Raj не записывал код, и поэтому моя реализация может отличаться от того, что он имел в виду.
Разница, которую я обнаружил, заключалась в том, что метод от Raj More часто был на два пиксела больше (как в X, так и Y), чем метод от Hans Kesting. Я еще не определил, почему это происходит. Я почти уверен, что это имеет какое-то отношение к тому факту, что, по-видимому, существует двухпиксельная граница вокруг содержимого формы Windows (например, внутри самой внешней формы). В моем тестировании, которое, конечно же, не было исчерпывающим, я только сталкивался с ним на элементах управления, которые были вложены. Однако не все вложенные элементы управления демонстрируют это. Например, у меня есть TextBox внутри GroupBox, который показывает несоответствие, но кнопка внутри того же GroupBox этого не делает. Я не могу объяснить, почему.
Обратите внимание, что, когда ответы эквивалентны, они рассматривают точку (0, 0) внутри границы содержимого, упомянутой выше. Поэтому я считаю, что я буду рассматривать решения Hans Kesting и Fredrik Mörk, но не думаю, что буду доверять решению, которое я реализовал в Raj More's.
Я также задавался вопросом, какой именно код Raj More написал бы, поскольку он дал идею, но не предоставил код. Я не полностью понял метод PointToScreen(), пока не прочитал этот пост: http://social.msdn.microsoft.com/Forums/en-US/netfxcompact/thread/aa91d4d8-e106-48d1-8e8a-59579e14f495
Вот мой метод тестирования. Обратите внимание, что "Метод 1", упомянутый в комментариях, несколько отличается от метода Ханса Кестинга.
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;
}
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;
}
}
Это то, что я сделал, работает как шарм
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);
}