Obtenha a posição absoluta do elemento dentro da janela no wpf

89

Gostaria de obter a posição absoluta de um elemento em relação à janela / elemento raiz quando ele é clicado duas vezes. A posição relativa do elemento dentro de seu pai é tudo o que consigo chegar, e o que estou tentando chegar é o ponto em relação à janela. Já vi soluções de como obter o ponto de um elemento na tela, mas não na janela.

BrandonS
fonte

Respostas:

130

Acho que o que BrandonS quer não é a posição do mouse em relação ao elemento raiz, mas sim a posição de algum elemento descendente.

Para isso, existe o método TransformToAncestor :

Point relativePoint = myVisual.TransformToAncestor(rootVisual)
                              .Transform(new Point(0, 0));

Onde myVisualestá o elemento que acabou de ser clicado duas vezes e rootVisualé Application.Current.MainWindow ou o que quer que você queira na posição relativa.

Robert Macnee
fonte
3
Oi, eu tentei isso e recebo a seguinte exceção: System.InvalidOperationException was unhandled Message = O Visual especificado não é um ancestral deste Visual. Source = PresentationCore Alguma ideia?
RoflcoptrException
8
Visual.TransformToAncestor só funcionará se você passar um Visual contido. Se você quiser a posição relativa de dois elementos e um não contiver o outro, use Visual.TransformToVisual.
Robert Macnee
5
TransformToVisual ainda requer um ancestral comum, o que pode ser problemático se o controle estiver em um pop
Adam Mills
1
Super intuitivo! Eles não podem envolver isso em uma chamada "GetRelativePosition"? :-) Obrigado pela ajuda. +1
Paul,
1
@ cod3monk3y - ou talvez, se a Microsoft abrir o WPF de código-fonte, enviarei uma solicitação de pull :-)
Paul
43

Para obter a posição absoluta de um elemento da IU dentro da janela, você pode usar:

Point position = desiredElement.PointToScreen(new Point(0d, 0d));

Se você estiver em um controle de usuário e simplesmente quiser a posição relativa do elemento de interface do usuário nesse controle, basta usar:

Point position = desiredElement.PointToScreen(new Point(0d, 0d)),
controlPosition = this.PointToScreen(new Point(0d, 0d));

position.X -= controlPosition.X;
position.Y -= controlPosition.Y;
Filip
fonte
4
Observe que isso provavelmente não faz o que você espera se a escala da sua tela não estiver configurada para 100% (ou seja, em telas com alto DPI).
Drew Noakes em
18

Adicione este método a uma classe estática:

 public static Rect GetAbsolutePlacement(this FrameworkElement element, bool relativeToScreen = false)
    {
        var absolutePos = element.PointToScreen(new System.Windows.Point(0, 0));
        if (relativeToScreen)
        {
            return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
        }
        var posMW = Application.Current.MainWindow.PointToScreen(new System.Windows.Point(0, 0));
        absolutePos = new System.Windows.Point(absolutePos.X - posMW.X, absolutePos.Y - posMW.Y);
        return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
    }

Defina o relativeToScreenparâmetro como truepara posicionamento do canto superior esquerdo de toda a tela ou falsepara posicionamento do canto superior esquerdo da janela do aplicativo.

Andreas
fonte
1
Isso funciona fantasticamente! Estou usando isso com uma animação que desliza uma imagem para dentro ou para fora da tela, modificando o RenderTransformdo elemento e, portanto, precisa saber a posição absoluta do elemento na tela.
cod3monk3y
6

Desde o .NET 3.0, você pode simplesmente usar *yourElement*.TranslatePoint(new Point(0, 0), *theContainerOfYourChoice*).

Isso lhe dará o ponto 0, 0 do botão, mas em direção ao recipiente. (Você também pode dar um outro ponto que 0, 0)

Verifique aqui o doc.

Guibi
fonte
0

Hm. Você deve especificar a janela em que clicou em O Mouse.GetPosition(IInputElement relativeTo) código a seguir funciona bem para mim

protected override void OnMouseDown(MouseButtonEventArgs e)
    {
        base.OnMouseDown(e);
        Point p = e.GetPosition(this);
    }

Suspeito que você precise se referir à janela não de sua própria classe, mas de outro ponto do aplicativo. Nesse caso Application.Current.MainWindowvai te ajudar.

Oleg
fonte
Embora não seja o que o autor pediu, isso me colocou no caminho certo, obrigado
Ladislav Ondris