We have a really simple app, the idea is the timer will update a label on the home screen depending on different configuration within the mobile app. I have created the binding and can update the homepage from it's self but not from the timer. I think what is missing is a OnChange within the home page to detect if the string has changed.
Display layout code, bind the label to the name "LabelText"
<Label
Text = "{Binding LabelText, Mode=TwoWay}"
x:Name="MainPageStatusText"
HorizontalOptions="CenterAndExpand"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="6"
VerticalOptions="CenterAndExpand"
TextColor="White"
FontSize="Medium"/>
This is the class file to link the text string to the label, I can see it been called from the different places but when it's called from the app.cs it does not work
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms;
namespace Binding_Demo
{
public class MyClass : INotifyPropertyChanged
{
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{ PropertyChanged?.Invoke(this, e); }
protected void OnPropertyChanged(string propertyName)
{ OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); }
public event PropertyChangedEventHandler PropertyChanged;
private string labelText;
public string LabelText
{
get {
return labelText;
}
set
{
labelText = value;
OnPropertyChanged("LabelText");
}
}
}
}
This is the code inside the homepage, this works and I can see it sending data to the text label
public static MyClass _myClass = new MyClass();
public Homepage()
{
BindingContext = _myClass;
_myClass.LabelText = "Inside the home page";
}
This is the App.cs code, we start the timer and then want to set the text on the Homepage label. I can see the class been called, but it does not set the text.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Binding_Demo
{
public partial class App : Application
{
public static MyClass _myClass = new MyClass();
public App()
{
//InitializeComponent();
Device.StartTimer(TimeSpan.FromSeconds(10), () =>
{
Task.Run(() =>
{
Debug.WriteLine("Timer has been triggered");
// !!!!! This is not setting the text in the label !!!!!
BindingContext = _myClass;
_myClass.LabelText = "Inside the timer app";
});
return true; //use this to run continuously
});
MainPage = new NavigationPage(new MainPage());
}
protected override void OnStart()
{
//
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
// force app to mainpage and clear the token
}
}
}
I have created the binding and can update the homepage from it's self but not from the timer.
As Jason said, please make sure the binding model is unique. You could create a global static instance of MyClass in App class, then bind this instance to HomePage.
Check the code:
App.xaml.cs
public partial class App : Application
{
public static MyClass _myClass = new MyClass();
public App()
{
InitializeComponent();
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
Task.Run(() =>
{
_myClass.LabelText = "Inside the timer app";
});
return true;
});
MainPage = new NavigationPage(new Homepage());
}
}
Homepage.xaml.cs:
public Homepage()
{
InitializeComponent();
BindingContext = App._myClass;
}
Related
Why when I try to set ActionBar.CustomView.SetBackgroundColor (Color.White); Does the application crash?
I tried installing the ToolbarItems background via style, toolbar.axml via ResourceDictionary but not one of these methods works for me - I think because I'm too dumb.
Now this option is the easiest for me, since there is only one line of code, but somewhere you can see the conflict comes from style or toolbar.axml or something else.
ActionBar.CustomView.SetBackgroundColor (Color.White);
my app.xaml.cs
using System;
using System.IO;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using MyApp1.Services;
using MyApp1.Views;
namespace MyApp1
{
public partial class App : Application
{
static Data.TodoItemDatabase database;
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
protected override void OnStart()
{
// Handle when your app start
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
public static Data.TodoItemDatabase Database
{
get
{
if (database == null)
{
database = new Data.TodoItemDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TodoSQLite.db3"));
}
return database;
}
}
public int ResumeAtTodoId { get; set; }
}
}
Solution 1:
You can set the style of TitleView in Forms . You can create a base Navigation Pageand set the style of TitleView
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal" BackgroundColor="White" >
//...
</StackLayout>
</NavigationPage.TitleView>
Solution 2
You can try creating a Custom Renderer and set it as the base class for all pages in the app. Within this custom renderer you can try to set the background color and other style as you want in CustomTitleView .
[assembly: ExportRenderer(typeof(Page), typeof(AndroidNavigationPageRenderer )]
public class AndroidNavigationPageRenderer : PageRenderer
{
private CustomTitleView _titleView;
public NavigationSearchRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
var activity = this.Context as FormsAppCompatActivity;
if (activity == null)
return;
var toolbar = activity.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
_titleView= new TitleView(Context);
toolbar.AddView(_titleView);
}
}
i have follow grial grid to enable the itemClickCommand.
below is the source code DashboardMultipleTilesPage.xaml:
<grial:GridView
WidthRequest="320"
Margin="0"
Padding="10"
ColumnSpacing="10"
RowSpacing="10"
ItemsSource="{ Binding Items }"
ItemClickCommand="{ Binding ItemCommand }"
ItemTemplate="{ StaticResource Selector }"
ColumnCount="2"/>
and below is from DashboardMultipleTilesViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows.Input;
using UXDivers.Grial;
using Xamarin.Forms;
namespace QlikApps
{
public class DashboardMultipleTilesViewModel :ObservableObject
{
private readonly Command _itemCommand;
public ICommand ItemCommand => _itemCommand;
public DashboardMultipleTilesViewModel(): base(listenCultureChanges: true)
{
_itemCommand = new Command<DashboardMultipleTilesPage>(ItemAction);
LoadData();
}
public ObservableCollection<DashboardMultipleTileItemData> Items { get; } = new ObservableCollection<DashboardMultipleTileItemData>();
protected override void OnCultureChanged(CultureInfo culture)
{
LoadData();
}
private void LoadData()
{
Items.Clear();
JsonHelper.Instance.LoadViewModel(this, source:"NavigationDashboards.json");
}
private void ItemAction(DashboardMultipleTilesPage items)
{
Application.Current.MainPage.DisplayAlert("Hello",
items.Title, "OK");
string id = items.id;
}
}
}
ItemAction(DashboardMultipletilesPage item) not fire at all?
How to access data which currently point to the grid?
please help.
You can improve
public ICommand ItemCommand {get; set;}
And
ItemCommand = new Command<DashboardMultipleTilesPage>(ItemAction);
Update:
Make sure you have set the BindingContext in your ContentPage.
enter image description here
Hello,
I have a problem with Xamarin, when i run program i see only white screen, just like on picture.
MainPage.xaml.cs have this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace Hello
{
public class App : Application
{
public App()
{ // The root page of your application
MainPage = new ContentPage
{
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
new Label {
HorizontalTextAlignment = TextAlignment.Center,
Text = "Welcome to Xamarin Forms!"
}
}
}
};
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
I take this code from "Creating Mobile Apps with Xamarin Forms", somebody know what is wrong with this program?
Write your code in Shared project's App.xaml.cs class which is partial class. The code above you are showing is not partial class. Probably, as your screenshot showing you have written App class code in Main.axml.cs file
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace App
{
public partial class App : Application
{
public App()
{
InitializeComponent();
//MainPage = new MainPage();
MainPage = new ContentPage
{
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children =
{
new Label
{
HorizontalTextAlignment=TextAlignment.Center,
Text="Welcome to xamarin forms"
}
}
}
};
}
}
}
I have been trying to bind my ListView to my View model. The view model successfully retrieves 5 records from the database and the Listview seems to display 5 blank rows, however it is not showing binding for each field within each row.
I have spent a couple of days searching internet but I don't seem to be doing anything different. I was using master detail pages so I thought that it may be the issue so I set my Events page as first navigation page without master/detail scenario but to no avail. Please note that I am using Portable Ninject for my dependencies/IoC.
My App.Xamal.cs is is as follows:
public App (params INinjectModule[] platformModules)
{
InitializeComponent();
var eventsPage = new NavigationPage(new EventsPage());
//Register core services
Kernel = new StandardKernel(new MyAppCoreModule(), new MyAppNavModule(eventsPage.Navigation));
//Register platform specific services
Kernel.Load(platformModules);
//Get the MainViewModel from the IoC
eventsPage.BindingContext = Kernel.Get<EventsViewModel>();
((BaseViewModel)(eventsPage.BindingContext)).Init();
MainPage = eventsPage;
}
My EventsPage.Xaml is provided below:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Views.EventsPage"
Title="Events">
<ContentPage.Content>
<ListView x:Name="Events" ItemsSource="{Binding Events}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding EventID}" BackgroundColor="Red" TextColor="White"
FontAttributes="Bold" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
My EventsPage.xaml.cs is provided below:
namespace MyApp.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EventsPage : ContentPage, IBaseViewFor<EventsViewModel>
{
public EventsPage ()
{
InitializeComponent ();
}
EventsViewModel _vm;
public EventsViewModel ViewModel
{
get => _vm;
set
{
_vm = value;
BindingContext = _vm;
}
}
}
}
My EventsViewModel is as follows, it successfully retrieves 5 records and OnPropertyChanged is fired for Events property:
namespace MyApp.ViewModels
{
public class EventsViewModel : BaseViewModel, IBaseViewModel
{
ObservableCollection<Event> _events;
readonly IEventDataService _eventDataService;
public ObservableCollection<Event> Events
{
get { return _events; }
set
{
_events = value;
OnPropertyChanged();
}
}
public EventsViewModel(INavService navService, IEventDataService eventDataService) : base(navService)
{
_eventDataService = eventDataService;
Events = new ObservableCollection<Event>();
}
public override async Task Init()
{
LoadEntries();
}
async void LoadEntries()
{
try
{
var events = await _eventDataService.GetEventsAsync();
Events = new ObservableCollection<Event>(events);
}
finally
{
}
}
}
}
My BaseViewModel is as follows:
namespace MyApp.ViewModels
{
public abstract class BaseViewModel : INotifyPropertyChanged
{
protected INavService NavService { get; private set; }
protected BaseViewModel(INavService navService)
{
NavService = navService;
}
bool _isBusy;
public bool IsBusy
{
get
{
return _isBusy;
}
set
{
_isBusy = value;
OnPropertyChanged();
OnIsBusyChanged();
}
}
protected virtual void OnIsBusyChanged()
{
}
public abstract Task Init();
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// Secod BaseViewModel abstract base class with a generic type that will be used to pass strongly typed parameters to the Init method
public abstract class BaseViewModel<TParameter> : BaseViewModel
{
protected BaseViewModel(INavService navService) : base(navService)
{
}
public override async Task Init()
{
await Init(default(TParameter));
}
public abstract Task Init(TParameter parameter);
}
}
IBaseViewModel is just a blank interface:
public interface IBaseViewModel
{
}
IBaseViewFor is given below:
namespace MyApp.ViewModels
{
public interface IBaseViewFor
{
}
public interface IBaseViewFor<T> : IBaseViewFor where T : IBaseViewModel
{
T ViewModel { get; set; }
}
}
My Event model is as follows:
namespace MyApp.Models
{
public class Event
{
public int EventID;
}
}
Finally, the image of the output, as you can see that 5 rows are created with red background but EventID is not binding in each row. I have checked the data and EventID is returned. I have even tried to manually add records into Events list but to no avail, see the manual code and image below:
async void LoadEntries()
{
try
{
Events.Add((new Event() { EventID = 1 }));
Events.Add((new Event() { EventID = 2 }));
Events.Add((new Event() { EventID = 3 }));
Events.Add((new Event() { EventID = 4 }));
Events.Add((new Event() { EventID = 5 }));
}
finally
{
}
}
I have spent a lot of time on it but unable to find a reason for this anomaly, can someone please cast a fresh eye and provide help!?
You can only bind to public properties - ie, you need a getter
public class Event
{
public int EventID { get; set; }
}
I want to make a simple login UI in xaml using Xamarin. I create a username and password field with Entry in the MainPage and then I try to bind them to my LoginViewModel where I can access my connexion method.
When I define the Binding context in the Mainpage codebehind the application simply shutdown and I dont understand why, what am I doing wrong ?
MainPage.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:suivAAndroid"
x:Class="suivAAndroid.MainPage">
<StackLayout
VerticalOptions="CenterAndExpand">
<Image></Image>
<Label
Text="Login"
StyleId="lbl_login"></Label>
<Entry
StyleId="ent_login"
Text="{Binding Username}"></Entry>
<Label
Text="Mot de passe"
StyleId="ent_mdp"></Label>
<Entry
StyleId="ent_mdp"
Text="{Binding Password}"></Entry>
<Button
Clicked="connexion_click"
Text="Connexion"></Button>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace suivAAndroid
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new LoginViewModel(); // Here is where it does not work. If the line is commented out, then the application launch without stopping but because there is no binding context I cant get the user inputs.
}
private void connexion_click(object sender, EventArgs e)
{
LoginViewModel connexionBtn = new LoginViewModel();
Device.BeginInvokeOnMainThread(async () =>
{
await connexionBtn.Connexion();
});
}
}
}
LoginViewModel.cs
using suivAAndroid.Models;
using suivAAndroid.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace suivAAndroid
{
public class LoginViewModel
{
#region propriétés
public string Username
{
get
{
return Username;
}
set
{
Username = value;
}
}
public string Password
{
get
{
return Password;
}
set
{
Password = value;
}
}
#endregion
#region constructor
public LoginViewModel()
{
}
#endregion
#region methodes
public void CreerListeVisiteurDur(List<Visiteur> uneListe)
{
Visiteur unVisiteur = new Visiteur("Clooney", "George", "cgeorge", "azerty", "rue du port", "59", "lille", new DateTime(2015 / 07 / 13));
uneListe.Add(unVisiteur);
}
public async Task Connexion()
{
List<Visiteur> uneListe = new List<Visiteur>();
CreerListeVisiteurDur(uneListe);
if (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password))
{
foreach (Visiteur unVisiteur in uneListe)
{
string login = unVisiteur.login;
string pass = unVisiteur.mdp;
if (login == Username && pass == Password)
{
App.Current.MainPage = new CreerVisite();
}
}
}
}
#endregion
}
}
Your ViewModel properties have infinite loops:
public string Username
{
get
{
return Username;
}
set
{
Username = value;
}
}
calling Username = value will call set on Username which in turn calls Username = value again.
Also, in order for your ViewModel to be bindable, you must implement INotifyPropertyChanged.
If you want a framework that is easy to use to help you do this, I would suggest Mvvm Light.
Here's an example of what your ViewModel should look like:
public class MyViewModel : INotifyPropertyChanged
{
public event EventHandler<PropertyChangedEventArgs> OnPropertyChanged;
private string _username;
public string Username
{
get
{
return _username;
}
set
{
_username = value;
PropertyChanged?.Invoke(new PropertyChangedEventArgs("Username");
}
}
....
}
in connexion_click you are creating a new copy of your VM that has no relation to the prior copy you created for your BindingContext.
public partial class MainPage : ContentPage
{
private LoginViewModel vm;
public MainPage()
{
InitializeComponent();
vm = new LoginViewModel();
BindingContext = vm;
}
private void connexion_click(object sender, EventArgs e)
{
Device.BeginInvokeOnMainThread(async () =>
{
await vm.Connexion();
});
}
}
your VM should implement INotifyPropertyChanged
your VM has a recursive getter