CompositeSymbol in WPF

3358
6
Jump to solution
02-14-2012 10:45 PM
AvnerKashtan
New Contributor II
I'm trying to develop a CompositeSymbol to display on the WPF map control, to add a text label to a PictureMarkerSymbol. I'm using a FeatureLayer, so my server-defined labels aren't defined (the layer is stored in an MXD on an ArcGIS 10 server).

Since I couldn't find a preexisting Symbol that can do that, I'm thinking of writing a custom class - LabeledMarkerSymbol (derived from MarkerSymbol) which receives a MarkerSymbol in its constructor, then edits its ControlTemplate to add a new text label.

My question is: is there a better way to do this?
Is there a CompositeSymbol object that can receive a MarkerSymbol and a TextSymbol and render them together?
Or do I just tear into the ControlTemplate myself?
1 Solution

Accepted Solutions
SimonGuard
New Contributor II
This is what I did...

  public class PlotObjectSymbol : ESRI.ArcGIS.Client.Symbols.MarkerSymbol     {         public PlotObjectSymbol()         {             string template = @"<ControlTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""                     xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" >     <Canvas>         <Rectangle Fill=""{Binding Symbol.Symbol}"" Width=""{Binding Symbol.Size}"" Height=""{Binding Symbol.Size}"" />         <TextBlock              Canvas.Top=""{Binding Symbol.Size}""              Text=""{Binding Symbol.LabelText}""              Style=""{Binding Symbol.LabelStyle}"" />     </Canvas> </ControlTemplate>";              System.IO.MemoryStream templateStream = new System.IO.MemoryStream(System.Text.UTF8Encoding.Default.GetBytes(template));             ControlTemplate = System.Windows.Markup.XamlReader.Load(templateStream) as ControlTemplate; }          #region LabelText Property         public string LabelText         {             get { return (string)base.GetValue(LabelTextProperty); }             set { base.SetValue(LabelTextProperty, value); }         }         public static DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText", typeof(string), typeof(PlotObjectSymbol), new UIPropertyMetadata(""));         #endregion  ...


I just could not figure out how to center the text. I'm not a WPF guru.

View solution in original post

0 Kudos
6 Replies
JenniferNery
Esri Regular Contributor
You can use a GraphicsLayer on top of your FeatureLayer to hold the label.
0 Kudos
SimonGuard
New Contributor II
This is what I did...

  public class PlotObjectSymbol : ESRI.ArcGIS.Client.Symbols.MarkerSymbol     {         public PlotObjectSymbol()         {             string template = @"<ControlTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""                     xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" >     <Canvas>         <Rectangle Fill=""{Binding Symbol.Symbol}"" Width=""{Binding Symbol.Size}"" Height=""{Binding Symbol.Size}"" />         <TextBlock              Canvas.Top=""{Binding Symbol.Size}""              Text=""{Binding Symbol.LabelText}""              Style=""{Binding Symbol.LabelStyle}"" />     </Canvas> </ControlTemplate>";              System.IO.MemoryStream templateStream = new System.IO.MemoryStream(System.Text.UTF8Encoding.Default.GetBytes(template));             ControlTemplate = System.Windows.Markup.XamlReader.Load(templateStream) as ControlTemplate; }          #region LabelText Property         public string LabelText         {             get { return (string)base.GetValue(LabelTextProperty); }             set { base.SetValue(LabelTextProperty, value); }         }         public static DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText", typeof(string), typeof(PlotObjectSymbol), new UIPropertyMetadata(""));         #endregion  ...


I just could not figure out how to center the text. I'm not a WPF guru.

View solution in original post

0 Kudos
AvnerKashtan
New Contributor II
You can use a GraphicsLayer on top of your FeatureLayer to hold the label.


I think that's a rather cumbersome method. If I want to control visibility of specific layers, it means I need to add a GraphicsLayer for each layer separately, each with that layers' labels. If I want to hide a layer, I need to hide its complementary GraphicsLayer as well. If I move a symbol around, I need to locate its corresponding TextSymbol label on a different layer and move that too. This seems to be a lot less elegant than just adding the label as part of the symbol itself.

Ideally, I'd want to have the label rendered automatically, but it seems FeatureLayers don't support that. :(
0 Kudos
AvnerKashtan
New Contributor II
I'm pretty sure your example is missing some parts - you have the LabelText property defined, but not the rest of them, from Symbol to Size to LabelStyle.

This is what I did...

  public class PlotObjectSymbol : ESRI.ArcGIS.Client.Symbols.MarkerSymbol
    {
        public PlotObjectSymbol()
        {
            string template =
@"<ControlTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                   xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" >
    <Canvas>
        <Rectangle Fill=""{Binding Symbol.Symbol}"" Width=""{Binding Symbol.Size}"" Height=""{Binding Symbol.Size}"" />
        <TextBlock
            Canvas.Top=""{Binding Symbol.Size}""
            Text=""{Binding Symbol.LabelText}""
            Style=""{Binding Symbol.LabelStyle}"" />
    </Canvas>
</ControlTemplate>";

            System.IO.MemoryStream templateStream = new System.IO.MemoryStream(System.Text.UTF8Encoding.Default.GetBytes(template));
            ControlTemplate = System.Windows.Markup.XamlReader.Load(templateStream) as ControlTemplate;
}

        #region LabelText Property
        public string LabelText
        {
            get { return (string)base.GetValue(LabelTextProperty); }
            set { base.SetValue(LabelTextProperty, value); }
        }
        public static DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText", typeof(string), typeof(PlotObjectSymbol), new UIPropertyMetadata(""));
        #endregion

...


I just could not figure out how to center the text. I'm not a WPF guru.
0 Kudos
MofoloMamabolo
New Contributor

What I do is implement IRenderer along with the PlotObjectSymbol class: like this,

ControlTemplate in App.xaml:

<ControlTemplate x:Key="cntltemplate">

            <StackPanel Orientation="Horizontal">

                    <Image Source="{Binding Symbol.ImageSource, Converter={StaticResource StringToImageSourceConverter}}"/>

                    <TextBlock Text="{Binding Symbol.LabelText}" />

            </StackPanel>

</ControlTemplate>

StringToImageSourceConverter is an IValueConverter implementation that converts strings to an ImageSource.

public class PlotObjectSymbol : ESRI.ArcGIS.Client.Symbols.MarkerSymbol

{

        public PlotObjectSymbol()

        {

            ControlTemplate = Application.Current.Resources["cntltemplate"] as ControlTemplate;

        }

public string LabelText

        {

            get { return (string) base.GetValue(LabelTextProperty); }

            set { base.SetValue(LabelTextProperty, value); }

        }

        public static DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText", typeof (string), typeof (PlotObjectSymbol), new UIPropertyMetadata(string.Empty));

public string ImageSource

        {

            get { return (string)base.GetValue(ImageSourceProperty); }

            set { base.SetValue(ImageSourceProperty, value); }

        }

        public static DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(string), typeof(PlotObjectSymbol), new UIPropertyMetadata(string.Empty));

}

public class LabelRenderer : IRenderer

{

     public Symbol GetSymbol(Graphic graphic)

     {

                    var pt = new PlotObjectSymbol();

                    pt.LabelText = (string)graphic.Attributes["Name"];

                    pt.ImageSource = "Images/marker.png";

                    graphic.Symbol = pt;

                    return graphic;

     }

}

Also, keep the ControlTemplate xaml simple, as this turns to slow down the rendering refresh rate.

Another thing: <esri:Map UseAcceleratedDisplay="False" .../>

0 Kudos
SimonGuard
New Contributor II
You are right. That's why there is a '...' at the end of the code fragment. I thought you would be able to fill in the missing parts. (Plus add anything else you need).
0 Kudos