Seiten

Mittwoch, 14. April 2010

How To: Position eines ChildWindow zur Laufzeit ermitteln

Manchmal ist es erforderlich, die Position eines regulären Silverlight 3 ChildWindow zur Laufzeit zu ermitteln. Dieser Beitrag beschreibt Schritt für Schritt, wie das geht.


Grundsätzliche Funktionsweise

Der Code funktioniert so, dass auf einzelne Teile (Parts) des ContentTemplate zugegriffen wird, um die Position zu ermitteln. Ermittelt wird das Offset, also die Position des ChildWindow, genauer gesagt des ContentRoot, relativ zur oberen linken Ecke des Silverlight-Plugin im Browser. Für unsere Zwecke relevant sind insofern die Elemente Root, ContentRoot und Chrome aus dem Control Template des ChildWindow.

Root stellt die äußerste Begrenzung eines ChildWindow dar, definiert durch die Grenzen des Silverlight-Plugin im Browser. ContentRoot ist der Teil im ControlTemplate eines ChildWindow, der vom Nutzer als das eigentliche visuelle ChildWindow wahrgenommen wird. Das Chrome ist die Fensterleiste eines ChildWindow, also der Bereich, in dem sich der Fenstertitel und der CloseButton, das kleine Kreuz oben rechts, befinden.

Standardmaßig wird die Position eines ChildWindows dadurch verändert, dass der Nutzer mit der Maus auf die Fensterleiste (das Chrome) klickt und das "Fenster" (den ContentRoot) mit gedrückter Maustaste zu seiner neuen Position zieht, um dann die Maustaste wieder loszulassen. Wenn der Nutzer die Maustaste wieder losläßt hat das ChildWindow seine neue Position erhalten. Also hängen wir uns an das MouseLeftButtonUpEvent des Chrome heran, um in dem Moment, wenn der Nutzer die Maustaste losläßt die neue Position des ChildWindow zu ermitteln.

Schritt für Schritt

In einem neuen Silverlight 3-Projekt brauchen wir zunächst drei Variablen, denen wir später die Template Parts Root, ContentRoot und Chrome zuweisen können.

  private FrameworkElement root;
  private FrameworkElement contentroot;
  private FrameworkElement chrome;

Dann brauchen wir noch eine weitere Variable, die später die Position des ChildWindow aufnehmen soll.

  private Point childWindowOffset;

Als nächstes fügen wir dem Loaded-Event des ChildWindow einen neuen RoutedEventHandler hinzu:

public MyChildWindow()
{
  InitializeComponent();
  Loaded += new RoutedEventHandler(ThisChildWindow_Loaded);
}

In dem RoutedEventHandler ThisChildWindow_Loaded greifen wir mit VisualTreeHelper auf den VisualTree des Control Template zu und weisen so unseren drei Variablen root, contentroot und chrome die entsprechenden Elemente des Control Template zu. Dem MouseLeftButtonUpEvent des Chrome fügen wir mit AddHandler einen weiteren MouseButtonEventHandler hinzu, in den wir später unseren Code für die Ermittlung der Position einfügen.

private void ThisChildWindow_Loaded
 (
   object sender,
   RoutedEventArgs e
 )
{
  if (VisualTreeHelper.GetChildrenCount(this) == 0)
  {
    Dispatcher.BeginInvoke(() =>
      ThisChildWindow_Loaded(this, e));
    return;
  }
  root = (FrameworkElement)VisualTreeHelper.GetChild
                                            (
                                              this, 0
                                             );
  contentroot = root.FindName("ContentRoot")
                  as FrameworkElement;
  chrome = root.FindName("Chrome")
             as FrameworkElement;
  if (chrome != null)
  {
    chrome.AddHandler
     (
      MouseLeftButtonUpEvent,
      new MouseButtonEventHandler
       (
        Chrome_MouseLeftButtonUp
       ),
      true
     );
  }
}

Dann müssen wir unsere Routine Chrome_MouseLeftButtonUp bauen, in der wir die Funktion GetChildWindowOffset aufrufen werden. In der Funktion GetChildWindowOffset findet die eigentliche Berechnung der neuen Position statt. Hier ist der Code für GetChildWindowOffset:

private Point GetChildWindowOffset
  (
    Point RootMousePosition,
    Point ContentrootMousePosition
  )
{
  Point result = new Point();

  result.X = RootMousePosition.X - ContentrootMousePosition.X;
  result.Y = RootMousePosition.Y - ContentrootMousePosition.Y;

  return result;
}

Die Funktion GetChildWindowOffset nimmt zwei Parameter entgegen, beide vom Typ Point. Der erste Parameter, RootMousePosition, erthält die Position der Maus relativ zur oberen linken Ecke des Root. Der zweite Parameter, ContentRootMousePosition, erthält die Position der Maus relativ zur oberen linken Ecke des ContentRoot. Die Werte für diese beiden Parameter ermitteln wir in der Routine Chrome_MouseLeftButtonUp jeweils durch Auswertung des Rückgabewerts von e.GetPosition:

private void Chrome_MouseLeftButtonUp
  (
    object sender,
    MouseButtonEventArgs e
  )
{
  this.childWindowOffset =
    GetChildWindowOffset
    (
      e.GetPosition(root),
      e.GetPosition(contentroot)
    );
}

Die Berechnung der neuen Position erfolgt in der Funktion GetChildWindowOffset, indem der X-Wert bzw. der Y-Wert von ContentRootMousePosition jeweils vom X-Wert bzw. vom Y-Wert von RootMousePosition subtrahiert werden:

  result.X = RootMousePosition.X - ContentrootMousePosition.X;
  result.Y = RootMousePosition.Y - ContentrootMousePosition.Y;

Das war's. Jedesmal, wenn das ChildWindow vom Nutzer umpositioniert worden ist, erhält unsere Variable childWindowPosition jetzt sofort die neue Position und kann im CodeBehind weiter verarbeitet werden.

Ich hoffe, der Code hilft Euch.

Beste Grüße,
Martin (SilverLaw)

Keine Kommentare:

Kommentar veröffentlichen