Related
Two part question: On starting up, My Xamarin.Forms app.cs navigates to a login page (ContentPage) with a button. I click the button and I login successfully with this event handler in the app code behind:
async void OnLoginButtonClicked(object sender, EventArgs e)
{
try
{
bool authenticated = await App.AuthenticationProvider.LoginAsync();
if (authenticated)
{
Application.Current.MainPage = new PapMobLandingPage();
}
else
{
await DisplayAlert("Authentication", "Authentication", "OK");
}
}
catch (MsalException ex)
{
if (ex.ErrorCode == "authentication_canceled")
{
await DisplayAlert("Authentication", "Authentication was cancelled by the user.", "OK");
}
else
{
await DisplayAlert("An error has occurred", "Exception message: " + ex.Message, "OK");
}
}
catch (Exception ex)
{
await DisplayAlert("Authentication", "Authentication failed in a big way. Exception: " + ex.Message, "OK");
}
}
}
I then get redirected to Page 2 (PapMobLandingPage) which also has a button. I click that PapMobLandingPage button and get redirected to a TabbedPage with the details of the logged in user flying right in there from my Azure SQL Database, no problems. All great til now! Here is the event handler:
public async void GoToTabbedPage(object sender, EventArgs args)
{
await Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new BotInTabbedPage());
}
Question 1: There is no OnAppearing() in the BotInTabbedPage (TabbedPage) code behind that checks if the user is logged in... there is no acquiring of tokens going on in the TabbedPage initialization, so how does the app know that I'm logged in on this page??
I looked at a similar question from #Creepin and using the link I could not understand the answer to his original question as to whether you have to authenticate each page individually. Link here:
use of AcquireTokenSilentAsync
The reason I ask question 1 is that the tabbed page above is one of six choices the user gets on sign in, so I needed a dashboard page (MVVM based) with six tiles. When I insert this between Page 2 (PapMobLandingPage) and BotInTabbedPage (TabbedPage), and click on the TabbedPage tile, the tapcommand linked to the tile takes me to the BotInTabbedPage (TabbedPage)... BUT...
NO CLIENT DATA! I HAVE BEEN LOGGED OUT!
So to sum up:
Login Page -> PapMobLandingPage -> BotInTabbedPage = Stays authenticated.
Login Page -> PapMobLandingPage -> Dashboard -> BotInTabbedPage = Drops authentication.
If I use a "vanilla" ContentPage with a button:
Login Page -> PapMobLandingPage -> ContentPage -> BotInTabbedPage = Stays authenticated!
So second question is: does anyone have a clue why?
When I say the Dashboard is MVVM based I mean it has a XAML ContentPage, a .cs code behind, with value bindings to a view model which also has a view model template and a template base. This is the code:
Dashboard XAML:
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PapWine;assembly=PapWine"
xmlns:artina="clr-namespace:UXDivers.Artina.Shared;assembly=UXDivers.Artina.Shared"
x:Class="PapWine.DashboardTaskMultipleTilesPage"
BackgroundColor="Black"
Title="{ artina:Translate PageTitleDashboardTaskMultipleTiles }">
<ContentPage.Resources>
<ResourceDictionary>
<artina:BoolMemberTemplateSelector
x:Key="Selector"
MemberName="IsNotification">
<artina:BoolMemberTemplateSelector.TrueDataTemplate>
<DataTemplate>
<local:DashboardAppNotificationItemTemplate
WidthRequest="145"
HeightRequest="145" />
</DataTemplate>
</artina:BoolMemberTemplateSelector.TrueDataTemplate>
<artina:BoolMemberTemplateSelector.FalseDataTemplate>
<DataTemplate>
<local:TaskTilesItemTemplate
ShowBackgroundImage="true"
ShowBackgroundColor="true"
ShowiconColoredCircleBackground="false"
TextColor="{ DynamicResource DashboardIconColor }"
WidthRequest="145"
HeightRequest="145"
/>
</DataTemplate>
</artina:BoolMemberTemplateSelector.FalseDataTemplate>
</artina:BoolMemberTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<ScrollView
Orientation="Both">
<artina:GridOptionsView
WidthRequest="320"
Margin="0"
Padding="10"
ColumnSpacing="10"
RowSpacing="10"
ColumnCount="2"
ItemsSource="{Binding DashboardTaskMultipleTilesList}"
ItemTemplate="{StaticResource Selector}"
/>
</ScrollView>
</ContentPage>
Dashboard XAML.cs
using Microsoft.Identity.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace PapWine
{
public partial class DashboardTaskMultipleTilesPage : ContentPage
{
public DashboardTaskMultipleTilesPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, true);
BindingContext = new DashboardTaskMultipleTilesViewModel();
}
protected override async void OnAppearing()
{
base.OnAppearing();
PublicClientApplication PCA = new PublicClientApplication(Constants.ClientID, Constants.Authority);
IEnumerable<IAccount> accounts = await PCA.GetAccountsAsync();
AuthenticationResult authenticationResult = await PCA.AcquireTokenSilentAsync(Constants.Scopes, GetAccountByPolicy(accounts, Constants.PolicySignUpSignIn), Constants.Authority, false);
JObject user = ParseIdToken(authenticationResult.IdToken);
var currentuseroid = user["oid"]?.ToString();
}
private IAccount GetAccountByPolicy(IEnumerable<IAccount> accounts, string policy)
{
foreach (var account in accounts)
{
string userIdentifier = account.HomeAccountId.ObjectId.Split('.')[0];
if (userIdentifier.EndsWith(policy.ToLower())) return account;
}
return null;
}
JObject ParseIdToken(string idToken)
{
// Get the piece with actual user info
idToken = idToken.Split('.')[1];
idToken = Base64UrlDecode(idToken);
return JObject.Parse(idToken);
}
string Base64UrlDecode(string str)
{
str = str.Replace('-', '+').Replace('_', '/');
str = str.PadRight(str.Length + (4 - str.Length % 4) % 4, '=');
var byteArray = Convert.FromBase64String(str);
var decoded = Encoding.UTF8.GetString(byteArray, 0, byteArray.Count());
return decoded;
}
}
}
Dashboard View Model:
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace PapWine
{
public class DashboardTaskMultipleTilesViewModel : ObservableObject
{
private List<DashboardTaskMultipleTileItem> _dashboardTaskMultipleTilesList;
public DashboardTaskMultipleTilesViewModel()
: base(listenCultureChanges: true)
{
LoadData();
}
public List<DashboardTaskMultipleTileItem> DashboardTaskMultipleTilesList
{
get { return _dashboardTaskMultipleTilesList; }
set { SetProperty(ref _dashboardTaskMultipleTilesList, value); }
}
protected override void OnCultureChanged(CultureInfo culture)
{
LoadData();
}
public async Task LogMeOut()
{
bool loggedOut = await App.AuthenticationProvider.LogoutAsync();
if (loggedOut)
{
Application.Current.MainPage = new LandingPagePreLogin();
}
}
private void LoadData()
{
DashboardTaskMultipleTilesList = new List<DashboardTaskMultipleTileItem>
{
//1 line
new DashboardTaskMultipleTileItem
{
Title = "Log Out",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Lock,
IconColour = "White"
},
new DashboardTaskMultipleTileItem
{
Title = "User Settings",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Gear,
IconColour = "White"
},
//2 line
new DashboardTaskMultipleTileItem
{
Title = "User Info",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.User,
Badge = 12,
IconColour = "White"
},
new DashboardTaskMultipleTileItem
{
Title = "Papillon Shop",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeWeb511Font.store,
Badge = 2,
IconColour = "White"
},
//3 line
new DashboardTaskMultipleTileItem
{
Title = "Check Bottles In",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Book,
IconColour = "White"
},
new DashboardTaskMultipleTileItem
{
Title = "Lay Bottles Down",
Body = "",
Avatar = "",
BackgroundColor = "transparent",
ShowBackgroundColor = false,
IsNotification = false,
BackgroundImage = "Tiles/DarkBlackTile.jpg",
Icon = FontAwesomeFont.Bed,
Badge = 2,
IconColour = "White"
},
};
}
}
public class DashboardTaskMultipleTileItem
{
public string Title { get; set; }
public string Body { get; set; }
public string Avatar { get; set; }
public string BackgroundColor { get; set; }
public string BackgroundImage { get; set; }
public bool ShowBackgroundColor { get; set; }
public bool IsNotification { get; set; }
public string Icon { get; set; }
public int Badge { get; set; }
public string NavigPage { get; set; }
private Xamarin.Forms.Command _tapCommand;
public Xamarin.Forms.Command TapCommand
{
get
{
if (_tapCommand == null)
{
switch (this.Title) {
case "Log Out":
App.AuthenticationProvider.LogoutAsync();
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new LogoutSuccessPage()));
break;
case "User Settings":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new UserSettingsPage()));
break;
case "User Info":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new UserProfilePage()));
break;
case "Papillon Shop":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new ProductFamilyMultipleTilesPage()));
break;
case "Check Bottles In":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage = new SignBottlesIn());
break;
case "Check Bottles Out":
_tapCommand = new Xamarin.Forms.Command(() =>
Xamarin.Forms.Application.Current.MainPage = new SignBottlesIn());
break;
}
}
return _tapCommand;
}
}
}
}
Tiles Item Template:
using Xamarin.Forms;
namespace PapWine
{
public partial class TaskTilesItemTemplate : TaskTilesItemTemplateBase
{
public TaskTilesItemTemplate()
{
InitializeComponent();
}
private async void OnTileTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null)
return;
var content = e.Item as DashboardTaskMultipleTileItem;
//await Navigation.PushAsync(new LayBottlesDown()); //pass content if you want to pass the clicked item object to another page
}
}
}
Tiles Item Template Base:
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace PapWine
{
public class TaskTilesItemTemplateBase : ContentView
{
public uint animationDuration = 250;
public bool _processingTag = false;
public static readonly BindableProperty ShowBackgroundImageProperty =
BindableProperty.Create(
nameof(ShowBackgroundImage),
typeof(bool),
typeof(TaskTilesItemTemplate),
true,
defaultBindingMode: BindingMode.OneWay
);
public bool ShowBackgroundImage
{
get { return (bool)GetValue(ShowBackgroundImageProperty); }
set { SetValue(ShowBackgroundImageProperty, value); }
}
public static readonly BindableProperty ShowBackgroundColorProperty =
BindableProperty.Create (
nameof( ShowBackgroundColor ),
typeof ( bool ),
typeof ( TaskTilesItemTemplate ),
false,
defaultBindingMode : BindingMode.OneWay
);
public bool ShowBackgroundColor {
get { return ( bool )GetValue( ShowBackgroundColorProperty ); }
set { SetValue ( ShowBackgroundColorProperty, value ); }
}
public static readonly BindableProperty ShowiconColoredCircleBackgroundProperty =
BindableProperty.Create (
nameof( ShowiconColoredCircleBackground ),
typeof ( bool ),
typeof (TaskTilesItemTemplate),
true,
defaultBindingMode : BindingMode.OneWay
);
public bool ShowiconColoredCircleBackground {
get { return ( bool )GetValue( ShowiconColoredCircleBackgroundProperty ); }
set { SetValue ( ShowiconColoredCircleBackgroundProperty, value ); }
}
public static readonly BindableProperty TextColorProperty =
BindableProperty.Create (
nameof( TextColor ),
typeof ( Color ),
typeof (TaskTilesItemTemplate),
defaultValue : Color.White,
defaultBindingMode : BindingMode.OneWay
);
public Color TextColor {
get { return ( Color )GetValue( TextColorProperty ); }
set { SetValue ( TextColorProperty, value ); }
}
//added start
public Color IconColour
{
get { return (Color)GetValue(TextColorProperty); }
set { SetValue(TextColorProperty, value); }
}
//added end
public async void OnWidgetTapped(object sender, EventArgs e)
{
if (_processingTag)
{
return;
}
_processingTag = true;
try{
await AnimateItem (this, animationDuration );
await SamplesListFromCategoryPage.NavigateToCategory ((SampleCategory)BindingContext, Navigation);
}finally{
_processingTag = false;
}
}
private async Task AnimateItem(View uiElement, uint duration ){
var originalOpacity = uiElement.Opacity;
await uiElement.FadeTo(.5, duration/2, Easing.CubicIn);
await uiElement.FadeTo(originalOpacity, duration/2, Easing.CubicIn);
}
}
}
LoginAsync looks like this:
public async Task<bool> LoginAsync(bool useSilent = false)
{
bool success = false;
//AuthenticationResult authResult = null;
try
{
AuthenticationResult authenticationResult;
if (useSilent)
{
authenticationResult = await ADB2CClient.AcquireTokenSilentAsync(
Constants.Scopes,
GetAccountByPolicy(await ADB2CClient.GetAccountsAsync(), Constants.PolicySignUpSignIn),
Constants.Authority,
false);
UpdateUserInfo(authenticationResult);
}
else
{
authenticationResult = await ADB2CClient.AcquireTokenAsync(
Constants.Scopes,
GetAccountByPolicy(await ADB2CClient.GetAccountsAsync(), Constants.PolicySignUpSignIn),
App.UiParent);
}
if (User == null)
{
var payload = new JObject();
if (authenticationResult != null && !string.IsNullOrWhiteSpace(authenticationResult.IdToken))
{
payload["access_token"] = authenticationResult.IdToken;
}
User = await TodoItemManager.DefaultManager.CurrentClient.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload);
success = true;
}
}
How do I programmatically add items to the ActionBar? I've been trying to play around with this code below but the action items never update.
public setActionBarItems(actionBar: ActionBar) {
let tab = TabFactory.getTab(this.currentTabIndex);
let actionItem = new ActionItem();
actionItem.set("ios.systemIcon", "12");
actionItem.set("ios.position", "right");
actionItem.set("android.systemIcon", "ic_menu_search");
actionItem.set("android.position", "right");
actionBar.actionItems.addItem(actionItem);
// for (let actionItem of tab.actionItems) {
// actionBar.actionItems.addItem(actionItem);
// }
}
Do I perhaps need to tell the view somehow to update itself? I also tried setting actionItems="{{actionBarItems}}" on the ActionBar itself, but that throws a warning that the property is read-only.
Not that proud of it right now but here is a possible solution. Will probably refactor it in the future.
view model
export class MainViewModel extends Observable {
public isArticlesListTabVisible : boolean;
public isArchiveTabVisible : boolean;
public isAccountTabVisible : boolean;
public items: Array<BottomBarItem> = [
new BottomBarItem(0, "Archive", "ic_archive_black", "#FF303030"),
new BottomBarItem(1, "My List", "ic_list_black", "#FF303030"),
new BottomBarItem(2, "Account", "ic_account_circle_black", "#FF303030")
];
constructor() {
super();
}
get title() : string {
return "My List";
}
public setActionBarTitle(tab: ITab) {
this.notifyPropertyChange("title", tab.title);
}
public setActionBarItems(currentTab: ITab) {
if (currentTab instanceof ArticlesListTab) {
this.isArticlesListTabVisible = true;
this.isAccountTabVisible = false;
this.isArchiveTabVisible = false;
}
else if (currentTab instanceof AccountTab) {
this.isAccountTabVisible = true;
this.isArticlesListTabVisible = false;
this.isArchiveTabVisible = false;
}
else {
this.isArchiveTabVisible = true;
this.isArticlesListTabVisible = false;
this.isAccountTabVisible = false;
}
this.notifyPropertyChange("isArticlesTabVisible", this.isArticlesListTabVisible);
this.notifyPropertyChange("isAccountTabVisible", this.isAccountTabVisible);
this.notifyPropertyChange("isArchiveTabVisible", this.isArchiveTabVisible);
}
}
xml file
<ActionBar title="{{ title }}" class="action-bar" id="mainActionBar">
<ActionItem tap="{{onSearch}}"
ios.systemIcon="12" ios.position="right"
android.systemIcon="ic_menu_search" android.position="right" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Newest" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Oldest" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Most Progress" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Least Progress" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
</ActionBar>
code behind
export function onBottomBarLoaded(args: EventData) {
this._bottomBar = args.object as BottomBar;
this._bottomBar.selectItem(1);
this._bottomBar.on('tabSelected', (args) => {
switchBottomBarTab(args.newIndex, args.oldIndex);
let currentTab = TabFactory.getTab(args.newIndex);
_model.setActionBarTitle(currentTab);
_model.setActionBarItems(currentTab);
});
}
I have a List-View in the content page, List view Items are picked from the SQLite. I want to refresh the page periodically so that I can able to show the latest items inserted in the sql lite.
1. When the first time I added record status of that record is "queued"in local db, List Item will be displayed and status of that Item will be shown as "[EmployeeNo] it is queued After 5 minutes it will be synced".
2.After 5 minutes,All local db [Sqlite] will be synced with the actual sql server, Then status of that record will be updated to "completed" in local db,Then status I want to show "[EmployeeNo] it is completed" in list view automatically.
Use an ObservableCollection<T> as your ItemSource - it will automatically update the UI whenever items are added or removed from it
StartTimer
Device.StartTimer (new TimeSpan (0, 0, 10), () => {
// do something every 10 seconds
return true; // runs again, or false to stop
});
public class EmployeeListPage : ContentPage
{
ListView listView;
public EmployeeListPage()
{
Title = "Todo";
StartTimer();
var toolbarItem = new ToolbarItem
{
Text = "+",
Icon = Device.OnPlatform(null, "plus.png", "plus.png")
};
toolbarItem.Clicked += async (sender, e) =>
{
await Navigation.PushAsync(new EmployeeItemPage
{
BindingContext = new Employee()
});
};
ToolbarItems.Add(toolbarItem);
listView = new ListView
{
Margin = new Thickness(20),
ItemTemplate = new DataTemplate(() =>
{
var label = new Label
{
VerticalTextAlignment = TextAlignment.Center,
HorizontalOptions = LayoutOptions.StartAndExpand
};
label.SetBinding(Label.TextProperty, "EmployeeName");
var labelText = new Label
{
VerticalTextAlignment = TextAlignment.Center,
HorizontalOptions = LayoutOptions.StartAndExpand
};
label.SetBinding(Label.TextProperty, "EmpStatusDisplayText");
var tick = new Image
{
Source = ImageSource.FromFile("check.png"),
HorizontalOptions = LayoutOptions.End
};
tick.SetBinding(VisualElement.IsVisibleProperty, "IsActive");
var stackLayout = new StackLayout
{
Margin = new Thickness(20, 0, 0, 0),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { label, tick }
};
return new ViewCell { View = stackLayout };
})
};
listView.ItemSelected += async (sender, e) =>
{
EmployeeDatabindingDto dto = (e.SelectedItem as EmployeeDatabindingDto);
Employee emp = new Employee {EmployeeID=dto.EmployeeID,EmployeeName=dto.EmployeeName,Salary=dto.Salary,IsActive=dto.IsActive };
Debug.WriteLine("Employee ResumeAt Id = " + emp.EmployeeID);
await Navigation.PushAsync(new EmployeeItemPage
{
BindingContext = emp
});
};
Content = listView;
}
protected override async void OnAppearing()
{
base.OnAppearing();
List<Employee> employees = await App.EmpDatabase.GetItemsAsync();
List<EmployeeDatabindingDto> listBindingDto = await MapEmpWithEmpBindingDto(employees);
listView.ItemsSource = listBindingDto;
}
public async Task<List<EmployeeDatabindingDto>> MapEmpWithEmpBindingDto(List<Employee> employees)
{
List<EmployeeDatabindingDto> bindEmployees = new List<EmployeeDatabindingDto>();
foreach (var employee in employees)
{
string displaysText = "";
string displayDate = "";
displayDate = employee.IsActive == false ? employee.Createddate.ToString() : employee.Modifieddate.ToString();
displaysText = employee.IsActive == false ? string.Format("{0} {1}", "is in queued on", displayDate) : string.Format("{0} {1} ", "is submitted on", displayDate);
bindEmployees.Add(new EmployeeDatabindingDto
{
EmployeeID = employee.EmployeeID
,
EmployeeName = employee.EmployeeName
,
Salary = employee.Salary
,
Createddate = employee.Createddate
,
IsActive = employee.IsActive
,
EmpStatusDisplayText = string.Format("{0} {1}", employee.EmployeeName, displaysText)
});
}
return bindEmployees;
}
private void StartTimer()
{
Device.StartTimer(System.TimeSpan.FromSeconds(10), () =>
{
List<Employee> employees = App.EmpDatabase.GetItemsAsync().Result;
List<EmployeeDatabindingDto> listBindingDto = MapEmpWithEmpBindingDto(employees).Result;
listView.ItemsSource = listBindingDto;
Device.BeginInvokeOnMainThread(UpdateUserDataAsync);
return true;
});
}
private async void UpdateUserDataAsync()
{
await App.EmpDatabase.UpdateEmpStatusAsync();
}
}
I'm trying to access some objects that I create with cs (carousel pages with labels, listview and buttons) but I have not had success.
Here reference this by saying that you can work with classId or styleId, but it does not make it very clear how to get the same:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/
Here you can see the carousel pages
This is my cs code:
private readonly List<AreaModel> _are = new List<AreaModel>();
public AreasModal()
{
Lista();
}
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
var index = Children.IndexOf(CurrentPage);
}
private async void Lista()
{
var listareas = await App.NaturalezaManager.GetAreas();
foreach (var Area in listareas) {
var areas = new AreaModel
{
IdArea = Area.IdArea,
NombreArea = Area.NombreArea
};
_are.Add(areas);
Button boton = new Button
{
Text = "Listo",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
Children.Add(new ContentPage
{
Content = new StackLayout
{
Children =
{
new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
Orientation = StackOrientation.Vertical,
Children =
{
new Label
{
Text = Area.NombreArea,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
}
}
},
new Switch
{
HorizontalOptions = LayoutOptions.EndAndExpand
}
}
},
new ListView
{
ItemsSource = listareas,
ItemTemplate = new DataTemplate(() =>
{
Label nameLabel = new Label
{
HorizontalOptions = LayoutOptions.FillAndExpand
};
nameLabel.SetBinding(Label.TextProperty,"NombreArea");
Switch switcher = new Switch
{
HorizontalOptions = LayoutOptions.EndAndExpand
};
return new ViewCell
{
View = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
Orientation = StackOrientation.Vertical,
Children =
{
nameLabel
}
},
switcher
}
}
};
})
},
boton
}
}
});
}
}
}
I have to access these objects from the "OnCurrentPageChanged" method but I have no idea how to do it, since if it were xaml code could be obtained with the property x:Name, in advance thanks for the help.
I'm using Telerik UI Pro components for {N} (without support) and having some problems with the DataForm.
When I pass a object to the page context, all fields using Picker editor can't select the right value.
I'll show the code I wrote:
teste-model.js
var Value = (function() {
function Value(text, value) {
this.text = text;
this.value = value;
}
return Value;
})();
var ValueModel = (function() {
function ValueModel() {}
Object.defineProperty(ValueModel.prototype, "model", {
get: function () {
if (!this._model) {
this._model = new Value("This is a text", "VALUE 1");
}
return this._model;
},
enumerable: true,
configurable: true
});
return ValueModel;
})();
exports.Value = Value;
exports.ValueModel = ValueModel;
teste.js
var ValueModel = require("./teste-model").ValueModel;
var page;
exports.onPageLoaded = function(args) {
console.log("Carregando página...");
page = args.object;
page.bindingContext = new ValueModel();
console.log(JSON.stringify(page.bindingContext));
}
teste.xml
<Page loaded="onPageLoaded"
xmlns:df="nativescript-telerik-ui-pro/dataform">
<StackLayout>
<df:RadDataForm id="myDataForm" source="{{ model }}">
<df:RadDataForm.properties>
<df:EntityProperty name="text" displayName="Text" index="0" />
<df:EntityProperty name="value" displayName="Value" index="1" valuesProvider="VALUE 0, VALUE 1, VALUE 2">
<df:EntityProperty.editor>
<df:PropertyEditor type="Picker" />
</df:EntityProperty.editor>
</df:EntityProperty>
</df:RadDataForm.properties>
</df:RadDataForm>
</StackLayout>
</Page>
The field value should show "VALUE 1" but shows "VALUE 0":
Any tips to solve this?
Update
I've made the changes Vladimir recommended but the picker property still not reflecting the object changes.
I also added a button to the page, to fill the dataform with random values.
The text property listen to the change normally but the picker property does not.
If I choose a picker value and click the button, the property is resetting to the first provider value.
The actual code is:
teste.xml
<Page loaded="onPageLoaded"
xmlns:df="nativescript-telerik-ui-pro/dataform">
<StackLayout>
<df:RadDataForm id="myDataForm" source="{{ model }}">
<df:RadDataForm.properties>
<df:EntityProperty name="text" displayName="Text" index="0" />
<df:EntityProperty name="value" displayName="Value" index="1" valuesProvider="VALUE 0, VALUE 1, VALUE 2">
<df:EntityProperty.editor>
<df:PropertyEditor type="Picker" />
</df:EntityProperty.editor>
</df:EntityProperty>
</df:RadDataForm.properties>
</df:RadDataForm>
<Button text="change" tap="changeModel" />
</StackLayout>
</Page>
teste.js
exports.onPageLoaded = function(args) {
console.log("Carregando página...");
page = args.object;
page.bindingContext = new ValueModel();
}
exports.changeModel = function(args) {
var arr = ["VALUE 0", "VALUE 1", "VALUE 2"];
page.bindingContext.set("model", new Value(
Math.random(10000, 99999).toString()
, arr[Math.floor(Math.random() * arr.length)]
)
);
console.log(JSON.stringify(page.bindingContext.model));
}
teste-model.js
var Observable = require("data/observable").Observable;
var Value = (function() {
function Value(text, value) {
this.text = text;
this.value = value;
}
return Value;
})();
var ValueModel = (function(_super) {
__extends(ValueModel, _super);
function ValueModel() {
_super.call(this);
this.model = new Value("This is a texte","VALUE 1");
}
Object.defineProperty(ValueModel.prototype, "model", {
get: function () {
return this.get("_model");
},
set: function(_model) {
this.set("_model", _model);
},
enumerable: true,
configurable: true
});
return ValueModel;
})(Observable);
exports.Value = Value;
exports.ValueModel = ValueModel;
It looks like you you are making runtime changes to an simple JavaScript object (the model) which is why those changes are not being reflected in the RadDataForm. When the desired behavior is to be able to change some objects properties at runtime you could use the {N} Observable located at data/observable module of the tns-core-modules. After that make the ValueModel to extend it and change the signature of the model property like this:
var observable_1 = require("data/observable");
var ValueModel = (function (_super) {
__extends(ValueModel, _super);
function ValueModel() {
_super.call(this);
this.model = new Value("This is a text", "INITIAL VALUE 0");
this.model.value = "VALUE 1";
}
Object.defineProperty(PersonViewModel.prototype, "model", {
get: function () {
return this.get("_model");
},
set: function (value) {
this.set("_model", value);
},
enumerable: true,
configurable: true
});
return PersonViewModel;
}(observable_1.Observable));
You can also take a look at the TypeScript variant in the master branch of the nativescript-telerik-ui-samples GitHub repository which was changed to illustrate this scenario.