I was trying out the newly released xamarin shell. Basically, I am trying out how to map the route to a dynamically generated page. I noticed the RegisterRoute has an overload that requires an object of type RouteFactory. I created a demo class for this:
class NavMan : RouteFactory
{
private readonly string title;
public NavMan(string title) : base()
{
this.title = title;
}
public override Element GetOrCreate()
{
return new ContentPage
{
Title = "tit: " + title,
Content = new Label { Text = title }
};
}
}
Now, in the App.cs I create a register a demo route:
Routing.RegisterRoute("batman", new NavMan("I am batman"));
I have tried several variations of setting up the Shell object. I mostly get blunt null pointers and need to guess what to change.
As of now, my App class's constructor has the following code:
var sea = new Shell();
var theItem = new FlyoutItem
{
IsEnabled = true,
Route = "batman"
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
This gives me a blunt null pointer too. All I am trying for now is to display the page of route "batman". Even a flyout or tab isn't mandatory.
Update
While not the aim, I at least got the app opening with the following:
var sea = new Shell();
Routing.RegisterRoute("batman", new NavMan("I am batman"));
var theItem = new ShellContent {
Title = "hello 20",
Route = "batman2",
Content = new ContentPage {
Content = new Button {
Text = "Something shown",
Command = new Command(async () => await Shell.Current.GoToAsync("//batman"))
}
}
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
On clicking the button, it now shows me the following exception and never calls the GetOrCreate function.
System.Exception: <Timeout exceeded getting exception details>
Update 3
Basically, I am looking for a way to bind a route to ShellContent in a way that it simply displays I mention the route property and it displays a page in the route. It doesn't make sense that I need to mention a route AND mention a content template for the page. The route is already mapped to a page.
Related
In my AppShell.xaml.cs page I can easily access the FlyoutItems of the Shell:
accountFlyoutItem.IsEnabled = false;
accountFlyoutItem.IsVisible = false;
However, how do you access these from another page? The only way I found was to try to iterate through the "Shell.Current.FlyoutItems". Is there a simpler way I'm missing?
Shell.Current.CurrentItem is used to get the current selected flyout .
If we want to access specific FlyoutItem after you named it in xaml (<FlyoutItem x:Name="a">) , we can get it from the following two ways
Create a global public variable in AppShell and assign the value in constructor.
public ShellItem AItem;
public AppShell()
{
InitializeComponent();
RegisterRoutes();
BindingContext = this;
AItem = a;
}
//In another page
var item = (Shell.Current as AppShell).AItem;
Create a method to return the item , in this way we don't need to create the public variable .
public ShellItem GetA()
{
return a;
}
//In another page
var item = (Shell.Current as AppShell).GetA();
I am working on an ASP.NET MVC app (ASP.NET NOT ASP.NET Core).
When a View is rendered, the user can click on some buttons on the page to collapse or show divs associated with each button. The div changes its class depending on whether it is collapsed or shown. I am using bootstrap attributes for this, and it works fine.
Now I have a "Save" button on the page. When the user clicks on this button, I need to retrieve the ids and classes of the divs, and pass them TO the Controller (in an array/collection/dictionary whatever).
Is there a way/method in ASP.NET to send to the Controller the attributes (ids, classes, etc) of the DOM elements on the client's browser ?
Thanks
If you want to send some attributes of DOM to Controller, I have a way.
HTML:
<div id="demo-1" class="chosendiv other-className" data-code ="abc">Lorem Ipsum</div>
<div id="demo-2" class="chosendiv other-className" data-code ="xyz">Lorem Ipsum</div>
<div id="demo-3" class="other-className" data-code ="mnt">Lorem Ipsum</div>
<button id="btn-save" onclick="Save()">SAVE</button>
Javascript
<script>
function Save(){
var cds = document.getElementsByClassName('chosendiv');
var finder = [];
if(cds != null){
for(i = 0; i< cds.length; i++){
finder.push({
ID: cds[i].getAttribute('id'),
ClassName: cds[i].getAttribute('class'),
Code: cds[i].getAttribute('data-code')
})
}
}
//
// Send finder to Controller. You can use Ajax...
// A simple ajax call:
//
$.ajax({
url: '/Home/YourAction',
type: 'GET', //<---- you can use POST method.
data:{
myDiv: JSON.stringify(finder)
},
success: function(response){
// Your code
}
})
}
</script>
Your Controller
public class HomeController: Controller
{
public HomeController(){}
[HttpGet]
public void YourAction(string myDiv)
{
//A lot of ways for converting string to Object, such as: creating new class for model, ...
// I use Dictionary Class
List<Dictionary<string, string>> temp = new List<Dictionary<string, string>>();
if(!string.IsNullOrEmpty(myDiv))
{
try
{
temp = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(myDiv);
}
catch { // Do something if it catches error. }
}
// Get a element (at index) from temp if temp.Count()>0
// var id = temp.ElementAt(index)["ID"];
// var className = temp.ElementAt(index)["ClassName"];
// var code = temp.ElementAt(index)["Code"];
//
//Your code
//
}
//......
}
It would be great if my answer could solve your problem.
Based on the answer provided by #Gia Khang
I made few changes in order to avoid the issue of the length of the URL exceeding the maximum limit.
Instead of adding the element's classes to an array using JS, I add them to a string :
function Save() {
var cds = document.getElementsByClassName('chosendiv');
// I use as string instead of an array
var finder = "";
if(cds != null){
for(i = 0; i< cds.length; i++){
finder = finder + "id=" + cds[i].getAttribute('id') + "class=" + cds[i].getAttribute('class') + "data-code=" +cds[i].getAttribute('data-code')
}
}
// Send finder to Controller. You can use Ajax...
// A simple ajax call:
var myURL = "/{Controller}/{Action}"
$.ajax({
url: myURL,
type: "POST",
data: { ids:finder },
success: function (response) {
}
})
}
In the Controller Action I add a parameter named "ids" (this must be the same name as the identifier of the data object in the post request)and I extract the id, class, and data value from the ids string by a method in one of my Models classes (sorry I work with VB.NET not with C# and it will take me a lot of time to convert the code to C#. I use the Split method in VB to split the ids string several times: a first one by using "id=" as delimiter, then spiting each element in the resulting array by the second delimiter "class=", etc. I add the resulting elements to a collection)
The Controller Action looks like this:
public class HomeController: Controller
{
public HomeController(){}
[HttpPost]
public void YourAction(string ids)
{
Models.myClass.splitStringMethod(ids)
Return View()
}
}
I'm trying to use certain icon instead of default Xamarin.Forms pins on Map.
So I created CustomisePin class that inheritance Pin.
using Xamarin.Forms.Maps;
namespace agroNet.Tools
{
public class CustomPin : Pin
{
public string PinIcon { get; set; }
}
}
Here is what I tried in my ViewModel
private Map _map;
public IrrigNetViewModel(Map map)
{
dialog = UserDialogs.Instance.Loading(AppResource.LocalizationResource.Loading);
TabTappedCommand = new Command((tabName) => OnTapClicked(tabName.ToString()));
HideListOnTapCommand = new Command(HideListOnTap);
_map = map;
GetData();
}
And here is method wor set pins on positions.
public void LoadMapTab()
{
//var irrigNetPins = new List<CustomPin>();
foreach (var item in IrrigNetCollection)
{
//var pins = new CustomPin
//{
// Label = item.StationName,
// Position = new Position(item.StationLatitude, item.StationLongitude),
// PinIcon = "satellite.png"
//};
_map.Pins.Add(new CustomPin
{
Label = item.StationName,
Position = new Position(item.StationLatitude, item.StationLongitude),
//PinIcon = P
});
_map.MoveToRegion(
MapSpan.FromCenterAndRadius(new Position(item.StationLatitude, item.StationLongitude),
Distance.FromKilometers(30)));
//irrigNetPins.Add(pins);
}
//return irrigNetPins;
}
In LoadMapTab under the comment lines is what I have tried to set pin icon.
And here is part of View if it's important because of Binding Context.
public partial class IrrigNetPage : ContentPage
{
public IrrigNetPage()
{
InitializeComponent();
BindingContext = new IrrigNetViewModel(MainMap);
}
}
I find some examples on Google, like:
https://github.com/raechten/BindableMapTest
https://github.com/paulpatarinski/ShouldIWashMyCar
For some reason I can't event run them, but still I tried to use code and no matter what I have Pin are alwas default, or not even show.
What is simple way to set certain icon for pin and is it posiple to render it just with some path to the icon without rendering map also (Because I saw many create costumise map for customised pins).
Also If it's important for someon I'm using MVVM patern.
The official Xamarin sample and documentation is outdated. I modified the official Custom Pin sample in order to use custom pins in iOS and Android. The project is hosted on GitHub.
An issue is reported about the official sample over here.
I am using Knockout 3.2 and the new component system. I am trying to have components that include sub-components.
Home Page (component - with HomePageViewModel)
NewsFeed1 (component with HomePageViewModel.NewsFeedViewModel1)
NewsFeed2 (component with HomePageViewModel.NewsFeedViewModel2)
HomePageViewModel
var viewModel = (function () {
function viewModel() {
this.message = ko.observable("Welcome to DKT!");
this.newsFeedViewModel = new gr.viewModel();
this.newsFeedViewModel2 = new gr.viewModel();
this.newsFeedViewModel.message("Message 1");
this.newsFeedViewModel2.message("Message 2");
}
return viewModel;
})();
NewsFeedViewModel
var viewModel = (function () {
function viewModel() {
this.message = ko.observable("This is the profile!");
}
return viewModel;
})();
As you can see the HomePageViewModel contains both the NewsFeedViewModel. I now want to be able to use these as the DataContext/BindingContext of my two components but this does not seem to work.
Home.html
<news-feed data-bind="newsFeedViewModel"></news-feed>
<news-feed data-bind="newsFeedViewModel2"></news-feed>
Both these components do not use the ViewModels from the HomePageViewModel but uses a new NewsFeedViewModel. How can I make the datacontext of both these components bind to the viewModels stored in the top component (home)?
Generally, you would want to supply your component with any data via params. For example, with your structure, you could create the component like:
ko.components.register("news-feed", {
viewModel: function (params) {
this.vm = params.vm;
},
template: "<h2>News Feed</h2><div data-bind=\"text: vm.message\"></div>"
});
Then, you would define the elements like:
<news-feed params="vm: newsFeedViewModel"></news-feed>
<news-feed params="vm: newsFeedViewModel2"></news-feed>
You could choose to pass the message in directly for each and/or choose whatever names make sense for your params (rather than vm).
Sample: http://jsfiddle.net/rniemeyer/fssXE/
I have this page (item) in my Sitecore website that is viewed from a Facebook page tab. It's a rather simple page, but I have an issue with Sitecore giving me the wrong language on first load, then subsequent loads are ok.
This item runs through this controller:
//
// GET: /Portfolio/
public override ActionResult Index()
{
var appId = "*****";
var appSecret = "*****";
// Defaults to en
var requestLanguage = "en";
// Get language from FB
if (Request.Form["signed_request"] != null)
{
if (Request.Url.Host.ToLower().Contains("local")) {
appId = "*****";
appSecret = "*****";
}
var fbUser = new Facebook.FacebookClient
{
AppId = appId,
AppSecret = appSecret
};
var parsedSignedRequest = JObject.Parse(fbUser.ParseSignedRequest(Request.Form["signed_request"]).ToString());
if (parsedSignedRequest != null)
{
requestLanguage = parsedSignedRequest["user"]["locale"].ToString().StartsWith("fr") ? "fr-CA" : "en";
} // Else: Request can't be parsed, something is wrong
} // Else: Probably not in FB
// ?l=***** can bypass language setting
if (!string.IsNullOrWhiteSpace(Request.QueryString["l"]))
{
requestLanguage = Request.QueryString["l"];
}
Context.Language = Language.Parse(requestLanguage);
// Views will need this
ViewBag.requestLanguage = requestLanguage;
ViewBag.appId = appId;
return base.Index();
}
When I debug this, it works perfectly. I'm setting Sitecore's Context.Language to that of what the Facebook user uses (I have french and english content).
Now onto views, I have this master layout that basically just (other than boring html markup) places the placeholder:
#Html.Sitecore().Placeholder("fb-body")
Finally, my view rendering looks like this:
#using Sitecore.Globalization
#using Sitecore.Data.Items
#model RenderingModel
#{
// I checked this and the context language here is always correctly set, even on first load (controller did that)
// Sitecore.Context.Language = Language.Parse(ViewBag.requestLanguage);
var all = "All";
var back = "Back";
var projetTitle = "the project";
var servicesTitle = "services";
// Since my language is correctly set, this works fine
if (Language.Current.Name.ToLowerInvariant().Contains("fr"))
{
all = "Tous les projets";
back = "Retour";
projetTitle = "le projet";
servicesTitle = "services";
}
var portfolio = Model.Item.Parent.Children.FirstOrDefault(x => x.TemplateName == "Portfolio");
var wrongLanguage = portfolio.Language;
var wrongLanguage2 = Model.Item.Language;
}
Here when I pull my portfolio node, it's in the wrong language. If I look in Model.Item.Language, I also get the wrong language.
What am I missing here, is there something else I need to tell Sitecore so that he understands my language? This also sort of looks like a caching issue... Where do I look to solve this?
Thanks!
I am not sure what base controller you have set up, but it seems you make use of the PageContext.Current.PageView either by returning it yourself or inheriting from the SitecoreController. I bumped into some issues trying to reproduce the problem so I ended up with this:
public class TestController : Controller
{
public ActionResult Index()
{
Sitecore.Context.SetLanguage(Language.Parse("nl-NL"), true);
var view = (PageContext.Current.PageView) as RenderingView;
var renderer = view.Rendering.Renderer as ViewRenderer;
renderer.ViewPath = "/Views/Test/Index.cshtml";
return View(view);
}
}
And in my view I render:
#model Sitecore.Mvc.Presentation.RenderingModel
Context language is: #Sitecore.Context.Language<br/>
Model language is: #Model.Item.Language
First time result:
Context language is: nl-NL
Model language is: en
Second time result:
Context language is: nl-NL
Model language is: nl-NL
The problem is that the language resolver first sets the language to the default (being 'en') and when it gets into your controller action it has already fetched the Item with 'en'. So your language adjustment comes in too late. The second time it is loaded in correct language because the adjusted language was persisted in a cookie.
A proper place to set the language would be the LanguageResolver. You can override/extend the LanguageResolver and replace it in the httpRequestBegin pipeline. That way you will also prevent localized content being cached with the default language in the cachekey.