Xamarin Forms Maps - Customization of Tiles - maps

I'm coming today to speak about the Tiles of the map !
For a personnal project and the project of my company, I need to customize a lot my Xamarin.Forms.Maps. I found this tutorial Custom Map Tiles in Xamarin.Forms which speak only about Android and iOS (one more time..) However, I would love to know how does it works for WinPhone 8.1 and/or UWP.
Also, Because it uses Mapbox, I would like to know if this project is really make to be available for a long time? (I ask only for those who knows something about this project, because I don't know juste by reading).
As I know, some nuget package about it exists but without making what I really want (I want to custom the tiles over each plateform)
If you have a website about it or if you already did it, can you give me some directions or any help please? Thank !
EDIT 1
I found this code for the UWP renderer, but it doesn't change the map tiles..
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapTileProject.UWP.Renderer
{
public class CustomMapRenderer : MapRenderer
{
CustomMap customMap;
MapControl mapControl;
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
customMap = e.NewElement as CustomMap;
mapControl = Control as MapControl;
UpdateTiles();
}
}
private void UpdateTiles()
{
Debug.WriteLine("BEGINING !");
HttpMapTileDataSource dataSource = new HttpMapTileDataSource(customMap.MapTileTemplate);
MapTileSource tileSource = new MapTileSource(dataSource);
mapControl.TileSources.Add(tileSource);
Debug.WriteLine("END !");
}
}
}

found this code for the UWP renderer, but it doesn't change the map tiles
If you check the web request using Fiddler, you will see the request API URL was incorrect:
Ref Overlay tiles from a tile source
The standard HttpMapTileDataSource in UWP should be like this:
http://www.web service name.com/z={zoomlevel}&x={x}&y={y}
It includes three replaceable parameters for the X and Y coordinates and the zoom level: {zoomlevel}, {x}, {y}
So we need to convert your MapTileTemplate string first:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapTileProject.UWP.Renderers
{
public class CustomMapRenderer : MapRenderer
{
CustomMap customMap;
MapControl mapControl;
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
customMap = e.NewElement as CustomMap;
mapControl = Control as MapControl;
UpdateTiles();
}
}
/// <summary>
/// Convert MapTileTemplate string to fit UWP HttpMapTileDataSource
/// </summary>
/// <param name="mapTileTemplate"></param>
/// <returns></returns>
private string GetTileTemplateForUWP(string mapTileTemplate)
{
return mapTileTemplate.Replace("{z}", "{zoomlevel}");
}
private void UpdateTiles()
{
Debug.WriteLine("BEGINING !");
HttpMapTileDataSource dataSource = new HttpMapTileDataSource(GetTileTemplateForUWP(customMap.MapTileTemplate));
MapTileSource tileSource = new MapTileSource(dataSource);
mapControl.TileSources.Add(tileSource);
Debug.WriteLine("END !");
}
}
}
Screenshot:

Related

How to execute a Revit IExternalCommand from a WPF button?

I am in need of help.
I have created a dockable WPF within Revit.
It is working well and I can 'show' & ;hide' from push buttons.
My aim is to create buttons within the WPF that run custom commands.I dont need to interact or show any information within the WPF, its purely just acting as a push button but in the WPF instead of a ribbon.
The commands currently work and can be executed via the Add-In Manager.
Below is the command I am trying to run:
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;
namespace Adams.Commands
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class PrecastDisallowJoin : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
var uiApplication = commandData.Application;
var application = uiApplication.Application;
var uiDocument = uiApplication.ActiveUIDocument;
var document = uiDocument.Document;
// Prompt the user to select some walls
var references = uiDocument.Selection
.PickObjects(
ObjectType.Element,
new WallSelectionFilter(),
"Please select walls");
var components = references.Select(r => document.GetElement(r)).ToList();
// Start a transaction
using (Transaction t = new Transaction(document, "Change Wall Join Behavior"))
{
t.Start();
// Loop through the selected walls and change their join behavior
foreach (Reference reference in references)
{
Wall wall = document.GetElement(reference) as Wall;
WallUtils.DisallowWallJoinAtEnd(wall, 0);
WallUtils.DisallowWallJoinAtEnd(wall, 1);
}
// Commit the transaction
t.Commit();
}
return Result.Succeeded;
}
public class WallSelectionFilter : ISelectionFilter
{
public bool AllowElement(Element elem)
{
//return elem is FamilyInstance;
return elem.Name.Contains("Precast");
}
public bool AllowReference(Reference reference, XYZ position)
{
return true;
}
}
}
}
My XAML.cs looks like this:
using Autodesk.Revit.UI;
using System.Windows.Controls;
using Adams.Commands;
using System.Windows;
namespace Adams.ui
{
public partial class Customers : UserControl
{
public UIDocument uIDocument { get; }
public ExternalCommandData commandData { get; }
public Customers(UIDocument uIDocument )
{
InitializeComponent();
}
private void btnStartExcelElementsApp_Click(object sender, RoutedEventArgs e)
{
string message = string.Empty;
PrecastDisallowJoin precastDisallow = new PrecastDisallowJoin();
precastDisallow.Execute(commandData, ref message, null);
}
}
}
Any ideas of what i should be trying?
I'm new to creating add-ins and appreciate any help offered.
If I have missed any critical info please let me know.
Thank you all
When I tried the above it crashes Revit.
Im not sure how to pass the required information in the Execute method in the XAML.
The Revit dockable dialogue and hence your WPF form lives in a modeless context. It does not execute within a valid Revit API context. A valid Revit API context is only provided by Revit itself, within the event handlers called by Revit when specific events are raised. For instance, clicking a button to launch an add-in external command raises the IExternalCommand.Execute event.
The Building Coder shares a long list of articles on Idling and External Events for Modeless Access and Driving Revit from Outside
explaining how to gain access to a valid Revit API context from a modeless state.
You can address your task by using an external event:
Idling Enhancements and External Events
External Command Lister and Adding Ribbon Commands
External Event and 10 Year Forum Anniversary
Implementing the TrackChangesCloud External Event
Vipassana and Idling versus External Events
The question has also been discussed many times in the Revit API discussion forum, so you can check there for threads including WPF, dockable and external event.
You can use IExternalEventHandler:
public class MyExternalEvent : IExternalEventHandler
{
public void Execute(UIApplication app)
{
//do your revit related stuff here
}
public string GetName()
{
return "xxx";
}
}
Create external event:
ExternalEvent myExEvent= ExternalEvent.Create(new MyExternalEvent());
In order to effectively use the above you will have to hold reference to "myExEvent" in some ViewModelClass then you will be able to raise this event inside your xaml.cs:
ViewModelClass.TheEvent = myExEvent;
ViewModelClass.TheEvent.Raise();
EDIT: What you were trying to do is unfortunately not acceptable with revit API. WPF window displayed as dockpanel does not have access to valid revit api context. IExternalEventHandler gives you the possibility to somehow link dockpanel user interface with revit api.

Using Prism for building a Plugin (.dll)

I am developing a plugin to be used for a software I use. The software plugin is attached to the software by attaching the .dll produced from my code. Therefore, the software's documentation requires you to have a certain class (call it CPlugin) as the plugins entry point.
All the tutorials I am reading with Prism is where you initiate your project as a WPF Application. This way your project will have App.xaml and App.cs files where you start to implement your Prism framework. Compiling the code also (for a WPF application) will produce a .exe not a .dll.
The way I have my plugin setup is I start my project with a C# class. I would then create my CPlugin class and initiate all my variables and then display my MainView which creates my ViewModel and take it from there. There is no App.xaml or App.cs. I am not sure how to use Prism with the constraints I have.
This is the software I am developing the plugin for: https://www.csiamerica.com/products/etabs
Upon installation in the install directory; the API helpfile can be found which explains how to develop or initiate a plugin. Here is a sample of the relevant information:
In your plugin, all functionality must be implemented in a class called cPlugin.
Class cPlugin must contain a subroutine cPlugin.Main that has two
reference arguments of types ETABSv1.cSapModel and
ETABSv1.cPluginCallback
Also
Adding a .NET Plugin
The process for adding a .NET plugin is much simpler. In the External Plugin Data form, the user should simply browse for and select the plugin .NET DLL and click the "Add" button
Here is some sample code for a plugin that displays an empty window:
Create a C# Class Library file (.NET Framework), reference the API as one of my references.
CPlugin.cs:
using Combonito.Views; // Access Views (open window)
using CSiAPIv1; //to Access ETABS/SAP2000 API
using System;
using static Globals;
namespace Combonito
{
// cPlugin has to be implemented in ETABS Plugins, it has to contain two functions Main() and Info()
public class cPlugin
{
private MainView _MyForm;
//Entry point of plugin - has to exist with this exact signature
// must call Finish() when plugin is closed!
public void Main(ref cSapModel _SapModel, ref cPluginCallback _ISapPlugin)
{
ParentPluginObject = this;
SapModel = _SapModel;
ISapPlugin = _ISapPlugin;
try
{
_MyForm = new MainView(); //Create MainView
_MyForm.ShowDialog(); // Display window
}
catch (Exception ex)
{
try
{
ISapPlugin.Finish(1);
Console.WriteLine(ex);
}
catch (Exception ex1)
{
Console.WriteLine(ex1);
throw;
}
}
}
// return value should be 0 if successful
public int Info(ref string txt)
{
try
{
txt = "Plugin is written by Moustafa El-sawy (mk.elsawy#live.com)";
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
return 0;
}
//Deconstructor to clean up
~cPlugin()
{
Console.WriteLine(GC.GetGeneration(0));
}
}
}
I then have an empty window MainView and A viewmodel MainWindowViewModel.
Edit: Here is the boiler initial to start any plugin similar to what I showed above but has more explanation https://kinson.io/post/etabs-plugin-quickstart/
Your starting point should be Bootstrapper
First you need to install:
Prism.Unity
Prism.Wpf
Need to create your bootstrapper based on
https://github.com/PrismLibrary/Prism/blob/master/src/Wpf/Prism.Wpf/PrismBootstrapperBase.cs
Override the virtual method to create Shell (which is your main view that contains regions
Override the virtual method to configure your container.
Finally register your views and viewmodels
P.S.: Consider using an interface for each registered type e.g. IShellViewModel
using Prism;
using Prism.Ioc;
using Prism.Unity;
using System.Windows;
namespace PrismTest
{
public class Bootstrapper : PrismBootstrapperBase
{
protected override IContainerExtension CreateContainerExtension()
{
return new UnityContainerExtension();
}
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IShellViewModel, ShellViewModel>();
}
}
}
Call your Bootstrapper from your plugin :
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
Your View (Shell)
<Window
x:Class="PrismTest.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PrismTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Shell"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<TextBox Text="{Binding MyText}" />
</Grid>
</Window>
In Shell.xaml.cs: Use your view model. This can be also automatically injected
public partial class Shell : Window
{
private readonly IUnityContainer _container;
public Shell(IUnityContainer container)
{
InitializeComponent();
_container = container;
this.DataContext = _container.Resolve<IShellViewModel>();
}
}
Your ViewModel
public class ShellViewModel : BindableBase, IShellViewModel
{
private string m_MyText = "Shell ViewModel Text";
public string MyText
{
get { return m_MyText; }
set { SetProperty(ref m_MyText, value); }
}
}
Your interface:
internal interface IShellViewModel
{
string MyText { get; set; }
}
Resulting view;

Can I subscribe to an event somewhere in wpf to listen for showDialog or new windows?

I want to observe certain information about all windows in my app. I have a window that shows (via Observables) lots of information, but I can't find anything short of polling that will let me know all the windows (and also all open windows in my app).
Is there somewhere I can subscribe that would be a good hook for any windows getting shown (.Show() or .ShowDialog()) ?
You have to add references to UIAutomationClient.dll, UIAutomationTypes.dll.
On my machine, they are in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.
CommonWindow is used as your common window, which keeps changing its owner. I create modal windows, which can further create more modal windows, and latest one owns CommonWindow, I also set latest window as DataContext of CommonWindow.
using System;
using System.Windows;
using System.Windows.Automation;
using System.Threading;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
CommonWindow _commonWin = new CommonWindow();
private void Application_Startup(object sender, StartupEventArgs e)
{
Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement,
TreeScope.Children,
(s, e1) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
var windows = Application.Current.Windows;
Window w = windows[windows.Count - 1];
w.Closing += w_Closing;
// First modal window is shown, so count now is 2 (>1)
if (App.Current.Windows.Count > 1)
{
_commonWin.Owner = w;
_commonWin.DataContext = w;
_commonWin.Show();
}
});
});
}
void w_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// Comparison to 3 is important as there would be 3 windows when first modal window is shown
if (Application.Current.Windows.Count >= 3)
{
_commonWin.Owner = Application.Current.Windows[App.Current.Windows.Count - 2];
_commonWin.DataContext = Application.Current.Windows[App.Current.Windows.Count - 2];
}
else
{
_commonWin.Close();
}
}
}
}
CommonWindow is just a Window as CommonWindow.xaml just like normal xaml file with its code-behind.

WPF Creating a Command Library

New to WPF/XAML and looking to build a Command library for my application. Current version of my application is "flat" meaning all the code behind resides in my MainWindow.cs file. I've decided to separate things out into User Controls and all the UI part so far works great but....
I'm having trouble binding my commands to things now that I've done this. To make it simple I created a new WPF project, added my Menu control (Controls\Menu.xaml) and referenced this in my MainWindow.xaml. Now, I've gone ahead and added a CommandLibrary class and can't seem to get anything working. Here is my File -> New command code:
public static class MyAppCommands
{
private static RoutedUICommand _NewItem;
static MyAppCommands()
{
_NewItem = new RoutedUICommand("Create a new project", "NewItem", typeof(MyAppCommands));
}
public static RoutedUICommand NewItem
{
get { return _NewItem; }
}
private string filePath = null;
private bool dataChanged = false;
public static void NewItem_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (dataChanged)
{
string sf = SaveFirst();
if (sf != "Cancel")
{
ClearState();
}
}
else
{
ClearState();
}
}
public static void NewItem_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public static void BindCommandsToWindow(Window window)
{
window.CommandBindings.Add(new CommandBinding(NewItem, NewItem_Executed, NewItem_CanExecute));
}
Where do I put the below in my class as a lot of my command will be using them...?
private string filePath = null;
private bool dataChanged = false;
Many Thanks!
I think you could benefit from setting up some design patterns like MVC or MVVM.
Check out these frameworks:
Caliburn (my favorite)
Prism
You can also follow MVC or MVVM just using a couple small helper classes, check out ariticles around the web, like this one MVVM.
Check this if you want a light-weight framework: MVVM Foundation

Is it possible to show/hide UserControls within a Silverlight XAP file from JavaScript?

I've created a Silverlight project that produces [something].xap file to package a few silverlight UserControls. I would like to manipulate that .xap file through the use of javascript in the browser to show and hide user controls based upon java script events.
Is it possible to do this?
If so any sample could or links to documentation would be appreciated.
Thanks in advance
Kevin
Here's my solution...not sure if it's the "best-practices" way...comments????
In the App class within my Silverlight application I have the following code:
private Page _page = null;
private void Application_Startup(object sender, StartupEventArgs e)
{
_page = new Page();
this.RootVisual = _page;
HtmlPage.RegisterScriptableObject("App", this);
}
Also to the App class I add a [ScriptableMember] to be called from JavaScript
[ScriptableMember]
public void ShowTeamSearch(Guid ctxId, Guid teamId)
{
_page.ShowTeamSearcher(ctxId, teamId);
}
The Page class is the default one that get's created within the Silverlight Control project, it really doesn't have any UI or logic, it's just used to swap in/out the views.
Login oLogin;
TeamSearcher oSearcher;
public Page()
{
InitializeComponent();
oLogin = new Login();
oSearcher = new TeamSearcher();
oLogin.Visibility = Visibility;
this.LayoutRoot.Children.Add(oLogin);
}
Also a method is added to show/hide the views...this could/will probably get more advanced/robust with animations etc...but this shows the basic idea:
public void ShowTeamSearcher(Guid ctxId, Guid teamId)
{
oSearcher.UserTeamId = teamId;
oSearcher.UserContextId = ctxId;
LayoutRoot.Children.Remove(oLogin);
LayoutRoot.Children.Add(oSearcher);
}
Then to invoke this in the JavaScript after assigning the id of oXaml to the instance of the silverlight host.
var slControl = document.getElementById('oXaml');
slControl.Content.App.ShowTeamSearch(sessionId, teamId);
This seems to work and isn't all that bad of a solution, but there might be something better...thoughts?
Here is a my collections of my links for this subject.
Javascript communication to
Silverlight 2.0
Silverlight
interoperability
Silverlight
and JavaScript Interop Basics

Resources