Select to view content in your preferred language

LegendItems - Labels & Symbology

813
2
06-09-2011 12:35 PM
KeithGanzenmuller
Frequent Contributor
I have what I hope is a simple question.
I am working on saving a map to PDF, which works fine.
The map and title, scale bar and north arrow save to the PDF fine.
Working on a legend which applies only to the PDF map, where the map page shows a listbox with all the map layers (from ESRI sample) and if the user selects a layer, say Planning (Dynamic Map Service) a legend will become visible with the Planning map service and it's sublayers with layers and symbology, both visible and not visible.
The legend in the pdf will show only the visible sublayers and symbology from the legend spread out in up to 4 columns (I hope).
What I want to do is get the visible sublayers associated with the selected map service and get the label and symbology. I would like to display the items (symbology and label) in columns of say 2 or 3 items per column across the page under the map.
So a couple questions:
Can legenditems (symbology and label) be displayed in a control of other than a legend, I assume so?
If not, can I create a legend for each column and add say, 4 items to each legend to display the items?

I have gotten this far:
'myLegend has only one possible map service at a time.
           For Each litems In myLegend.LayerItems.Item(0).LayerItems
                MessageBox.Show(litems.Label)
                If litems.IsVisible Then
                    For Each legitems In litems.LegendItems
                        MessageBox.Show(legitems.Label)
                    Next
                End If
            Next

It seems to work as far as, the first messagebox displays the sublayer name and the second message box shows the label for the unique values in the legend if the layer is visible.
So...how can i put the label and its symbology in my columns?


Thanks,
Keith
0 Kudos
2 Replies
DominiqueBroux
Esri Frequent Contributor
Can legenditems (symbology and label) be displayed in a control of other than a legend, I assume so?
It seems to work as far as, the first messagebox displays the sublayer name and the second message box shows the label for the unique values in the legend if the layer is visible.
So...how can i put the label and its symbology in my columns?

In your code legItems.Label and legeItems.ImageSource are the label and it's symbology, so you could store them in your own collection and display them in your own control other than a legend.

Nevertheless, the easiest way seems to retemplate the legend control by using a WrapPanel (needs the toolkit).

Something like:
 
<Style x:Key="WrapPanel" TargetType="esri:Legend">
    <Setter Property="LayerItemsMode" Value="Flat" />
    <Setter Property="ShowOnlyVisibleLayers" Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="esri:Legend">
                <ListBox x:Name="listBox" ItemsSource="{TemplateBinding LayerItemsSource}"                                      
                                    Background="{TemplateBinding Background}"
                                    Foreground="{TemplateBinding Foreground}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"      
                                    Padding="{TemplateBinding Padding}"
                                ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                ScrollViewer.VerticalScrollBarVisibility="Disabled"
                                    >
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Controls:WrapPanel Orientation="Vertical"/>
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical">
                                <!--Layer Item-->
                                <ContentPresenter Content="{Binding}" ContentTemplate="{Binding Template}" />
                                <!--Legend Items-->
                                <ItemsControl ItemsSource="{Binding LegendItems}">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate >
                                            <ContentPresenter Content="{Binding}" ContentTemplate="{Binding Template}" />
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>


With this legend style, the main listbox is using a wrap panel to organize the items, one item being itself a listbox with all swatches of a sublayer.

You might want not to see the sublayers but only the swatches.

This style with only one level of listbox shoud help:
 
<local:SelectManyLegendItems x:Key="selectManyLegendItems" />
<Style x:Key="WrapPanelItems" TargetType="esri:Legend">
    <Setter Property="LayerItemsMode" Value="Flat" />
    <Setter Property="ShowOnlyVisibleLayers" Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="esri:Legend">
                <ListBox x:Name="listBox" ItemsSource="{Binding LayerItemsSource, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource selectManyLegendItems}}"                                      
                                    Background="{TemplateBinding Background}"
                                    Foreground="{TemplateBinding Foreground}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"      
                                    Padding="{TemplateBinding Padding}"
                                ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                ScrollViewer.VerticalScrollBarVisibility="Disabled"
                                    >
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Controls:WrapPanel Orientation="Vertical"  />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical">
                                <ContentPresenter Content="{Binding}" ContentTemplate="{Binding Template}" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>


The issue with this style is that we need to initialize the ItemsSource of the listbox with all swatches (i.e. all legendItems) of all sublayers.
This can easily be done by using the LINQ 'SelectMany' in a converter.
Code of the converter:
 
public sealed class SelectManyLegendItems : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value as IEnumerable<LegendItemViewModel>).OfType<LayerItemViewModel>().SelectMany(layerItem => layerItem.LegendItems);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw (new Exception(string.Format("Not implemented or Bad usage of {0}", GetType().Name)));
    }
}


Hope this help.
0 Kudos
KeithGanzenmuller
Frequent Contributor
Great, thanks for the excellent example.
Concerning listboxes in general, is there a way to tell when the list of items has completed loading?
The items load asynchronously correct? So in some cases in the past I had trouble using capturing the listbox image since all the items hadn't been displayed yet even though they were added programatically. I looked at the Listbox events and couldn't find and exact match, I suppose one could use a change is height or width somehow.

Thanks,
Keith
0 Kudos