xamarin forms unable to show sqlite data in listview - sqlite

I am struggling to find the answer by myself, using previous Stackoverflow posts, youtube and google searching.
I am trying to learn how to use SQLite with xamarin forms.
Solution connection:
using SQLite;
namespace TestSQLite
{
public interface IDatabaseConnection
{
SQLiteAsyncConnection GetConnection();
}
}
Android specific connection (iOS is identical)
using SQLite;
using System.IO;
using TestSQLite;
using Xamarin.Forms;
[assembly: Dependency(typeof(DatabaseConnection))]
namespace TestSQLite
{
public class DatabaseConnection : IDatabaseConnection
{
public SQLiteAsyncConnection GetConnection()
{
var dbName = "TestDb.db3";
var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments), dbName);
return new SQLiteAsyncConnection(path);
}
}
}
And the MainPage C# code:
using SQLite;
using Xamarin.Forms;
namespace TestSQLite
{
public class ControlledDrugs
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Drug { get; set; }
public double Volume { get; set; }
}
public class Users
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
public partial class MainPage : ContentPage
{
private SQLiteAsyncConnection _connection;
public MainPage()
{
InitializeComponent();
_connection = DependencyService.Get<IDatabaseConnection>().GetConnection();
}
protected override async void OnAppearing()
{
await _connection.CreateTableAsync<ControlledDrugs>();
await _connection.CreateTableAsync<Users>();
RefreshUsers();
RefreshDrugs();
base.OnAppearing();
}
async void OnAdd(object sender, System.EventArgs e)
{
var user = new Users { Name = UserInput.Text };
await _connection.InsertAsync(user);
}
void OnUpdate(object sender, System.EventArgs e)
{
}
void OnDelete(object sender, System.EventArgs e)
{
}
async void RefreshUsers()
{
var userlist = await _connection.Table<Users>().ToListAsync();
Userlistview.ItemsSource = userlist;
}
async void RefreshDrugs()
{
var druglist = await _connection.Table<ControlledDrugs>().ToListAsync();
Drugslistview.ItemsSource = druglist;
}
private void Userlistview_Refreshing(object sender, System.EventArgs e)
{
RefreshUsers();
Userlistview.EndRefresh();
}
}
}
I know the add to sqlite method works, firstly because a user on Stackoverflow helped me, and secondly a blank cell appears on the listview. But thats the issue, the cells are blank, no matter how many I add, all blank.
I can't seem to physically access the sqlite database on the emulator to open and investigate if the entries are being written or entered as blanks. System.Environment.SpecialFolder.MyDocuments does not seem to save the .db3 in the emulator My Documents - separate issue, but limiting me to find the answer myself.
So i know the issue is either: 1)when the solution enters the data into the database (as blank) or if 2)the recall of data from the database to be viewed on the listview has the error.
Also, from my code you can probably see I am calling the refresh listview manually (by the user pulling the listview, because I am still learning and observable collection method/approach is a bit beyond me ATM.
Thanks team
UPDATE: Xaml code as requested: Thank you.
<?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:TestSQLite"
x:Class="TestSQLite.MainPage">
<StackLayout>
<Label Text="User Input"></Label>
<Entry x:Name="UserInput"></Entry>
<Button Text="add it" Clicked="OnAdd"></Button>
<Label Text="User"></Label>
<ListView x:Name="Userlistview" IsPullToRefreshEnabled="True" Refreshing="Userlistview_Refreshing"></ListView>
<Label Text="Drugs"></Label>
<ListView x:Name="Drugslistview"></ListView>
</StackLayout>
</ContentPage>

I'm a bit late, but hopefully this will help someone in the future (it would have certainly helped me!)
I ran into this same problem while working through the Xamarin tutorials on Microsoft's site. The tutorial first had you save a list to files, then changed to using the SQLite database. When I switched I found that adding a new record populated a blank list entry.
The culprit turned out to be in the binding between the data entry page, the list view and the variable names in the class. I had the class defined as:
public class Player
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string PlayerName { get; set; }
public DateTime JoinDate { get; set; }
}
When performing data entry I SHOULD have had:
<StackLayout Margin="20">
<Editor Placeholder="Enter player name"
Text="{Binding PlayerName}"
HeightRequest="50" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnRosterEntrySaveButtonClicked"
Grid.Row="1" />
<Button Text="Delete"
Clicked="OnRosterEntryDeleteButtonClicked"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
</StackLayout>
Instead I had "Text = "{Binding Text}" in the Editor. This didn't generate an error on build. I also had an error in the list view. What I SHOULD have had was:
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding PlayerName}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
Instead I again had "Text" instead of "PlayerName". Note above that "Detail="{Binding Date}" is also wrong. The variable in the class is actually JoinDate. The above binding doesn't generate an error, however when the app runs no data is shown. Changing the binding to JoinDate and re-building allows the data to be shown.
My recommendation would be to check your bindings for setting and displaying the data on those pages.

Related

How to bind two different class properties in DataTemplate

I am trying to bind two properties from different classes in DataTemplate.
<DataTemplate x:Key="DemoItemTemplate" x:DataType="local:DemoInfo">
<NavigationViewItem Visibility="{Binding Visibility, Mode=TwoWay}" Content="{x:Bind Name}"/>
</DataTemplate>
DataType set as DemoInfo for this DataTemplate and Name value updated from DemoInfo.
I have tried view model as source and relative source binding. But Visibility property binding not working from ViewModel class. Any suggest how to achieve this?
Visibility="{Binding Visibility, Source={StaticResource viewModel}}"
AFAIK , you cant use multibinding in UWP , you can try to use Locator What is a ViewModelLocator and what are its pros/cons compared to DataTemplates?
How to bind two different class properties in DataTemplate
If you bind Visibility with StaticResource, please declare ViewModel class in your page Resources like the following.
ViewModel
public class ViewModel
{
public ViewModel()
{
Visibility = false;
}
public bool Visibility { get; set; }
}
Xaml
<Page.Resources>
<local:ViewModel x:Key="ViewModel" />
</Page.Resources>
<DataTemplate x:DataType="local:Item">
<TextBlock
Width="100"
Height="44"
Text="{x:Bind Name}"
Visibility="{Binding Visibility, Source={StaticResource ViewModel}}" />
</StackPanel>
</DataTemplate>
Update
If you want Visibility value changed dynamically at run-time, you need implement INotifyPropertyChanged interface for ViewModel class.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Visibility = false;
}
private bool _visibility;
public bool Visibility
{
get
{
return _visibility;
}
set
{
_visibility = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
}
For more detail please refer Data binding in depth official document.

Binding two viewModel to one view

i am trying to bind my MasterViewModel where i have initiated two original viewModel to one view. But i am not getting any data so i must be doing the binding wrong. I have found several post
I have tried
in Xaml
<Label
x:Name="SectionRequired"
Grid.Row="2"
HorizontalOptions="End"
IsVisible="{Binding PostViewModel.IsRequired, Source={x:Reference PostViewModel}}"
Text="{x:Static resources:AppResources.AlertRequired}"
TextColor="Red" />
And also followed this solution but i was getting an expcetion that its used lika markup extenstion 'local1:PostViewModel' is used like a markup extension but does not derive from MarkupExtension.
https://stackoverflow.com/questions/50307356/multiple-bindingcontexts-on-same-contentpage-two-different-views
My Master
class MasterPostsViewModel : BaseViewModel
{
public PostViewModel postViewModel { get; set; }
public CategoriesViewModel categoriesViewModel { get; set; }
public MasterPostsViewModel()
{
postViewModel = new PostViewModel();
categoriesViewModel = new CategoriesViewModel();
}
}
}
Conte page
I have set the binding to one field here and that works, buit having to do that for the whole page is not what i want.
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel();
NameRequired.IsVisible = ViewModel.postViewModel.IsRequired;
}
Can you help please
instead of
IsVisible="{Binding PostViewModel.IsRequired, Source={x:Reference PostViewModel}}"
just use
IsVisible="{Binding postViewModel.IsRequired}"
your property name is postViewModel is lower case
also, get rid of this line - it will break the binding you have setup in the XAML
NameRequired.IsVisible = ViewModel.postViewModel.IsRequired;

prism null exception on Container.Resolve<IEventAggregator>()

The line Resources.Add("eventAggregator", Container.Resolve()); raises Null exception.
UPDATE
I've added all classes to explain more. As #Axemasta said, there is no need to register IEventAggregator and I removed registration. Now I don't how to connect the Listview EventAggregator behavior to the EventAggregator.
This is whole App.xaml code file.
public partial class App : PrismApplication
{
/*
* The Xamarin Forms XAML Previewer in Visual Studio uses System.Activator.CreateInstance.
* This imposes a limitation in which the App class must have a default constructor.
* App(IPlatformInitializer initializer = null) cannot be handled by the Activator.
*/
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
Resources.Add("eventAggregator", Container.Resolve<IEventAggregator>());// Removed on update
FlowListView.Init();
await NavigationService.NavigateAsync("NavigationPage/MainPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage>();
}
}
}
The behavior class:
public class ScrollToMyModelBehavior : BehaviorBase<ListView>
{
private IEventAggregator _eventAggregator;
public IEventAggregator EventAggregator
{
get => _eventAggregator;
set
{
if (!EqualityComparer<IEventAggregator>.Default.Equals(_eventAggregator, value))
{
_eventAggregator = value;
_eventAggregator.GetEvent<ScrollToMyModelEvent>().Subscribe(OnScrollToEventPublished);
}
}
}
private void OnScrollToEventPublished(ListItem model)
{
AssociatedObject.ScrollTo(model, ScrollToPosition.Start, true);
}
protected override void OnDetachingFrom(ListView bindable)
{
base.OnDetachingFrom(bindable);
// The Event Aggregator uses weak references so forgetting to do this
// shouldn't create a problem, but it is a better practice.
EventAggregator.GetEvent<ScrollToMyModelEvent>().Unsubscribe(OnScrollToEventPublished);
}
}
The Event class:
public class ScrollToMyModelEvent : PubSubEvent<ListItem>
{
}
The page view model:
public MainPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
: base (navigationService)
{
Title = "صفحه اصلی";
ListHeight = 100;
ListWidth = 250;
_eventAggregator = eventAggregator;
Items items = new Items();
ListViewItemSouce = items.GetItems();
MyModels = items.GetItems();
SelectedModel = ListViewItemSouce[3];
_eventAggregator.GetEvent<ScrollToMyModelEvent>().Publish(SelectedModel);
}
The page view:
<StackLayout HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="{Binding ListWidth}" HeightRequest="{Binding ListHeight}"
Grid.Row="1" Grid.Column="1">
<local:NativeListView x:Name="lst3" ItemsSource="{Binding ListViewItemSouce}" Margin="1" BackgroundColor="Transparent" RowHeight="47" HasUnevenRows="false">
<ListView.Behaviors>
<local:ScrollToMyModelBehavior EventAggregator="{StaticResource eventAggregator}" /> // Error raised that there is not such a static property
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Word}" TextColor="Black"/>
</DataTemplate>
</ListView.ItemTemplate>
</local:NativeListView>
</StackLayout>
You do not need to register IEventAggregator when the app initialises, much like INavigationService or IPageDialog, you can use it straight out of the box!
To use EventAggregator you should do the following things:
Create an Event
You will first need to create an Event (using Prism) that you can pass to the EventAggregator. Your event should inherit from PubSubEvent, you can pass this an object (optional). So your event would look like this:
using System;
using Prism.Events;
namespace Company.App.Namespace.Events
{
public class SampleEvent : PubSubEvent
{
}
}
Looking at a recent app, I most commonly use this when passing data between custom popup views (like a dictionary of params).
Subscribe to the Event
When IEventAggregator fires, anything that has subscribe to the event will execute any code specified. In the class you want to recieved the event you will have to do the following:
Pass the class IEventAggregator through the constructor (prism does the DI afterall)
Initialise a local IEventAggregator for use in this class
Subscribe IEventAggregator to a handler method.
Here is what the code may look like:
public class TheClassListeningForAnEvent
{
private readonly IEventAggregator _eventAggregator;
public TheClassListeningForAnEvent(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.GetEvent<SampleEvent>().Subscribe(OnEventRecieved);
}
void OnEventRecieved()
{
//Do something here
}
}
Fire the Event
Now you have registered for the event, you can fire the event. Pass the IEventAggregator into whatever class you want to fire the event from and use the Publish Method:
public class TheClassPublishingAnEvent
{
private readonly IEventAggregator _eventAggregator;
public TheClassListeningForAnEvent(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.GetEvent<SampleEvent>().Publish();
}
}
Thats the long and the short of it. You could pass anything to the IEventAggregator, you would just need to handle for this in the methods you are subscribing.
Hopefully that is enough to get you going using IEventAggregator!
Just verify if you are adding below code in IOS, Android and UWP projects.
IOS- Appdelegate
public class AppdelegateInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
Android - MainActivity
public class MainActivityInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
UWP-- MainPage.cs
public class UwpInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
My guidance on this has evolved as features have been added to Prism to make this sort of thing even easier.
In the past the reason you would resolve and add the IEventAggregator as a StaticResource is that there was no way to inject this. We now have the ContainerProvider which amazingly allows you to add types in XAML that require Dependency Injection. To start you can refactor your ScrollToBehavior to use a DI Pattern by adding the IEventAggregator as a constructor parameter, removing the Bindable Property (if you choose).
public class ScrollToBehavior : BehaviorBase<ListView>
{
private IEventAggregator _eventAggregator { get; }
public ScrollToBehavior(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
}
As I mentioned you can use the ContainerProvider in XAML to resolve and provide a type that requires DI, as follows:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ioc="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:behavior="using:AwesomeProject.Behaviors
x:Class="AwesomeProject.Views.ViewA">
<ListView>
<ListView.Behaviors>
<ioc:ContainerProvider x:TypeArguments="behavior:ScrollToBehavior" />
</ListView.Behaviors>
</ListView>
</ContentPage>

Binding TextBlock text not update except Initial

I'm new on Caliburn.Micro.
The Binding a text on TextBlock.
The text of TextBlock is changed on start up or initialize on ViewModel,
But it would not change in fired function.
I don't know why for a day.
I need any help badly.
Here is code what i wrote.
In View
<TextBlock Grid.Row="0" FontSize="72" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"
x:Name="DisplayedPhoneNumber"/>
In ViewModel
//! Scren Binding.
public string DisplayedPhoneNumber { get; set; } ="0103214321";
When i press a button on view, i call a function like this,
In View
<Border Style="{StaticResource StyleNumberKeyBorder}">
<Button Content="1" Style="{StaticResource StyleNumberKeyButton}"
cal:Message.Attach="[Event Click]=[Action CmdNumberClick(1)]"/>
</Border>
In ViewModel, CmdNumberClick function like this...
public void CmdNumberClick(string pressed_number)
{
DisplayedPhoneNumber = "plz change...";
}
I check the fired function, and checked DisplayedPhoneNumber is changed,
But TextBlck was not changed.
Please help.
public string DisplayedPhoneNumber { get; set; }
needs to be
private string _displayedPhoneNumber;
public string DisplayedPhoneNumber{
get{ return _displayedPhoneNumber;}
set{
_displayedPhoneNumber = value;
NotifyOfPropertyChanged(() => DisplayedPhoneNumber);
}
}
Associated ViewModel has to inherit PropertyChangedBase or a base class that derives INotifyPropertyChanged;

Xamarin forms Picker Binding error

I have a picker control:
<Picker Title="Number of People"
ItemsSource="{Binding SomeList, Source={x:Static local:MyModelHandler.MyModel}}"
SelectedItem="{Binding SomeListSelectedIndex, Source={x:Static local:MyModelHandler.MyModel}}">
</Picker>
when trying to build i get "No property, bindable property, or event found for 'ItemsSource'" error.
Above that i have a label:
<Label Text ="{Binding SomeLabel, Source={x:Static local:MyModelHandler.MyModel}, Mode=OneWay}"></Label>
And that binding works perfectly
MyModelHandler is an static class that allowes only one Model
public static class MyModelHandler
{
private static MyModel myModel = new MyModel();
public static MyModel MyModel
{
get
{
return myModel;
}
}
}
And Model is simple:
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int selectedNumber = 1;
private string someLabel = "";
public IList<int> SomeList
{
get
{
return Enumerable.Range(1, 10).ToList();
}
}
public int SomeListSelectedIndex
{
get
{
return SomeList.IndexOf(this.selectedNumberOfPeople);
}
set
{
this.selectedNumber = SomeList[value];
}
}
public double SomeLabel
{
get
{
return this.someLabel;
}
set
{
this.someLabel= value;
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
...
}
Edit: Using Xamarin 4.3
You might check your Xamarin.Forms version.
The ItemsSource property was introduced in Xamarin.Forms 2.3.4.184-pre1, see release notes here:
https://developer.xamarin.com/releases/xamarin-forms/xamarin-forms-2.3/2.3.4-stable/#2.3.4.184-pre1.
If you are using an older Xamarin.Forms version you will get the Xamarin.Forms XAML error "No property, bindable property, or event found for 'ItemsSource'".
That is a very strange way to set up the binding context for a view. The fact you have to specify the source for each element adds a lot of extra boilerplate code.
Try setting the bindingcontext to the model in the the view constructor
BindingContext = new MyModel ();
Then the XAML becomes
<Picker Title="Number of People"
ItemsSource="{Binding SomeList}"
SelectedItem="{Binding SomeListSelectedIndex}">
</Picker>
Or use a proper MVVM framework and save yourself a lot of grief. I can recommend FreshMvvm.
https://github.com/rid00z/FreshMvvm

Resources