Помогите усовершенствовать TOC-control Silverlight

0 голосов
спросил 22 Ноя, 10 от Mitrich (13,680 баллов) в категории Программные продукты Esri
Гуру программирования, посмотрите пожалуйста, кто может помочь с Сильверлайтом.
есть вот такой контрол для TOC + легенда

подробнее и скачать на сайте разработчика


он дает возможность не перешедшим на десятку+sp1, создавать список слоев с подслоями, даже если ваш сервер 9.3.1 не выставлен наружу
однако оказалось, контрол не поддерживает тайловые слои OSM VE  и им подобные.
как бы для всех неподдерживаемых типов слоев создать минимальный функционал: добавление в список, поддержка имени слоя, смена видимости при выборе чекбокса и прозрачности  слайдером.

источником данных для списка слоев в ТОС служит набор сервисов. Вот бы создать dummy-service, чтобы можно было слой добавить в TOC


я пробовал, но у меня не получилось Cry
технические подробности, если заинтересовались:
попробовал руками создать пустой сервис для неподдерживаемого типа слоя,
в ТОС.cs
процедура
private void legend_LoadCompleted(object sender, EventArgs e)
        {
            MapService service = (MapService)sender;
            addHandlers((TreeViewItem)tocTree.ItemContainerGenerator.ContainerFromItem(service));

передает в процедуру addHandlers - null. Что нужно передать в ContainerFromItem, дабы заработало?

       private void addHandlers(TreeViewItem tvitem)
        {
            if (tvitem == null) return;

соответсвенно нет tvitem , нет к нему привязанных событий.

думаю, не только я скажу большое спасибо
(кстати, легенда в 2.1rc тоже не поддерживает OSM. и

The legend supports the following layer types: ArcGISDynamicMapServiceLayer, ArcGISTiledMapServiceLayer, FeatureLayer. If the layer is an ArcGISDynamicMapServiceLayer or ArcGISTiledMapServiceLayer created using ArcGIS Server 10.0 SP1 or higher the legend is generated using the REST Map Service legend resource.

http://mapservice-url/legend

If the layers are version 10 or lower the legend is created using the ArcGIS.com legend service. In order to use the ArcGIS.com legend service your map service needs to be publicly accessible and your application must be able to access ArcGIS.com.)




29 Ответы

0 голосов
ответил 22 Ноя, 10 от TDenis (42,620 баллов)
addHandlers((TreeViewItem)tocTree.ItemContainerGenerator.ContainerFromItem(service));
передает в процедуру addHandlers - null. Что нужно передать в ContainerFromItem, дабы заработало?

Я гляжу, они там хитро сделали.
Вы всё правильно вызываете. За исключением того, что Вам надо вызывать этот метод чуть позже :) Дерево просто-напросто не успевает сформировать соответствующий элемент UI. И возвращает null.
У авторов с этим проблем нет, потому что они предварительно выполняют запрос к сервису (см. последнюю строчку в конструкторе класса MapService), и этот запрос выполняется гораздо дольше, чем обновляется дерево.

-----------
Можно отслеживать события дерева/генератора.
Но проще, наверное, добавить в MapControl DispatcherTimer и запускать его в случае OpenStreetMapLayer. Таймер просто подождёт сколько-то времени (например, секунду) и затем поднимет событие LoadCompleted.

Имя (ID), чекбокс и слайдер работать должны.
А вот можно ли вытаскивать из OSM легенду, иерархию слоёв или хотя бы описание сервиса - понятия не имею.
Для Bing Maps aka VE вообще ключ какой-то нужен.

    
0 голосов
ответил 22 Ноя, 10 от TDenis (42,620 баллов)
Как-то так наверное:


private void loadTree()
{
    if (tocTree == null) return;
    this.Services.Clear();
    for (int i = this.Map.Layers.Count - 1; i >= 0; i--)
    {
        Layer layer = this.Map.Layers[ i ];
        if (layer is ArcGISDynamicMapServiceLayer || layer is ArcGISTiledMapServiceLayer || layer is OpenStreetMapLayer)
        {
            MapService service = new MapService(this.Map, layer, i, LegendServiceUrl);       
            service.LoadingVisibility = Visibility.Visible;
            service.LoadCompleted += legend_LoadCompleted;
                    
            this.Services.Add(service);
        }
    }
}


----------------------------------

DispatcherTimer _timer;

public MapService(Map map, Layer layer, int id, string legendurl)
    : base(layer.ID, id, layer.Visible)
{           
    base.SliderVisibility = Visibility.Visible;
    base.Color = new SolidColorBrush(Colors.Black);
    if (layer is ArcGISDynamicMapServiceLayer)
    {
        ArcGISDynamicMapServiceLayer dynlayer = layer as ArcGISDynamicMapServiceLayer;
        _url = dynlayer.Url;
        _proxy = dynlayer.ProxyURL;
        _token = dynlayer.Token;
        _isdynamic = true;
        base.Opacity = layer.Opacity;
    }
    else if (layer is ArcGISTiledMapServiceLayer)
    {
        ArcGISTiledMapServiceLayer tilelayer = layer as ArcGISTiledMapServiceLayer;
        _url = tilelayer.Url;
        _proxy = tilelayer.ProxyURL;
        _token = tilelayer.Token;
        _isdynamic = false;
        base.Opacity = layer.Opacity;
    }
    else if (layer is OpenStreetMapLayer)
    {
        OpenStreetMapLayer osmLayer = layer as OpenStreetMapLayer;
        _isdynamic = false;
        base.Opacity = layer.Opacity;
        _map = map;
        _timer = new DispatcherTimer();
        _timer.Tick += new EventHandler(_timer_Tick);
        _timer.Interval = new TimeSpan(0, 0, 1);
        _timer.Start();
        return;
    }

    _map = map;
    _legendurl = legendurl;
    this.SoapURL = _url.Replace("/rest/services/", "/services/");
    Uri uri;
    WebClient client = new WebClient();
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(this.DownloadStringCompleted);
    if (!string.IsNullOrEmpty(_token))
    {
        uri = new Uri(_url + "?" + "token=" + _token + "&" + MapService.JSON);
    }
    else if (!string.IsNullOrEmpty(_proxy))
    {
        uri = new Uri(_proxy + "?" + _url + "?" + MapService.JSON, UriKind.Relative);
    }
    else
    {
        uri = new Uri(_url + "?" + MapService.JSON);
    }
    client.DownloadStringAsync(uri);
}

void _timer_Tick(object sender, EventArgs e)
{
    _timer.Stop();
    this.LoadCompleted(this, new EventArgs());
}



Ну и понятно, что надо не забыть добавить ссылку на сборку ESRI.ArcGIS.Client.Toolkit.DataSources.
0 голосов
ответил 23 Ноя, 10 от Mitrich (13,680 баллов)
Денис! Огромное спасибо!
все великолепно!
если добавить
            else if (layer is Layer)
            {
                Layer otherLayer = layer as Layer;
                _isdynamic = false;
                base.Opacity = layer.Opacity;
                _map = map;
                _timer = new DispatcherTimer();
                _timer.Tick += new EventHandler(_timer_Tick);
                _timer.Interval = new TimeSpan(0, 0, 1);
                _timer.Start();
                return;
            }
то грузит все слои в контрол, и Бинг и графические и проч...
теперь надо придумать фильтр, чтобы часть не грузить :)
для динамических слоев грузится легенда с сервера 9.3.1 !

еще захотелось добавлять в ТОС какой-нибудь текстовый сепаратор , отделять группы слоев, но это уже блажь

что касается Бинга, то с ним все просто:
http://msdn.microsoft.com/en-us/library/ff428642.aspx

надо зарегистрироваться, получить LiveID , а потом получить ключ для своего приложения, указав его адрес. для разработки можно указать просто http:\\localhost\(кажется имяприложения)
на один логин дают 5 ключей. Еще надо выбрать тип приложения(коммерческого за деньги я там не нашел, пришлось выбрать Education)

еще раз спасибо


0 голосов
ответил 23 Ноя, 10 от Mitrich (13,680 баллов)
реализация хотелок

<vtforester:TOC Map="{Binding ElementName=Map}"  MaxHeight="400" MaxWidth="400" 
                                   x:Name="toc"
                                   LegendServiceUrl="../Legend.ashx"
                                   LayersIDs="OpenStreetMap Mapnik,@separator,MyAGServerLayer"
                                   DisplayLegend="False"
                                   Foreground="White"/>

новое свойство LayersIDs. если слой начинается с @ - это сепаратор группы.

в TOC.cs

в #region "Properties"
       /// <summary>
        /// Gets or sets the Layers visible in the TOC.
        /// </summary>
        public String LayersIDs { get; set; }

разбирает список слоев , переданных в LayersIDs
        /// <summary>
        /// Loads the tree.
        /// </summary>
        private void loadTree()
        {
            if (tocTree == null) return;
            this.Services.Clear();
            if (String.IsNullOrEmpty(LayersIDs))
            {
                for (int i = this.Map.Layers.Count - 1; i >= 0; i--)
                {
                    Layer layer = this.Map.Layers;
                    if (layer is Layer)
                    {
                        MapService service = new MapService(this.Map, layer, i, LegendServiceUrl);
                        service.LoadingVisibility = Visibility.Visible;
                        service.LoadCompleted += legend_LoadCompleted;
                        this.Services.Add(service);
                    }

                }
            }
            else
            {
            string[] _layersIDs = LayersIDs.Split(new Char[] {','});
            int k=0;
                foreach (string l in _layersIDs)
                {
                    Layer layer = this.Map.Layersimage;
                    if (layer == null && l.StartsWith("@"))
                        layer = new GraphicsLayer { ID = l, Visible = false };

                    if (layer is Layer)
                    {
                       
                        MapService service = new MapService(this.Map, layer, k, LegendServiceUrl);
                        service.LoadingVisibility = Visibility.Visible;
                        service.LoadCompleted += legend_LoadCompleted;
                        this.Services.Add(service);
                        k = k + 1;
                    }
                }
            }
        }



скрывает чекбокс и слайдер для сепаратора
        private void addHandlers(TreeViewItem tvitem)
        {
            if (tvitem == null) return;
            TocItem item = (TocItem)tvitem.Header;
            item.Color=this.Foreground;
            item._nullcolor=this.Foreground;              
            CheckBox ckb = GetChildObject<CheckBox>(tvitem, "ckbVisibility");
            if (ckb != null)
   &
0 голосов
ответил 23 Ноя, 10 от TDenis (42,620 баллов)
image
0 голосов
ответил 23 Ноя, 10 от Mitrich (13,680 баллов)
вдогонку вопрос:
если я в xaml приписал слою карты x:Name="Название слоя по-русски и понятно", как в коде это самое x:name найти?
т.е. в ТОС хочется писать не ID слоя, а что-то удобоваримое. Может куда-то еще можно "перевод" записать в xaml? куда?


вопрос снят layer.GetValue(NameProperty)
0 голосов
ответил 16 Дек, 10 от Holger (19,360 баллов)
Джентльмены, перенёс пример на ESRI SL 2.1 API. Почему-то периодически начинает вывыливаться в Web developer (у меня Express 2010) конструктор на контроле
<localtoolkit:Navigator x:Name="nav" Map="{Binding ElementName=Map}" HorizontalAlignment="Left" VerticalAlignment="Top" Loaded="nav_Loaded" MapUnit="Meters"/>

Возникло необработаное исключение
System.Reflection.TargetInvocationException
Адресат вызова создал исключение.
   в System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   в System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   в System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   в System.Delegate.DynamicInvokeImpl(Object[] args)
   в System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   в MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

и тд.
System.InvalidOperationException
Обнаружено необработанное исключение при попытке преобразовать текущий проект silverlight для просмотра в рабочей области конструирования. Для выяснения причины этой неполадки попробуйте запустить проект в стандартном браузере с помощью среды выполнения silverlight.
   в Microsoft.Windows.Design.Platform.SilverlightViewProducer.OnUnhandledException(Object sender, ViewUnhandledExceptionEventArgs e)
   в Microsoft.Windows.Design.Platform.SilverlightViewProducer.SilverlightContentHost.MeasureOverride(Size availableSize)
   в System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   в System.Windows.UIElement.Measure(Size availableSize)
   в M
   в System.Windows.UIElement.Measure(Size availableSize)
   в System.Windows.Interop.HwndSource.SetLayoutSize()
   в System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
   в System.Windows.Interop.HwndSource.set_RootVisual(Visual value)
   в MS.Internal.DeferredHwndSource.ProcessQueue(Object sender, EventArgs e)

.....
System.Exception
В результате вызова компонента COM возвращена ошибка в формате HRESULT E_FAIL.
   в MS.Internal.XcpImports.MethodEx(IntPtr ptr, String name, CValue[] cvData)
   в MS.Internal.XcpImports.MethodEx(DependencyObject obj, String name)
   в MS.Internal.XcpImports.UIElement_UpdateLayout(UIElement element)
   в Microsoft.Windows.Design.Platform.SilverlightViewProducer.SilverlightContentHost.MeasureOverride(Size availableSize)

    
0 голосов
ответил 16 Дек, 10 от Mitrich (13,680 баллов)
я тут выложил сборку кажется в 2.1rc. если ее перекомпилировать (заменив ссылки на 2.1) та же ошибка? а из вышеприведенного я ничего не понимаю :(
кстати, в xaml  при включенной легенде студия не показывает дизайн страницы. но вроде работает
0 голосов
ответил 16 Дек, 10 от Holger (19,360 баллов)
У меня всё компилируется, если контрол в XAML закомментировать - то показываются оставшиеся в конструкторе. При переносе на другую машину - тот же трабл. Иногда откр нормально. Неустойчиво всё как то  :(.
0 голосов
ответил 16 Дек, 10 от TDenis (42,620 баллов)
При переносе на другую машину - тот же трабл.

Выложите куда-нибудь глючащий проект, посмотреть.
Добро пожаловать на сайт Вопросов и Ответов, где вы можете задавать вопросы по GIS тематике и получать ответы от других членов сообщества.
...