Reputation: 1026
I am making a library management software. I have separated management of database to a separate c# library 'DataAccessLibrary'. It contains DataAccess.cs:
public static event MyDelegate Event_AddBook;
public static void InitializeDatabase()
{
using (SqliteConnection db = new SqliteConnection("Filename=MyBooks.db"))
{
db.Open();
//Not necessary
}
}
public static void AddBook(Book book)
{
Event_AddBook.Invoke(book);
using (SqliteConnection db = new SqliteConnection("Filename=MyBooks.db"))
{
db.Open();
//Not necessary
}
}
public static ObservableCollection<Book> GetBooks()
{
ObservableCollection<Book> books = new ObservableCollection<Book>();
using (SqliteConnection db = new SqliteConnection("Filename=MyBooks.db"))
{
db.Open();
//Not necessary
}
return books;
}
}
When a book is added to the database, it invokes the event so that my YourBooks_View class (which displays the books) is notified that a book is added.
(NOTE: The book is added using a new window.)
In some other file, I add a book to the database like this:
Book b = new Book
{
Title = title,
Author = author,
Publisher = publisher,
ISBN = isbn,
Quantity = quantity.GetValueOrDefault(),
CoverImageLocation = CoverImageUri
};
DataAccess.AddBook(b);
The event 'Event_AddBook' is subscribed to by YourBooks class:
public sealed partial class YourBooks_View : Page
{
private BooksViewModel ViewModel { get; set; } = new BooksViewModel();
public YourBooks_View()
{
this.InitializeComponent();
DataAccess.Event_AddBook += new MyDelegate(this.GridView_AddBook);
}
private void GridView_AddBook(Book book)
{
ViewModel.Books.Add(book);
}
}
The code compiles without errors but at run time, when I add the book, this exception is thrown at the line YourBooks_View.GridView_AddBook() :
System.InvalidCastException HResult=0x80004002 Message=Unable to cast COM object of type 'System.Collections.Specialized.NotifyCollectionChangedEventHandler' to class type 'System.Collections.Specialized.NotifyCollectionChangedEventHandler'. Instances of types that represent COM components cannot be cast to types that do not represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface. Source=System.Private.CoreLib StackTrace: at System.StubHelpers.StubHelpers.GetCOMIPFromRCW_WinRTDelegate(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget) at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection
1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection
1.InsertItem(Int32 index, T item) at System.Collections.ObjectModel.Collection`1.Add(T item) at Bookshelf.YourBooks_View.GridView_AddBook(Book book) in C:\Users\Hemil\source\repos\Bookshelf\Bookshelf\YourBooks_View.xaml.cs:line 54 at DataAccessLibrary.DataAccess.AddBook(Book book) in C:\Users\Hemil\source\repos\Bookshelf\DataAccessLibrary\DataAccess.cs:line 44 at Bookshelf.NewBook_View.d__3.MoveNext() in C:\Users\Hemil\source\repos\Bookshelf\Bookshelf\NewBook_View.xaml.cs:line 137
I don't know what this means. Is there a way to solve this problem or is there a way to notify the view in some other way.
I am not so good in c#. Forgive me for any mistake or silly question.
EDIT: I am using Sqlite Database as provided in Microsoft.Data.Sqlite container.
EDIT: I followed this tutorial: https://learn.microsoft.com/en-us/windows/uwp/data-access/sqlite-databases. I am targetting the latest Windows 10 so I did not include Sqlite with my app
EDIT: I uploaded the whole source code to a public repository on gitlab here: https://gitlab.com/rahem027/bookshelf2
EDIT: I think i should have included BookViewModel source code as well. Well, here it is:
public class BooksViewModel
{
private ObservableCollection<Book> books { get; set; }
public ObservableCollection<Book> Books
{
get { return this.books; }
set
{
this.books = value;
}
}
public BooksViewModel()
{
books = DataAccess.GetBooks();
}
}
Upvotes: 3
Views: 2459
Reputation: 3808
The reason you get this issue is that you update the Main View's ViewModel
from the New View's thread. When you clicked the Add
button, the AddBook
event triggered, this is on the New View's thread, but you update the Main View's ViewModel
in the YourBooks_View
class. You should use the CoreDispatcher.RunAsync method to schedule work on the UI thread for the new view.
So just change your code of your GridView_AddBook
method with CoreDispatcher.RunAsync method to schedule work on the UI thread to add new Book
object.
Here is the modified code of GridView_AddBook
method:
private async void GridView_AddBook(Book book)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ViewModel.Books.Add(book);
});
}
Upvotes: 3
Reputation: 4465
the book object you are trying to add to the collection maybe attached to the database, When you perform an operation with the database make sure to Dispose its context ( that depends on which db provider you are using ).
So to work around that you can just try to copy the book object to a new instance and put that in your ViewModel Collection like so :
private void GridView_AddBook(Book book)
{
Book b = new Book
{
Title = book.title,
Author = book.author,
Publisher = book.publisher,
ISBN = book.isbn,
Quantity = book.quantity.GetValueOrDefault(),
CoverImageLocation = book.CoverImageUri
};
ViewModel.Books.Add(b);
}
Upvotes: 0