Seiten

Mittwoch, 2. März 2011

How To: Eine Aktion erst mit der vollständigen Sichtbarkeit einer Anwendung im Browserfenster starten

In meinem letzten Artikel habe ich beschrieben, wie man ein Storyboard erst dann startet, wenn die Anwendung im Browserfenster sichtbar wird. Die dort vorgestellte Lösung hat jedoch eine Beschränkung. Diese Beschränkung besteht darin, dass das Storyboard schon dann startet, wenn die Anwendung auch nur teilweise im Browserfenster sichtbar wird. Das liegt daran, dass das Ereignis "CompositionTarget.Rendering()" feuert, sobald auch nur ein Pixel der Anwendung sichtbar wird.

In diesem Artikel beschreibe ich eine Lösung, die es ermöglicht, eine Aktion erst dann zu starten, wenn die Anwendung vollständig im Browserfenster sichtbar ist. Diese Technik kann beispielsweise für das Erstellen einer Silverlight-Anwendung nützlich sein, mit der eine Werbung dargestellt werden soll.

Generelle Vorgehensweise
Die vorgestellte Lösung verwendet einen Trick. Dieser Trick besteht darin, dass oberhalb und unterhalb der eigentlichen Anwendung jeweils eine weitere Anwendung eingebettet wird. Es gibt also die Hauptanwendung, die den eigentlichen Inhalt darstellen soll. Unmittelbar oberhalb dieser Anwendung ist in der html-Seite eine weitere Anwendung eingebettet. Das ist die TopApp. Unmittelbar unterhalb der Hauptanwendung ist noch eine Anwendung in die html-Seite eingebettet. Das ist die BottomApp. Die beiden Anwendungen TopApp und BottomApp haben jeweils eine Breite, die der Breite der Hauptanwendung entspricht. Die Höhe von TopApp und BottomApp beträgt 1px.

Die drei Anwendungen kommunizieren über LocalMessaging miteinander. Wenn der Nutzer die html-Seite von oben nach unten scrollt, wird zunächst die TopApp sichtbar. Dann wird die Hauptanwendung sichtbar. Und zuletzt wird die BottomApp sichtbar. Sobald die TopApp sichtbar wird, beginnt das Ereignis "CompositionTarget.Rendering()" der TopApp zu feuern. Diesem Ereignis ist ein Delegat hinzugefügt. Die Methode, auf die dieser Delegat zeigt, hat die Aufgabe, an die Hauptanwendung eine Nachricht zu senden. Die Hauptanwendung notiert das in der Variablen "IsTopVisible" und setzt deren Wert auf "True". Damit die Methode, die im Sender für das Senden der Nachricht verantwortlich ist, entfernt wird, antwortet die Hauptanwendung dem Sender der TopApp mit einem TrueString. Damit der Sender von TopApp diese Antwort verarbeiten kann, ist dem Ereignis "SendCompleted()" des "LocalMessageSender" ein Delegat hinzugefügt. Die Methode, auf die dieser Delegat zeigt, hat die Aufgabe, die Anwort der Hauptanwendung zu verarbeiten. Das heisst, sie entfernt den Delegaten, der dem "CompositionTarget.Rendering()" von TopApp hinzugefügt worden war. Ebenso wie die TopApp, schickt auch die BottomApp an die Hauptanwendung eine Nachricht, sobald die BottomApp sichtbar ist. Die Hauptanwendung notiert das wiederum in einer Variablen. Das ist die Variable "IsBottomVisible", deren Wert auf "True" gesetzt wird. Wenn sowohl "IsTopVisible" als auch "IsBottomVisible" den Wert "True" haben, ist die Hauptanwendung vollständig sichtbar. Die Hauptanwendung kann dann die Aktion starten, die erfolgen soll, sobald sie vollständig sichtbar ist. Damit die Hauptanwendung reagieren kann, wird dem Ereignis "CompositionTarget.Rendering()" der Hauptanwendung ein Delegat hinzugefügt. Die Methode, auf die dieser Delegat zeigt, überprüft die beiden Variablen "IsTopVisible" und "IsBottomVisible" auf den Wert "True", führt dann die Aktion durch und entfernt schließlich den Delegaten wieder.

Quellcode
Nachfolgend ist der Quellcode der drei Anwendungen abgebildet.

1. "TopApp"

Imports System.Windows.Messaging

' ##############
' TOP SENDER App
' ##############

Partial Public Class MainPage
  Inherits UserControl

  Public Sub New()
    InitializeComponent()
    AddHandler CompositionTarget.Rendering, AddressOf SendMessages
    AddHandler TopSender.SendCompleted, AddressOf ListenToResponse
  End Sub

  Private TopSender As New LocalMessageSender("SilverLawTopReceiver")

  Private Sub SendMessages(ByVal sender As Object, ByVal e As EventArgs)
    TopSender.SendAsync(Boolean.TrueString)
  End Sub

  Private Sub ListenToResponse(ByVal sender As Object, ByVal e As SendCompletedEventArgs)
    If e.Response = Boolean.TrueString Then
      RemoveHandler CompositionTarget.Rendering, AddressOf SendMessages
      RemoveHandler TopSender.SendCompleted, AddressOf ListenToResponse
      MessageBox.Show("TopSender received True-Message as a response.")
    End If
  End Sub

End Class

2. "MainApp"

Imports System.Windows.Messaging

' ####################
' MAIN SILVERLIGHT APP
' ####################

Partial Public Class MainPage
  Inherits UserControl

  Public Sub New()
    InitializeComponent()
    AddHandler CompositionTarget.Rendering, AddressOf ListenToVisibility

    AddHandler MainTopReceiver.MessageReceived, AddressOf TopMessageReceived
    MainTopReceiver.Listen()
    AddHandler MainBottomReceiver.MessageReceived, AddressOf BottomMessageReceived
    MainBottomReceiver.Listen()
  End Sub


#Region " TOP VISIBILITY MESSAGING "

  Private IsTopVisible As Boolean

  Private MainTopReceiver As New LocalMessageReceiver("SilverLawTopReceiver")

  Private Sub TopMessageReceived(ByVal sender As Object, ByVal e As MessageReceivedEventArgs)
    If e.Message = Boolean.TrueString Then
      Me.IsTopVisible = True
      tbTopInfo.Text = "Top side visible."
      e.Response = Boolean.TrueString
    End If
  End Sub

#End Region


#Region " BOTTOM VISIBILITY MESSAGING "

  Private IsBottomVisible As Boolean

  Private MainBottomReceiver As New LocalMessageReceiver("SilverLawBottomReceiver")

  Private Sub BottomMessageReceived(ByVal sender As Object, ByVal e As MessageReceivedEventArgs)
    If e.Message = Boolean.TrueString Then
      Me.IsBottomVisible = True
      tbBottomInfo.Text = "Bottom side visible."
      e.Response = Boolean.TrueString
    End If
  End Sub

#End Region


#Region " LISTENING FOR FULL VISIBILITY OF THE APP "

  Private Sub ListenToVisibility(ByVal sender As Object, ByVal e As EventArgs)

    If Me.IsTopVisible = True And Me.IsBottomVisible = True Then
      LayoutRoot.Background = New SolidColorBrush(Colors.DarkGray)
      RemoveHandler CompositionTarget.Rendering, AddressOf ListenToVisibility
    End If

  End Sub

#End Region


End Class

3. "BottomApp"

Imports System.Windows.Messaging

' #################
' BOTTOM SENDER APP
' #################

Partial Public Class MainPage
  Inherits UserControl

  Public Sub New()
    InitializeComponent()
    AddHandler CompositionTarget.Rendering, AddressOf SendMessages
    AddHandler BottomSender.SendCompleted, AddressOf ListenToResponse
  End Sub

  Private BottomSender As New LocalMessageSender("SilverLawBottomReceiver")

  Private Sub SendMessages(ByVal sender As Object, ByVal e As EventArgs)
    BottomSender.SendAsync(Boolean.TrueString)
  End Sub

  Private Sub ListenToResponse(ByVal sender As Object, ByVal e As SendCompletedEventArgs)
    If e.Response = Boolean.TrueString Then
      RemoveHandler CompositionTarget.Rendering, AddressOf SendMessages
      RemoveHandler BottomSender.SendCompleted, AddressOf ListenToResponse
      MessageBox.Show("BottomSender received True-Message as a response.")
    End If
  End Sub

End Class

Beispielanwendung
Eine Beispielanwendung, mit der diese Technik demonstriert wird, findet man unter diesem Link. Sobald die Hauptanwendung vollständig sichtbar ist, wechselt die Hintergrundfarbe der Anwendung von Schwarz zu Dunkelgrau.

Viel Spass damit.

Keine Kommentare:

Kommentar veröffentlichen