Updating all lists in Xamarin form SQLite - sqlite

I am using the listview of the same SQLite Table and I am facing a problem as I add new items or update the previous one it updates the values on the current page but need to reopen the app to update the list on Page B. Do you have any solution?Before Update
Updated Value here
Did'nt update here
so can you help me by giving me a link or video?

For adding and deleting items from list and make changes instantly, you need to use ObservableCollection in System.Collections.ObjectModel. There's no difference whether using SQLite or not; it's about list.
Here is an example:
public partial class Page : ContentPage
{
private ObservableCollection<Client> _client;
public SQLiteAsyncConnection connection;
public Page()
{
InitializeComponent();
}
protected override async void OnAppearing()
{
//initialize database
await connection.CreateTableAsync<CLient>();
var listofclient = await connection.Table<Client>().ToListAsync();
//initialize observablecollection
_client = new ObservableCollection<Client>(listofclients);
//using the list of client as source of listview
clientListveiw.ItemsSource = _client;
base.OnAppearing();
}
//add button
private async void onadd(object sender, EventArgs e)
{
var client = new Client { Name = "jhon"};
await connection.InsertAsync(patient);
_client.Add(patient);
}
}
And about the update, you should use INotifyPropertyChange interface; its a long story, but you can google it for further information.

Related

How to create viewmodels instance when app start time in xamarin forms MVVM

My aim is to access bindable property across the the App. But My current framework ViewModel Instance create multiple time
My Requirement : I have the cart count in the bottomTray(CheckuoutViewModel) i want to increase the cart count any where in the app page, but in this cart count not update when back click, its only working on forward navigation, the reason behind CheckoutViewModel instance create each and every time. so that i'm try to instant creation at earlier.
Here I'm list out sample ViewModel and calling method
Login ViewModel
Checkuout ViewModel(This view model common for all page)
BaseNavigationViewModel(Its BaseViewModel)
As of now i'm calling when BindinContext each and every time like,
new LoginViewMode(navigation)
new CheckoutViewModel(navigation)
what will do to create all ViewModel instance when app start time like ViewModel Locator?
Im tried
public static ViewModelLocator Locator
{
get { return locator ?? (locator = new ViewModelLocator()); }
}
And ViewModel Locator
public ViewModelLocator()
{
navigation = App.Current.MainPage.Navigation;
}
internal CustomTabBarViewModel CustomTabBarVM
{
get
{
return customTabBarVM ?? (customTabBarVM = new CustomTabBarViewModel(navigation));
}
}
And CustomTabBar.xaml.cs
public CustomTabBar()
{
viewModel = App.Locator.CustomTabBarVM;
InitializeComponent();
BindingContext = viewModel;
}
and Expectation
App.Locator.CustomTabBarVM.BadgeCartCount = OrderObject.Instance.ORDER_OBJECT.Items.Count;
This approach is working fine but it's create some navigation issues
A singleton instance is a common feature of virtually all MVVM frameworks (Prism, FreshMVVM etc). If you aren't using a framework (if you aren't, I would STRONGLY advise you consider using one), below is a solution.
To obtain a single instance of a ViewModel you can use the App class to host the object and access it whenever you need.
Create a public static property of your ViewModel:
public static MyViewModel MyViewModelInstance { get; }
Create an instance in the constructor of the app
public App()
{
InitializeComponent();
MyViewModelInstance = new MyViewModel();
var myPage = new MyPage()
{
BindingContext = MyViewModelInstance
};
var navPage = new NavigationPage(myPage);
MainPage = navPage;
}
Whenever you create a new page, access the shared instance
// This method is just an example of how you might create a new page and wire up the view model
async void GoNextClicked(System.Object sender, System.EventArgs e)
{
var myPage = new MyPage()
{
BindingContext = App.MyViewModelInstance
};
await this.Navigation.PushAsync(myPage);
}
This approach comes with a few caveats, you are creating instances when the app loads not when they are needed (Eagerly loading). So a performance optimisation would be to use Lazy<T> to handle the creation of these objects. However this is logic that has already been written for you in MVVM frameworks, they are there to help you and you should be using them.
Lazy Load
You can save memory and performance at startup by lazy loading the viewmodel, here is this example rewritten to support this pattern:
public static MyViewModel MyViewModelInstance
{
get => _myViewModelInstanceFactory.Value;
}
private static Lazy<MyViewModel> _myViewModelInstanceFactory = new Lazy<MyViewModel>(() => new MyViewModel(), true);
public App()
{
InitializeComponent();
var myPage = new MyPage()
{
BindingContext = MyViewModelInstance
};
var navPage = new NavigationPage(myPage);
MainPage = navPage;
}
Now this object won't be created until it is accessed by your code, and once it has been accessed once it has already been created and will go on to live in memory for the rest of your apps lifecycle.
Axemasta has good answer about re-use of a shared view model instance.
I'll give an alternative approach to the underlying need given in one comment: how to have a static property (so the value is common), and Bind to it when Binding to an instance.
Use this approach if you do want a different CheckoutViewModel for each new page. For example, if there are other properties that should be set up differently, depending on the page.
public class CheckoutViewModel : : INotifyPropertyChanged // or your MVVM library's base class for ViewModels.
{
public static int SharedCount { get; set; }
public void IncrementCount()
{
Count = Count + 1;
}
public int Count {
get => SharedCount;
set {
// Exact code might be simpler if using an MVVM library.
if (SharedCount != value)
{
SharedCount = value;
OnPropertyChanged("Count");
}
}
}
}
}
LIMITATION: This assumes that only the current instance of CheckoutViewModel is visible; if you need to "notify" OTHER Views (update other CheckoutViewModel instances), then you'll need a "publish/subscribe" solution. In Xamarin Forms, one such solution is MessagingCenter.

SQLITE Delete item does't delete for real

I got a xamarin forms app, and the problem is when I delete an item from a sqlite table, it looks like all works, the item is deleted from the collection, the grids got updated, etc, but when I restart the app, the item is still there. its like the delete only works in memory but it never got saved in the database.
my code is below
I create an instance called DB in my App constructor
public partial class App
{
static Database database;
public static Database DB
{
get
{
if (database == null)
{
string nombreBD = "MyDataBaseFile.db3";
string _databasePath = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, nombreBD);
database = new Database(_databasePath);
}
return database;
}
}
................
}
I'm using sqlite with tables created from classes, like this
db = new SQLiteAsyncConnection(dbPath);
db.CreateTableAsync<MyType>().Wait();
where MyType is a class like this
public class MyType
{
[PrimaryKey]
public int Idtable { get; set; }
......
}
I try to delete a row of the table like this:
var x = await App.DB.GetItemAsync<MyType>(obj.Idtable );
int regsDeleted = await App.DB.DeleteItemAsync<MyType>(x);
the GetItemsAsync is basically: await db.FindAsync<T>(id);
public async Task<T> GetItemAsync<T>(int id) where T : new()
{
try
{
return await db.FindAsync<T>(id);
}
catch (System.Exception ex)
{
throw new System.Exception($"Error sqlLite {MethodBase.GetCurrentMethod().Name}: {ex.Message}");
}
}
and the delete method is this:
public async Task<int> DeleteItemAsync<T>(T item) where T : new()
{
try
{
int regsDeleted=await db.DeleteAsync(item);
db.GetConnection().Commit();
return regsDeleted;
}
catch (System.Exception ex)
{
throw new System.Exception($"Error sqlLite {MethodBase.GetCurrentMethod().Name}: {ex.Message}");
}
}
like I said I got no errors and all looks like worked, but when restart the app, the item still there!!
any Idea? something to add in the connection maybe? transactions?... any help will be great
thanks
UPDATE After a lot of test I realize the problem is not the delete. The problem is that every time I run the app from VS to my android device through USB cable, I don't know how or why the database get restored from some backup, that I donĀ“t know when or where was done. Looks like Android have a backup and the "data" of my app and when a new version comes he just restore the data. I read somne that said the Xamarin.Essentials.FileSystem.AppDataDirectory should not be used to save databases, so the question is. where is th right place to save the SQLLite database.Any Idea? My app don't deployed an empty database, my app create the database in the first execution. Does anyone knows how to avoid that restauration of the folder? every time I run the app from VisualStudio ?
The DeleteAsync works without Commit. I make come changes for your code. It works on my side.
I add the PrimaryKey and AutoIncrement attributes to ensure that each Note instance in the SQLite.NET database will have a unique id provided by SQLite.NET.
public class MyType
{
[PrimaryKey, AutoIncrement]
public int Idtable { get; set; }
public string Text { get; set; }
}
The code for the connect to the database, save the record, delete the row and get the all the items.
readonly string _databasePath = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, "MyDataBaseFile.db3");
SQLiteAsyncConnection database;
public MyType myType { get; set; }
int i = 0;
public Page2()
{
InitializeComponent();
}
private void Connect_Clicked(object sender, EventArgs e)
{
database = new SQLiteAsyncConnection(_databasePath);
database.CreateTableAsync<MyType>().Wait();
}
async void Save_Clicked(object sender, EventArgs e)
{
myType = new MyType() { Text = "Hello" + i };
if (myType.Idtable != 0)
{
// Update an existing note.
await database.UpdateAsync(myType);
i++;
}
else
{
// Save a new note.
await database.InsertAsync(myType);
i++;
}
}
async void Delete_Clicked(object sender, EventArgs e)
{
var x = await database.FindAsync<MyType>(myType.Idtable);
int regsDeleted = await database.DeleteAsync(x);
}
async void Get_Clicked(object sender, EventArgs e)
{
var s = await database.Table<MyType>().ToListAsync();
try
{
var s2 = await database.FindAsync<MyType>(myType.Idtable);
}
catch
{
return;
}
}
}
Please note if i restart the app, there is no myType.Idtable. So i use the try catch to make my project run.
Add four items for the database and detele the last one.
After restart the app, the items:
I had a similar error. Very annoying and couldn't figure it out. After reading this question I have just deleted the db3 file on the android device rerun my app and now it works. I suspect that during development and changing the structure of the class for the table something gets screwed up. Deleting the database db3 (or whatever, sqlite doesn't care) re-created the the tables completely.
So how do you get to the file? (For a Pixel 5 emulator)
I used Android Studio and the DeviceFileExplorer (View>ToolWindows)
But where is it. Well In my app I use
private readonly static string filename = "xxx.db3";
...
database = new Database.Database(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), filename));
and I (eventually) found this located in data>data>(my Application Id)>files
where my ApplicationID is something like uk.co.mydomainname.myappname
I just then deleted the file with a right click delete
(Note: I found sometimes you have to right click the files folder and synchronise to refresh the tree and see the db file)
Hope this helps.
PS I wish for me (.net maui) the documentation explained more clearly the paths and where things get located/placed!!

Passing the search term from SearchHandler to ContentPage in Xamarin Forms 4

I'm trying to make use of the new SearchHandler implemented as part of Xamarin Forms 4. I've found it pretty easy so far to get suggestions populated but now I want to raise an event, or follow the suggested method of handling when a search is confirmed.
public class FoodSearchHandler: SearchHandler
{
IFoodDataStore dataStore = new FoodDataStore();
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
// What to do here?
}
protected override void OnQueryChanged(string oldValue, string newValue)
{
base.OnQueryChanged(oldValue, newValue);
if(!string.IsNullOrWhiteSpace(newValue)
{
// Populate suggestions
ItemsSource = dataStore.GetSuggestions(newValue);
}
else
{
ItemsSource = null;
}
}
}
public partial class FoodsPage : ContentPage
{
ObservableCollection<Food> Foods = new ObservableCollection<Food>();
public ItemsPage()
{
InitializeComponent();
// Wire up the search handler
Shell.SetSearchHandler(this, new FoodSearchHandler());
BindingContext = this;
}
}
Unfortunately, althought the alpha docs mention the search handler they don't contain any details on how to use it and the sample apps only demonstrate populating the suggestions.
Does anyone out there have a pointer to offer on how I should be notifying my ContentPage that my SearchHandler confirmed a search?
So, after reading the Shell docs some more, it seems what I want to do in this situation is use of Shell's new Navigation and navigate to a route passing the search text as a query, for example:
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
var shell = Application.Current.MainPage as Shell;
shell.GoToAsync($"app:///fructika/search?query={Query}", true);
}
N.B. It doesn't look like passing data works right now or if it does I'm doing it wrong but I'll raise a separate question about that.

EF: Update entity stored in session

I'm using EF 5 with Web Forms (ASP.NET 4.5), with the "one DbContext instance per request" approach.
But this situation is a bit complicated: I have a multi-step create/edit screen, and I store the current entity in Session, then I manipulate it and in the final step, I commit it to the Database.
Creating a new instance was fine, but I can't for the life of me edit an existing entity... Because it's another request, my original DbContext instance was lost and when I attach it to a new one, I get the An entity object cannot be referenced by multiple instances of IEntityChangeTracker error.
My code is far too complex to post here, but I'll try and summarize it accurately:
My DbContext:
public class AppContext : DbContext
{
// DbSet declarations...
public static AppContext Current {
get { var context = HttpContext.Current.Items["Contexts.AppContext"] as AppContext;
if (context == null)
{
context = new AppContext();
HttpContext.Current.Items["Contexts.AppContext"] = context;
}
return context;
}
}
}
An example of what the page code looks like:
protected void Page_Load(object sender, EventArgs e)
{
int? id = null; // After this, I try to get it from the QueryString, parse it, etc.. Omitted for sake of brevity
// If I have an ID, it means I'm editing...
Session["Product"] = id.HasValue ? new Product() : AppContext.Current.Products.Find(id));
MethodToPopulateFields(); // Internally, it uses the Session variable
}
protected void Step1(){ // through n
// Manipulates the Session["Product"] based on page input...
}
protected void Save(){
var product = Session["Product"] as Product;
if(product.ID == 0)
product = AppContext.Current.Products.Add(product);
// throws an exception:
// AppContext.Current.Entry(product).State = EntityState.Modified;
// this too:
// AppContext.Products.Attach(product);
AppContext.Current.SaveChanges();
}
I know I can get the old entity from the database, update it manually and save, all in the last step, but I really don't want to do that...
Thank you.
Try calling
AppContext.Current.Entry(product).State = EntityState.Detached;
in the first method.

Caliburn Micro: querying view-specific data from the VM

I'm completely new to CM and also to learn it I'm migrating an application from MVVM light to Caliburn Micro. In my original code, I had a VM which responds to some UI actions (via commands) to replace some text into a string. The position is given by the view, using the textbox selection.
So the VM has (1) a bound string property representing the textbox's text, (2) another bound string property to represent the new text to be added, and (3) needs to know selection start and length in order to replace the right portion of text with the new one.
In my original code, I had a custom DialogMessage-derived object sent in the VM command implementation with a couple of properties for selection data: when the command was issued, the message was sent, and the view received it and filled it with its textbox selection start and length; then the VM was called back and could use these data.
Which would be the best way of implementing this in CM? I'd prefer the VM to remain agnostic of the view, so I don't like too much the idea of accessing the view from it. I'd rather opt for a "message"-based mechanism like the above, but I'm not sure how I can implement it in CM: I would probably look at IResult, but most of the samples I find are related to coroutines and I'm not sure how to relate the void ReplaceText() method of the VM to the view code behind.
Could anyone point me in the right direction, and/or to some code samples about dialog-like interactions between VM 'command' methods and view? Thanks!
I'd probably look at the IResult option. You'll have access to the view so code that you would have had in the code behind can be in your Result and not in your VM.
Here is code from a ShowDialog result. I believe I grabbed it from the CM discussion group. Search the discussion group for ShowDialog for more examples. The GameLibrary sample that comes with CM also has some.
public class ShowDialog : IResult
{
private readonly Type _screenType;
private readonly string _name;
[Import]
public IWindowManager WindowManager { get; set; }
public ShowDialog(string name)
{
_name = name;
}
public ShowDialog(Type screenType)
{
_screenType = screenType;
}
public void Execute(ActionExecutionContext context)
{
var screen = !string.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_screenType, null);
Dialog = screen;
WindowManager.ShowDialog(screen);
var deactivated = screen as IDeactivate;
if (deactivated == null)
Completed(this, new ResultCompletionEventArgs());
else
{
deactivated.Deactivated += (o, e) =>
{
if (e.WasClosed)
{
Completed(this, new ResultCompletionEventArgs());
}
};
}
}
public object Dialog { get; private set; }
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static ShowDialog Of<T>()
{
return new ShowDialog(typeof (T));
}
}
edit: If you extend TextBox you can bind SelectedText.
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty SelectedTextProperty = DependencyProperty.Register("SelectedText", typeof(string), typeof(TextBoxEx), new PropertyMetadata("oa"));
public TextBoxEx()
{
SelectionChanged += UpdateDependencyProperty;
}
private void UpdateDependencyProperty(object sender, RoutedEventArgs e)
{
SelectedText = base.SelectedText;
}
public new string SelectedText
{
get { return GetValue(SelectedTextProperty).ToString(); }
set { SetValue(SelectedTextProperty, base.SelectedText); }
}
}
then:
<SLTest:TextBoxEx x:Name="MyTextBox2"
Grid.Row="1"
Width="200"
SelectedText="{Binding SelectedText, Mode=TwoWay}"
Text="This is some text." />

Resources