DropShadowEffect for graphics in Graphic layer.

759
9
04-05-2010 05:29 AM
MuzammilAK
New Contributor
Dear All,


How to dynamically set the dropshadoweffect for graphics in a Graphic Layer. Suppose i have 2 polygons added as graphics to graphic layer. Whenever i hover upon the graphic, i should get dropshadoweffect for the symbol of that graphic. Also, the symbol color should be same i.e., if the symbol fill is RED initially, when i hover, i should see dropshadoweffect for that symbol with fill RED.

Please provide any pointers/samples.

Thank you.

Regards
Muzammil
0 Kudos
9 Replies
dotMorten_esri
Esri Notable Contributor
First off here's how to add the effect to the entire layer:
<esri:GraphicsLayer   >
   <esri:GraphicsLayer.Effect>
     <DropShadowEffect  BlurRadius="10" Color="Black" Direction="-45" Opacity="0.5" ShadowDepth="10" />
   </esri:GraphicsLayer.Effect>
</esri:GraphicsLayer>

To achieve it on hover, you have to create a custom symbol template, and use the hover visual state storyboard to turn the effect on and off on the element.
I suggest you watch our session from devsummit where custom templates and visual states is covered:
http://gisupdates.esri.com/video/devsummit2010/DEV80.wmv
0 Kudos
MuzammilAK
New Contributor
Dear SharpGIS,

Thank you for the quick reply.

I gave tried using below code as suggested by you.
<esri:GraphicsLayer >
<esri:GraphicsLayer.Effect>
<DropShadowEffect BlurRadius="10" Color="Black" Direction="-45" Opacity="0.5" ShadowDepth="10" />
</esri:GraphicsLayer.Effect>
</esri:GraphicsLayer>

But it throws AG_E_PARSER_PROPERTY_NOT_FOUND error. PLease let me know if i am missing anything. I am using Silverlight 3 version.

And regarding the second one qouted below, Could you please give me some sample code on how to make it work.
"To achieve it on hover, you have to create a custom symbol template, and use the hover visual state storyboard to turn the effect on and off on the element."

Thank you.

Regards
Muzammil
0 Kudos
DominiqueBroux
Esri Frequent Contributor
XAML code behind defines a symbol with a drop shadow effect when the mouse is over.

Dominique


<DropShadowEffect x:Name="dropShadow" BlurRadius="10" ShadowDepth="10" Direction="-45" Color="Black" Opacity="0.5" />
<esriSymbols:MarkerSymbol x:Name="DefaultMarkerSymbol"  >
  <esriSymbols:MarkerSymbol.ControlTemplate>
    <ControlTemplate>
      <Ellipse x:Name="ellipse" Height="12" Width="12" Fill="Red">
        <vsm:VisualStateManager.VisualStateGroups>
          <vsm:VisualStateGroup x:Name="CommonStates">
            <vsm:VisualState x:Name="Normal">
              <Storyboard>
              </Storyboard>
            </vsm:VisualState>
            <vsm:VisualState x:Name="MouseOver">
              <Storyboard>
               <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse"  Storyboard.TargetProperty="Effect">
                  <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{StaticResource dropShadow}" />
                </ObjectAnimationUsingKeyFrames>
              </Storyboard>
            </vsm:VisualState>
          </vsm:VisualStateGroup>
        </vsm:VisualStateManager.VisualStateGroups>
      </Ellipse>
    </ControlTemplate>
  </esriSymbols:MarkerSymbol.ControlTemplate>
</esriSymbols:MarkerSymbol>
0 Kudos
MuzammilAK
New Contributor
Thanks Nielsen and Dominique.

I have tried below code. This works when run in Expression Blend 3. But does not work in Silverlight Application built using visual studio 2008 sp1. Throws the same AG_E_PARSER_PROPERTY_NOT_FOUND error. But runs perfectly in expression blend. I am not able to figure out what is the problem.

This code is similar to what Dominique had replied earlier. The below code runs perfectly when the "Fill" property of path "Element" is fixed (i.e. RED or GREEN or any other fixed color). But i want it to be dynamic so that different graphics will have different colors. I have tried "Binding" as shown below but it is not working. Code behind follows this XAML.

<DropShadowEffect Color="Black" Direction="25" BlurRadius="7" Opacity=".75" ShadowDepth="10" x:Name="dropShadow"/>
        <symbols:FillSymbol x:Name="ColorCode">
            <symbols:FillSymbol.ControlTemplate>
                <ControlTemplate>
                    <Path x:Name="Element" StrokeThickness="3" Stroke="#ff000000"
                          StrokeLineJoin="Round" Fill="{Binding Path=MyFill}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Element" Storyboard.TargetProperty="Effect">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{StaticResource dropShadow}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                       </Path>
                </ControlTemplate>
            </symbols:FillSymbol.ControlTemplate>
        </symbols:FillSymbol>


Code behind:

private Brush fillBrush;
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, e);
        }

        public Brush MyFill
        {

            get { return fillBrush; }

            set {
                if (fillBrush == value)
                    return;

                fillBrush = value;
                this.OnPropertyChanged(new PropertyChangedEventArgs("MyFill"));

            }
        }

setting the brush as folows
SolidColorBrush scb=new SolidColorBrush(<dynamic color>);
MyFill=(Brush)scb;

The dynamic binding is not happening. Please provide pointers regarding this.

Thank you.

regards
Muzammil
0 Kudos
MuzammilAK
New Contributor
Resolved above error.Re-attched references and now the code works even in Visual Studio 2008 sp1. But one thing that remains still is setting the "Fill" property dynamically in the code sent in earlier post-
<Path x:Name="Element" StrokeThickness="3" Stroke="#ff000000"
StrokeLineJoin="Round" Fill="{Binding Path=MyFill}">

Please help ASAP.

Thank you.

Regards
Muzammil
0 Kudos
DominiqueBroux
Esri Frequent Contributor
Hi Muzammil,

In your example, I don't understand which class is implementing MyFill.

It should be a new symbol class inheriting from FillSymbol, then you should use your own symbol class in the xaml and  bind to the symbol property using {Binding Symbol.MyFill}.

If you only need a 'Fill' property, a shortcut might be to reuse the SimpleFillSymbol which has already a Fill property.

Example:

      <symbols:SimpleFillSymbol x:Name="ColorCode" Fill="Blue">
        <symbols:SimpleFillSymbol.ControlTemplate>
          <ControlTemplate>
            <Path x:Name="Element" StrokeThickness="3" Stroke="#ff000000"
StrokeLineJoin="Round" Fill="{Binding Symbol.Fill}">
              <Path.Effect>
                <DropShadowEffect Color="Black" Direction="25" BlurRadius="7" Opacity="0" ShadowDepth="10" />
              </Path.Effect>
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal">
                    <Storyboard>
                      <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="Element" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)" To="0" Duration="0:0:0.1" />
                      <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="Element" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.Opacity)" To="0" Duration="0:0:0.1" />
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="MouseOver">
                    <Storyboard>
                      <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="Element" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)" To="10" Duration="0:0:0.1" />
                      <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="Element" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.Opacity)" To="0.75" Duration="0:0:0.1" />
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>
            </Path>
          </ControlTemplate>
        </symbols:SimpleFillSymbol.ControlTemplate>
      </symbols:SimpleFillSymbol>

Then you can change the color by code by setting the 'Fill' property of this SimpleFillSymbol (or of your own symbol class, if you'd rather to create one)

Note : in this sample, I apply the effect with a smooth transition using ShadowDepth and Opacity properties.

Dominique
0 Kudos
dotMorten_esri
Esri Notable Contributor
Muzammil: I really encourage you to watch the video I linked to in my first reply. I'm covering all these things in that session.
0 Kudos
MuzammilAK
New Contributor
Great Stuff Dominique and Morten.

@Morten: I have seen your video, but it took some time for me to comprehend what was happening. Later i understood and it is really great stuff which was demo'ed with ease. Thank you.
@Dominique: I used your advice in above post -"Then you can change the color by code by setting the 'Fill' property of this SimpleFillSymbol ". It worked perfectly. Thank you.

I have done in using 2 ways as follows.
Process 1:
1. Read xml (containing 260 polygon features) and parse it as polygons.
2. Loop through and create Graphic objects with geometry as above polygons. Also, bind MouseEnter and MouseLeave events for each Graphic object.
3. Give symbol as simplefillsymbol.
4.Add graphic collection to graphic layer.
5.Run. Now on hovering a graphic, MouseEnter event fires where I am overwriting the symbol of graphic with another fillsymbol which has dropshadoweffect in its controltemplate.

void loader2_MouseEnter(object sender, ESRI.ArcGIS.Samples.GeoRss.GeoRssLoader.MouseEnterEventArgs e)
        {
                 Graphic g = e.Graphic;
                 SolidColorBrush brus = (SolidColorBrush)g.Attributes["Color"];
                 ColorCode.Fill = brus; // ColorCode is FillSymbol declared in UserControl.Resources with dropshadoweffect
                g.Symbol = ColorCode;
       }

XAML for ColorCode variable used above:
<UserControl.Resources>
<symbols:FillSymbol x:Name="ColorCode" Fill="Transparent">
            <symbols:FillSymbol.ControlTemplate>
                <ControlTemplate>
                    <Path x:Name="Element" StrokeThickness="3" Stroke="#ff000000"
                          StrokeLineJoin="Round" Fill="{Binding Symbol.Fill}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal">
                                        <Storyboard>
                                    </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="MouseOver">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Element" Storyboard.TargetProperty="Effect" >
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{StaticResource dropShadow22}" />
                                            </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                    </Path>
                </ControlTemplate>
            </symbols:FillSymbol.ControlTemplate>
        </symbols:FillSymbol>
</UserControl.Resources>
6. Now in MouseLeave event, i am assigning back the normal symbol.
void loader2_MouseLeave(object sender, ESRI.ArcGIS.Samples.GeoRss.GeoRssLoader.MouseLeaveEventArgs e)
        {
            Graphic g = e.Graphic;
            FillSymbol sfs = new FillSymbol();
            SolidColorBrush brus = (SolidColorBrush)g.Attributes["Color"];
            sfs.Fill = brus;
            g.Symbol = sfs;
       }

This logic is working but i feel it is not so optimized. I have tried another approach as follows for which i got partial output 😞

Process 2:
1. Read xml (containing 260 polygon features) and parse it as polygons.
2. Loop through and create Graphic objects with geometry as above polygons. No MouseEnter and MouseLeave events are bound.
3. Give symbol as DynamicFillSymbol (CustomFillSymbol) whihc extends FIllSymbol. i have attched the class file. The constructor loads the ControlTemplate.
4.Add graphic collection to graphic layer.
5. Run. I should get the same feel. But all the graphics look in MouseOver state.
may be it is not applying or my control template is wrong. Please have a look of class file which contains Controltemplate.

I think this approach will be good as we need not impplement MouseEnter and MouseLeave events and set/reset symbols.


Thanks for your patience and concern.

Regards
Muzammil
0 Kudos
DominiqueBroux
Esri Frequent Contributor
Hi Muzammil,

Your process 1 is not the best because you are trying to manage yourself the mouse events and the symbology. That's not useful. By defining a symbol with 2 visual states : 'MouseOver' and 'Normal', you should get the expected result without any development.

For the color depending on an attribute, you should consider to use a renderer (see http://resources.esri.com/help/9.3/arcgisserver/apis/silverlight/help/Working_symbols_renderers.htm ).
If you have a predefined list of possible color, you can declare the list of possible symbols as static resources and use the renderer to associate a symbol to an attribute value. This would be the simplest option.

If you really need dynamically generated symbols, you will have to manage that by code with your own custom symbol. You will have to instantiate a new symbol for each color (or for each graphic, if in a first time, performances are not a point).
Concerning the development of your custom class, you should keep separated xaml and C# parts. It would be simpler to understand (See Morten's video).


Dominique
0 Kudos