This sample demonstrates a technique for creating a smooth visual transition when changing the visibility of a layer. The static CompositionTarget class represents the display surface of a Silverlight application. The CompositionTarget.Rendering event is used to determine when a frame of visuals are rendered. This sample uses the Rendering event to change layer visibility and opacity and create a smooth visual fade from one layer to another.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using ESRI.ArcGIS.Client;
namespace ArcGISWPFSDK
{
publicpartialclass LayerTransitions : UserControl
{
int progress = 0;
Layer fromLayer;
Layer toLayer;
Layer pendingLayer;
Layer animatingLayer;
public LayerTransitions()
{
InitializeComponent();
MyMap.Progress += MyMap_Progress;
}
privatevoid RadioButton_Checked(object sender, RoutedEventArgs e)
{
RadioButton radioButton = sender as RadioButton;
if (MyMap != null)
{
Layer from = null;
foreach (Layer layer in MyMap.Layers)
if (layer.Visible && layer.Opacity == 1)
{
from = layer;
break;
}
string radioButtonContent = radioButton.GroupName;
if (radioButtonContent != "")
{
Layer to = MyMap.Layers[radioButton.Content.ToString()];
Fade(from, to);
}
}
}
privatevoid Fade(Layer from, Layer to)
{
// If in process of animatingif (animatingLayer != null)
{
pendingLayer = to;
return;
}
pendingLayer = null;
to.Opacity = 0;
to.Visible = true;
Dispatcher.BeginInvoke(new Action(() =>
{
if (progress >= 97)
StartFade(from, to);
else// Wait for layer to load before fading to it
{
EventHandler<ProgressEventArgs> handler = null;
handler = (s, e) =>
{
if (e.Progress >= 97)
{
MyMap.Progress -= handler;
StartFade(from, to);
}
};
MyMap.Progress += handler;
}
}));
}
privatevoid StartFade(Layer from, Layer to)
{
fromLayer = from;
toLayer = to;
// If fromLayer is below toLayer, layer to animate is toLayer. // If fromLayer is above toLayer, layer to animate is fromLayer. The toLayer opacity // should be set to completely opaque. if (MyMap.Layers.IndexOf(fromLayer) < MyMap.Layers.IndexOf(toLayer))
animatingLayer = toLayer;
else
{
animatingLayer = fromLayer;
toLayer.Opacity = 1;
}
// Listen for when a frame is rendered
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
// Change the opacity of the fromLayer and toLayerdouble opacity = -1;
if (animatingLayer == fromLayer)
{
opacity = Math.Max(0, fromLayer.Opacity - .05);
fromLayer.Opacity = opacity;
}
else
{
opacity = Math.Min(1, toLayer.Opacity + .05);
toLayer.Opacity = opacity;
}
// When transition complete, set reset properties and unhook handler if (opacity == 1 || opacity == 0)
{
fromLayer.Opacity = 0;
fromLayer.Visible = false;
animatingLayer = null;
CompositionTarget.Rendering -= CompositionTarget_Rendering;
// If layer pending animation, start fadingif (pendingLayer != null)
Fade(toLayer, pendingLayer);
pendingLayer = null;
}
}
// Track overall progress of loading map content privatevoid MyMap_Progress(object sender, ESRI.ArcGIS.Client.ProgressEventArgs e)
{
progress = e.Progress;
}
}
}
Imports System.Windows.Controls
Imports System.Windows
Imports ESRI.ArcGIS.Client
Imports System
Imports System.Windows.Media
Namespace ArcGISWPFSDK
PartialPublicClass LayerTransitions
Inherits UserControl
Private progress AsInteger = 0
Private fromLayer As Layer
Private toLayer As Layer
Private pendingLayer As Layer
Private animatingLayer As Layer
PublicSubNew()
InitializeComponent()
AddHandler MyMap.Progress, AddressOf MyMap_Progress
EndSubPrivateSub RadioButton_Checked(ByVal sender AsObject, ByVal e As RoutedEventArgs)
Dim radioButton As RadioButton = TryCast(sender, RadioButton)
If MyMap IsNotNothingThenDim [from] As Layer = NothingForEach layer As Layer In MyMap.Layers
If layer.Visible AndAlso layer.Opacity = 1 Then
[from] = layer
ExitForEndIfNext layer
Dim radioButtonContent AsString = radioButton.GroupName
If radioButtonContent <> ""ThenDim [to] As Layer = MyMap.Layers(radioButton.Content.ToString())
Fade(from, [to])
EndIfEndIfEndSubPrivateSub Fade(ByVal [from] As Layer, ByVal [to] As Layer)
' If in process of animatingIf animatingLayer IsNotNothingThen
pendingLayer = [to]
ReturnEndIf
pendingLayer = Nothing
[to].Opacity = 0
[to].Visible = True
Dispatcher.BeginInvoke(Sub()
If progress >= 97 Then
StartFade([from], [to])
Else'Wait for layer to load before fading to itDim handler As EventHandler(Of ProgressEventArgs) = Nothing
handler = Sub(s, e)
If e.Progress >= 97 ThenRemoveHandler MyMap.Progress, handler
StartFade([from], [to])
EndIfEndSubAddHandler MyMap.Progress, handler
EndIfEndSub)
EndSubPrivateSub StartFade(ByVal [from] As Layer, ByVal [to] As Layer)
fromLayer = [from]
toLayer = [to]
' If fromLayer is below toLayer, layer to animate is toLayer. ' If fromLayer is above toLayer, layer to animate is fromLayer. The toLayer opacity ' should be set to completely opaque. If MyMap.Layers.IndexOf(fromLayer) < MyMap.Layers.IndexOf(toLayer) Then
animatingLayer = toLayer
Else
animatingLayer = fromLayer
toLayer.Opacity = 1
EndIf' Listen for when a frame is renderedAddHandler CompositionTarget.Rendering, AddressOf CompositionTarget_Rendering
EndSubPrivateSub CompositionTarget_Rendering(ByVal sender AsObject, ByVal e As EventArgs)
' Change the opacity of the fromLayer and toLayerDim opacity AsDouble = -1
If animatingLayer Is fromLayer Then
opacity = Math.Max(0, fromLayer.Opacity - 0.05)
fromLayer.Opacity = opacity
Else
opacity = Math.Min(1, toLayer.Opacity + 0.05)
toLayer.Opacity = opacity
EndIf' When transition complete, set reset properties and unhook handler If opacity = 1 OrElse opacity = 0 Then
fromLayer.Opacity = 0
fromLayer.Visible = False
animatingLayer = NothingRemoveHandler CompositionTarget.Rendering, AddressOf CompositionTarget_Rendering
' If layer pending animation, start fadingIf pendingLayer IsNotNothingThen
Fade(toLayer, pendingLayer)
EndIf
pendingLayer = NothingEndIfEndSub' Track overall progress of loading map content PrivateSub MyMap_Progress(ByVal sender AsObject, ByVal e As ESRI.ArcGIS.Client.ProgressEventArgs)
progress = e.Progress
EndSubEndClassEndNamespace