Identify Tool, multiple layers, Hyperlinks

1052
6
02-03-2011 07:15 AM
AngelGonzalez
Occasional Contributor II
The Identify Tool in the SDK works great, but how would you go about setting up one of the returned field as a hyperlink?  In cases where multiple layers are returned from the Identify Tool and each layer has one or more hyperlink "fields", how would you go about creating these hyperlinks in the SL datagrid for the Identify Tool?

I am looking at the DataGridTemplateColumn.CellTemplate sample I found but since each layer has different fields name is this possible? (see code below, converter check for null value in a field)

<sdk:DataGridTemplateColumn Header="Code" Width="40">
    <sdk:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <HyperlinkButton IsHitTestVisible="{Binding Path=MyUrl,
                             Converter={StaticResource InvertNullOrEmptyConverter}}"
                             Content="{Binding Code}" NavigateUri="{Binding MyUrl}"/>
        </DataTemplate>
    </sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>



Note: the dataGrid will have many fields returned but one or more can be a hyperlink field?


ESRI - Ideally if we can have a Datatype of Hyperlink that we can define within the feature class (see attached pic) and have it display as a hyperlink in a SL dataGrid without going thru extra steps would be great.

Any guidance will be appreciated.



Below is the code from the Identify Tool that pertains to the DataGrid:

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
                                  Width="330" MinHeight="200" Grid.Row="1">
                        <slData:DataGrid x:Name="IdentifyDetailDataGrid" AutoGenerateColumns="False" HeadersVisibility="None"
                                         Background="White">
                            <slData:DataGrid.Columns>
                                <slData:DataGridTextColumn Binding="{Binding Path=Key}" FontWeight="Bold"/>
                                <slData:DataGridTextColumn Binding="{Binding Path=Value}"/>
                            </slData:DataGrid.Columns>
                        </slData:DataGrid>
                    </ScrollViewer>
0 Kudos
6 Replies
AliMirzabeigi
New Contributor
Angel,

Unfortunately there is no such hyperlinking data type in feature classes and you have to use templated data grid columns to achieve this use case. Since you are dealing with multiple layers you can either have one data grid control and in the code-behind dynamically create its columns collection based upon the Url property of the sender object in the ExecuteCompleted event handler of your IdentifyTask, or have multiple data grid controls with their corresponding columns definitions for each service that you are querying against and change their visibilities with regards to the Url property of the sender object in the ExecuteCompleted event handler of your IdentifyTask.

Hope this helps.
0 Kudos
ErikEngstrom
Occasional Contributor II
I'm in this exact boat and I was going to pretty much ask the very same question, almost verbatim 🙂
This is why the deprecation of the .NET ADF bothers me. Creating an application using the ESRI templates in the ADF, this was extremely easy to do. Simply editing the template for each data layer was a matter of editing a little bit of HTML code.

I have multiple map services, with multiple layers. Some of which include a hyperlink and some of those layers contain multiple fields with hyperlinks. What's even MORE frustrating, is that with Silverlight you can't even copy the text in the identify grid. Without a hyperlink, you'd be forced to write it down on paper.

COMPLETELY RIDICULOUS!
0 Kudos
DaveTimmins
Occasional Contributor II
You can create a custom control that parses the text value and renders either a textblock or hyperlinkbutton based on the string. Something like this:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:YourNamespaceHere">
    <Style TargetType="local:LinkLabel">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:LinkLabel">                    
                    <StackPanel Margin="2,0,2,0" VerticalAlignment="Center">
                        <TextBlock x:Name="LabelText" Visibility="Collapsed" />
                        <HyperlinkButton x:Name="LinkText" Visibility="Collapsed" />
                    </StackPanel>                    
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>


[TemplatePart(Name = "LabelText", Type = typeof(TextBlock)),
    TemplatePart(Name = "LinkText", Type = typeof(HyperlinkButton))]
    public class LinkLabel : Control
    {
        private TextBlock _labelText;
        private HyperlinkButton _linkText;
        private const string Pattern = @"\b(https?|ftp|file)://[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|]";  

        public LinkLabel()
        {
            DefaultStyleKey = typeof(LinkLabel);
        }

        public string TextValue
        {
            get { return (string)GetValue(TextValueProperty); }
            set { SetValue(TextValueProperty, value); }
        }

        public static readonly DependencyProperty TextValueProperty =
            DependencyProperty.Register("TextValue", typeof(string), typeof(LinkLabel), new PropertyMetadata(string.Empty, TextValuePropertyChanged));

        private static void TextValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var linkLabel = sender as LinkLabel;
            if (linkLabel != null) linkLabel.Refresh();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _labelText = (TextBlock)GetTemplateChild("LabelText");
            _linkText = (HyperlinkButton)GetTemplateChild("LinkText");

            Refresh();
        }

        public void Refresh()
        {
            if (_labelText == null || _linkText == null)
                return;

            var regex = new Regex(Pattern);
            if (string.IsNullOrWhiteSpace(TextValue) || !regex.IsMatch(TextValue))
            {
                _labelText.Text = TextValue;

                _labelText.Visibility = Visibility.Visible;
                _linkText.Visibility = Visibility.Collapsed;
            }
            else
            {
                _linkText.Content = TextValue;
                _linkText.NavigateUri = new Uri(TextValue, UriKind.RelativeOrAbsolute);

                _labelText.Visibility = Visibility.Collapsed;
                _linkText.Visibility = Visibility.Visible;
            }
        }
    }
0 Kudos
ErikEngstrom
Occasional Contributor II
Thanks Dave, but how would I bind the TextValue to the IdentifyResults?
I created the control and added it to my generic.xaml. I can see the stack panel in my mainpage.xaml. However I believe I need point the control to what is returned in my identify results and that's where I'm running into an issue now.

As it is, I have a datagrid that binds the results of the IdentifyResults (Key/Value).
       
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto"  Width="230" MinHeight="200" Grid.Row="1">
<sdk:DataGrid x:Name="IdentifyDetailsDataGrid" AutoGenerateColumns="False" HeadersVisibility="None" background="White">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding Key}" FontWeight="Bold" />
<sdk:DataGridTextColumn Binding="{Binding Value}" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</ScrollViewer>


public void ShowFeatures(List<IdentifyResult> results)
        {
            _dataItems = new List<DataItem>();
            if (results != null && results.Count > 0)
            {
                IdentifyComboBox.Items.Clear();
                foreach (IdentifyResult result in results)
                {
                    Graphic feature = result.Feature;
                    string title = result.Value.ToString() + " (" + result.LayerName + ")";
                    _dataItems.Add(new DataItem()
                    {
                        Title = title,
                        Data = feature.Attributes
                    });
                    IdentifyComboBox.Items.Add(title);
                }
                IdentifyComboBox.SelectedIndex = 0;
            }
        }


ShowFeatures(args.IdentifyResults);
0 Kudos
DaveTimmins
Occasional Contributor II
Hi Erik,

assuming you have the previous code as a control you need to amend your xaml to use that to display the value, something like

        <slData:DataGrid.Columns>
                        <slData:DataGridTextColumn Binding="{Binding Path=Key}" />
                        <slData:DataGridTemplateColumn>
                            <slData:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <localControls:LinkLabel TextValue="{Binding Path=Value}" />
                                </DataTemplate>
                            </slData:DataGridTemplateColumn.CellTemplate>
                        </slData:DataGridTemplateColumn>
                    </slData:DataGrid.Columns>
0 Kudos
ErikEngstrom
Occasional Contributor II
Dave,
I was able to get it to work! I've made a few tweaks to your code, but for the most part, it works great! I'm still very new to Silverlight (and programming in general). These snippets that the user community provides makes things much more managable and has taught me greatly!

This code is a perfect example of what should have been included from the beginning!
THANKS AGAIN DAVE!
0 Kudos