✅ Adding a delete function to Todo application
I followed the avalonia tutorial for creating a todo app, but it only has ways to add items to the list. I'm wanting to remove items from the list based on a delete button, and I'm not quite sure how to implement it. I attempted to copy and do the reverse of the add item button] and I attempted to copy the C# code as well. but when it comes to creating the RemoveItemViewModel.cs file, I'm getting a bit lost. so how can I create a simple code behind for my delete button to just remove the items from the list and from view?
415 Replies
Assuming you're using an
ObservableCollection
you can just call Remove
on the ObservableCollection
instanceDon't upload zips, put your project on GitHub and share the URL instead
At a glance, I don't think you need the
RemoveItemViewModel
at all
its needed for add since you need to give it a description, but for remove its probably just gonna remove the item selected in the main viewIn the TodoListView.axaml remove the binding to the RemoveItem in the MainWindowViewModel and instead bind to a new method in TodoListViewModel.cs
And in the TodoListViewModel.cs to remove checked items
^
ok so I implemented this, thank you by the way, however, when I ran the code and selected the items on the screen and clicked the delete button, this triggered. I'm curious if it has to do with my Database.cs file?
Np!
As you can see in the sample
We decrement
i
(i--
) instead of incrementing it (i++
)
In your code you're incrementing i
, which means i
would be outside the bounds of the list!oh I didn't even notice that!
ok so that fixed it, but now how can I have an empty "fake" database, or how can I use a json file instead of a "Database" to hold my entries and such. I'm still trying to learn working with json
like maybe turning my Database.cs file into 2 functions for workign with json that are callable? like an add function to write to json and a delete function to remove the item(s) from json
The reason why this for loop is decrmenting is imagine this scenario
We have a list
["Walk the dog", "Buy some milk", "Learn Avalonia"]
The indices are
[0, 1, 2]
We want to remove "Buy some milk" and "Learn Avalonia"
If the loop is incrementing and we start at 0
i.e i = 0
We first check the 0th index it has "walk the dog" so we skip it
Increment i
i
now is 1
We check the 1st index it has "Buy some milk" we remove it from the list, but what happens to items after it? the get shifted
So now "Learn Avalonia" after removal is at index 1
The list looks like
["Walk the dog", "Learn Avalonia"]
The indices are
[0, 1]
i
get's incremented
i
now is 2
We check the 2nd index, but our list only has 0th and 1st, because it got "shrunk" after we removed the item.
This would throw an exception (error)so we start at the end of the list instead so that we can decrement
i
to stay within the bounds of the list index?Exactly!
We remove from the end so that when the list items get shifted, we stay within bounds
For saving to a json file, you would simply call
var content = JsonSerializer.Serialize(Items)
, content is a string that you can write to the fileso quick question
Sure
looking at the code on my repo, is there a better way I can layout the code?
what I mean is
I have my MainWindow.axaml file. All this file does is exist as a blank screen and it hold the
Content={Binding Content}
within the <Window></Window>
tag. Then we go to the TodoListView.axaml
file which has all of my screen stylings, buttons, it's where the items in the list are displayed, etc. The two buttons (Add, Delete) in this file are bound to two functions that live in separate locations. My Add buttons bound function lives within MainWindowViewModel.cs
file and my Delete buttons bound function lives within my TodoListViewModel.cs
file. If I were to move them to the MainWindowViewModel and move my TodoListView.axaml stuff to my MainWindow.axaml, I'd reduce the project by 2 files. I'm fine with keeping the AddItemView and AddItemViewModel. I don't fully understand the code behind here, but I know that it's creating new items within the collection. And with my Database.cs file, is there a way that I can get rid of that file altogether, and put my json add/delete code behind in with my add and delete buttons?
Questions:
- 1) Would it be better to move my stuff in my TodoListView.axaml file to my MainWindow.axaml file?
- 2) Is there a way to re-write the code behind so that if we were to do question #1, then I can have both functions in the MainWindowViewModel instead of them living in two separate places?
- 3) if we do #3, can I also incorporate my json add/delete functions into the Add/Delete buttons code behind so that it's all together in one place instead of everything being spread across multiple files?
To be more specific
I'm just trying to find a better way to write this code so that it's easier for me to understand, and everything isn't so spread out between so many files. MVVM is awesome and I like it, but this seems a bit excessive for a simple Todo Application1. Yes you can move the view and viewmodel implementation from
TodoListView.axaml
and TodoListViewModel.cs
to MainWindow.axaml
and MainWindowViewModel.cs
respectively, but the reason it's implemented that way is if you were to add more operations to the todo list view (sorting, searching, etc.) the MainWindowViewModel.cs
would be clutered. And switching between different views would be a bit more complex.
2. I'm not sure what did you mean by both functions but assuming you mean TodoList and AddItem, it's again possible but it would add more complexity to how you switch between a view for deleting and adding (Unless it everything would be in a single view).
3. It's possible, but using a seperate class to handle the saving/loading would be better, because you can change how the database
(which in this case is a json file) behaves without affecting the View or ViewModel, you'd simply need to call the Load
or Save
method this class provide and Load/Save the database.
Unfortunately MVVM adds complexity to simple apps, but it's really awesome when you try encounter more complex situations!1. Yes you can move the view and viewmodel implementation from TodoListView.axaml and TodoListViewModel.cs to MainWindow.axaml and MainWindowViewModel.cs respectively, but the reason it's implemented that way is if you were to add more operations to the todo list view (sorting, searching, etc.) the MainWindowViewModel.cs would be clutered. And switching between different views would be a bit more complex.ok so I'll leave it as is. I do want to add a date and time column to the main view so that it can start being sorted at least by date and by time (2 separate sort things).
2. I'm not sure what did you mean by both functions but assuming you mean TodoList and AddItem, it's again possible but it would add more complexity to how you switch between a view for deleting and adding (Unless it everything would be in a single view).Currently my AddItems which is the function that adds the new item to the collection, lives in both the
AddItemViewModel.cs
which pushes it to the parent function which lives in MainWindowViewModel.cs
file. There are two separate functions for adding a new item to the collection list. Then my delete items function, which you wrote earlier in this thread, lives in the TodoListViewModel.cs
file and I wanted to put them all together in the same file, but if it's meant to be separated like that for a reason, then I don't want to mess that up which I would assume blends in with 1.
as I add complexity, it makes it less jumbled.
3. It's possible, but using a seperate class to handle the saving/loading would be better, because you can change how the database (which in this case is a json file) behaves without affecting the View or ViewModel, you'd simply need to call the Load or Save method this class provide and Load/Save the database.With this being said, I like easier to use and easier to understand. I'd like to re-write my
Database.cs
file to handle my json operations of adding a new item to the json file, and removing the selected items from the json file.
I'm just confused on how to go about implementing the changes I want. Adding Date and Time to the items so that they can be sorted by Date and Time. I can make the buttons with no problem, but the rest of it will be hard for me. This is where I want to start
and I'd honestly like to switch from a dock panel back to a grid. is that plausible?
I switched from DockPanel to Grid and updated the repoThe idea behind MVVM is to enforce separation of concerns, basically each part of the app (Model, View, ViewModel) handles it's own thing and you can change the underlying code for them without effecting one another that much.
You can rewrite your
Database.cs
to have a Save
and Load
method, when the application starts you call Load
to populate your TodoListView
and when you Add
or Remove
you update the Database's TodoItem
list and call Save
And yes you can any container be it Grid, DockPanel, StackLayout, whatever suits your needs!You can rewrite your Database.cs to have a Save and Load method, when the application starts you call Load to populate your TodoListView and when you Add or Remove you update the Database's TodoItem list and call SaveI would like to have this, but would I need to get my sort button and having date and time added into my items first?
Are Date and Time seperate here?
yes so like this is where I'm confused
right now I Have this
You can add to your
TodoItem
model, and simply bind on these two properties
I am confused on how I'll get 3 separate columns within my ItemsControl so that I have a column for the Date, one for Time, and one for the note itself
I need to get 3 columns within my ItemsControl, and my sort button needs to have a pop-open window with select boxes that will show a check box list of ways the user can sort their todo items, and then an update button within that popup box that will refresh the screen and display according to the new parameters
with that being said, I'm assuming that I'll need a
SortView.axaml
and a SortViewModel.cs
file?You can have something like
But if you want something like a table you could use a DataGrid, which is meant for tabular data
Sry, I'll be afk for a bit!
well I've thought about it 2 ways.
Way #1
- Sort button with a pop out screen. This pop out will show a list of checkboxes that have a different way to sort the list of todo's with an update button to reload the screen with the new sort parameters and show the information accordingly
Way #2
- Clickable column names. This will look like
and when you click the Date, it sorts the information by the date column and so on
so something like this, but with better alignment lol I can't figure out the alignment with the columns, but I'm sure it's not a grid?
Grid would work nicely for this, but you could also just use DataGrid package if you want something like a table
how would I use the DataGrid?
You'd need to install it first
https://docs.avaloniaui.net/docs/controls/datagrid/
And you can have something like this
You can add more columns in
<DataGrid.Columns>
DataGrid | Avalonia UI
The DataGrid control is a control that displays data in a customizable grid.
Make sure to install a version matching your Avalonia's package version so 0.10.21
and those few lines of code would iterate through all of the entries and display them correctly?
Yeah pretty much
You can even combine it
DatePicker
and TimePicker
controls and make it easy to modify dates and timesok cool. give me a bit to try this out. I'll ping you when I'm able to get to it
is it this package?
Yup but make sure to install the 0.10.21 version @mekasu0124
yep lol noticed that when I tried to add it but didn't do it right
ok so I installed the packaged, and used the code example above, and now when I hover over
<DataGrid
it tells me that it's Unable to resolve type DataGrid from namespace://github.com/avaloniaui Line 34, position 4
I updated the repo if you can take a lookYou need to add the DataGrid style
And then run the application
You can add the style in app.axaml
ok so now my only problem is figuring out how to style the headers of the grid, and making it scrollable content
and I have one more column than I should have. There should only be 3 columns, but there are 4?
The default column's width would be just enough to fit the content so to stretch it all the way you would do
<DataGridTextColumn Width="*" Header="Date" Binding="{Binding Date}" /
Width="*"
means fill as much as possible, if you add Width="*"
to the three columns, it means each column gets 1/3 of the the free space.gotcha gotcha
To style the headers you can just do
There's no background property for them?
There should be
It's not working for me
oh I had the selector wrong
Btw if you want to style the row you can use
Selector="DataGridRow"
ok I've fixed the UI's and made the app bigger. Instead of 200x300 it's now 400x500 lol more room to work with
I would just want to style the hover color so like when they hover over an item, the background color becomes green with white font, and same for when they select an item
For the item selection
The selector here means seelcted a Rectangle control named
BackgroundRectangle
which is in the template
of a :selected
DataGridRow
The value for Fill
here is basically the same as Background
For hover you would just change the :selected
state to :pointerover
Now for the actual text of the row, you'd just select the DataGridRow with both :pointerover
and :selected
states and set the Foreground propertyforeground isn't a property of it
Foreground is a property of
DataGridRow
but not Rectangle
so you need to have two separate style selectors for background (Fill) and foregroundgot that put in. Ok so now. Re-writing the Database.cs file. I've updated the repo with all these changes so that the code is fully updated. I would like to do this now if that's alright https://discord.com/channels/143867839282020352/1141209129654943754/1141553787589378159
Mekasu0124
You can rewrite your Database.cs to have a Save and Load method, when the application starts you call Load to populate your TodoListView and when you Add or Remove you update the Database's TodoItem list and call SaveI would like to have this, but would I need to get my sort button and having date and time added into my items first?
Quoted by
<@1032644230432182302> from #Adding a delete function to Todo application (click here)
React with ❌ to remove this embed.
https://github.com/mekasu0124/Todo
or should I redo the code behind for the ViewModels first?
You don't really need to do anything with ViewModels yet, until you add the logic for loading and saving json files.
What you would want to do in your code is this:
1. Check if the database json file exists
2. If it exists
Load
it into the application and populate the Todo item list using it's content
3. When a user adds/removes a todo item update the database and Save
it to a json file.yea so here comes the fun part
1. How and Where do I check if the with when the app loads
so far I have in my
database.json
file exists?
2. How do I load the data if it does exist?
3. How do I create the file if it doesn't exist?
All of this will need to happen on launch so it needs to happen before anything else Database.cs
file
but this file, Database.cs
would need to be called before the program showed an image1. You'd use File.Exists
https://learn.microsoft.com/en-us/dotnet/api/system.io.file.exists?view=net-7.0
Keep note of microsoft's api docs, it really comes in handy when you want to check something
2. You can read all contents from a file with
File.ReadAllText
https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalltext?view=net-7.0
Ideally you'd use File.ReadAllTextAsync
but getting into Async
would complicate stuff more.
3. You don't need to create the file until you actually try to save somethingok so then how do I import json, save, and load? This will be a very much step one learning for me. I've not yet done anything like this in C#
Saving is simple
JsonSerializer.Serialize(*The database items go here*)
this serializes the object to a json string.
And to deserialize you'd do JsonSerializer.Deserialize<*The type goes here*>(* json string goes here*)
this will "try" to deserialize the json string according to the type you specifiedok here's my start
Well, you don't really need to check if the file exists before saving, you can just overwrite the old file
but it wouldn't be overwritten? Just items either added or removed from it? right?
so like the program would start with an empty json file. When I click "Add Item" and fill out the form with the Date, Time, Description it will write that information to the json file. If I do that 3 more times, I now have 4 entries. If I select an entry and click delete, the program would need to remove that item from the json file but keep the other 3 entries
so like the program would start with an empty json filethis is where I would start, right? So how would I make it create an empty json file to start the program with, or how would i start the program without the json file, and then it would add the entry and create the json file upon the first item being entered?
so like this file would need to push the information to the Database.json file
Well, deleting a specific json object from a file is complicated, so to get around this an easier approach would be like this
Load the database if it exists into memory (into the app)
When a new item is added, serialize all todo items in the current in-memory database (the todo item list) to the save file (if the file exists overwrite it because the serialized data has both new and old entries)
The same goes for when reomving an item
ok cool. so not going to lie, I'm still lost. I have no idea what my first step is or how to implement it
Your first step is checking if the database file exists and loading it, a very simple approach would be doing this logic in the Database class constructor
like that?
Kinda, but not fully right
Constructors are special methods that get invoked when the class is instantiated
new ...
Constructors definition MUST have the same name as the class and they don't have a return type
You also need to define the type here Deserialize<**>
If a json file looks like this
Deserialize<string[]>
Would work, because the json file is an array of stringsI'm confused
?
For example this is the constructor
In the AddItemViewModel
Tbh, I think starting with a GUI app adds way more complexity if you're starting out with c# 😅
I agree, but I'm giving it my best foot forward on learning it. So far, I've gotten down the concept of building a console application. I can do that kinda kinda kinda sorta easy. I started one here https://github.com/mekasu0124/TheAdventure. It's just taking those concepts and going into a GUI that makes it difficult because it's no longer just regular C# class files that I'm balancing. It's throwing in the MVVM and axaml files, etc
so I started over. I have
so you said my next move is to check and load the file information if it exists right?
it doesn't like the
string[]
Pretty close! just need to read the file content first and pass the type here
JsonSerializer.Deserialize<*****>
between <>
, string[]
ofc means we are reading a json array of strings, which is not quite right for a database for todoitems with date and time properties.this is getting frustrating lol
I'm really not understanding what I'm doing wrong....
is it this instead?
I reduced it lol 😂
I just don't know what would go in place of
null
Perfect, the reason why the first doesn't really work is:
1. File.ReadAllLines(dbFile) returns string[] JsonSerializer.Deserialize wants a single string (or a stream but that's for another day 😅 )
2. JsonSerializer.Deserialize(dbInfo, List<string>); there is no method signature that accepts these arguments
You still need to make a small modification,
List<string>
means the json file is an array of strings NOT todo item objects.
You also can't pass null
If you want to reduce it you can do
Though only executing the deserilization code when the files exists is better1. File.ReadAllLines(dbFile) returns string[] JsonSerializer.Deserialize wants a single string (or a stream but that's for another day 😅 )so that's why I'm using
File.ReadAllText()
? RIght? So it reads all the text instead of multiple strings at once
2. JsonSerializer.Deserialize(dbInfo, List<string>); there is no method signature that accepts these argumentsso that's why it was throwing an error although I do not understand the difference between
Deserialize()
versus Deserialize<>()
You still need to make a small modification, List<string> means the json file is an array of strings NOT todo item objects.so change the list to a string cast, right?
List<string> info = JsonSerializer.Deserialize<string>(dbInfo);
You also can't pass null
If you want to reduce it you can do
var dbInfo = File.Exists(dbFile) ? File.ReadAllText(dbFile) : "[]";
so this way if the file doesn't exists, it returns a new serialized string?
but when I put the "[]"
in for null
it throws an error on the deserializer
Though only executing the deserilization code when the files exists is betterso it's better to have the if/else instead of a one-liner so this is where I need to be then, right?
so that's why I'm using File.ReadAllText()? RIght? So it reads all the text instead of multiple strings at onceYup, spot on
so that's why it was throwing an error although I do not understand the difference between Deserialize() versus Deserialize<>()The difference is drastic
Deserialize()
is just a method call Deserialize<>()
is a method call that takes a type parameter, you can read more on it here, it's essential to working with C#
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics
so change the list to a string cast, right? List<string> info = JsonSerializer.Deserialize<string>(dbInfo);Well, it should be
JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
because you want a List/Array of TodoItems, you don't want to just read an array of stringsok I've got that bookmarked for reading later when I go to lay down. The only problem I'm having right now is finding the correct variable type to set the deserializing equal to
List<string> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
it doesn't like this, and I'm confused on it because I thought it needed to be a list as I'm deserializing the information into list...or does it not need to be saved to a variable at all?It does need to be a list
List<string> means a list of strings
List<int> means a list of ints
right
So to deserialize it you need a list of todo items
ohhhh ok so since we're deserializing to
List<TodoItem>
the variable has to match itRight, a short hand way is just
var ... = ...
var infers the typeso I can choose between
var
and List<TodoItem>
Yeah, you can fully write the type or just let the compiler infer it, it sees that
Deserialize
returns so it knows the variable's type is List<TodoItem>
If you wrote python before var is not the same as python week typing where you can just do
items = []
items = "hello world"
right. python doesn't have the variable type like c# does
ok so I'm here now. What's my next step?
Next step is keeping the deserialized data available so when MainWindow loads it can get the items from it
so I need to make it IEnumerable like I did here?
You just need a way to get the data
Be it
IEnumerable<TodoItem>
List<TodoItem>
TodoItem[]
ok apologies. I definitely fell asleep at my computer and then woke up with 20 minutes to get to work lol so I can use any of those 3 types of ways to get the data?
Here's what I have now. So what's my next step?
well you need to expose the database that was read from the file somehow. right now its only a local variable that gets thrown away after the constructor runs
so pass the
items
variable to a function like
but it would be something like
right?something along those lines
but thats a method (why does it take items in)?
your database probably wants to expose the list of known items as a property
I didn't know if it needed to be passed the items from the IENumerable<TodoItem> items = new List<TodoItem>(); or not
so if I don't need a method, then how would I do it?
IENumerable<TodoItem> items = new List<TodoItem>();
is an empty list.
it has no content in it, and its temporary and gets deleted after that method ends
Do you not know what a property is in C#?
I'm imagining your expected result to be something like...
yea I guess so. idk. maybe I just need to go back to console based applications for the time being because this is making me feel beyond stupid
I have no idea where to put that code, or how to use it. I went through the basics of C# and I can make a console based application using regular C# files with classes and such, but I'm not that great at it and then going into avalonia MVVM just made it so much worse. idk.
this isnt an MVVM problem, its just about the basics of C#
if we want a
Database
instance to have a property, how do we define that?Database db = new Database();
right?that would be creating the new database instance
but how do we then make
db.TodoItems
work?using something like this?
Something like that indeed
getters and setters for the information?
public string Description { get; set; }
is a string property called Description
so your Database
should have an IList<TodoItem>
property, imhoI've updated my repo with my current files and how they sit https://github.com/mekasu0124/Todo but I don't see an IList anywhere. The only thing I see is the IEnumerable in my Database.cs file, I have a
public TodoListViewModel(IEnumerable<TodoItem> items) { Items = new ObservableCollection<TodoItem>(items); }
in my TodoListVIewModel.cs file, and yea. idk. will you take a look at the repo and see if you can help me find where I'm supposed to be and what I'm missing?GitHub
GitHub - mekasu0124/Todo: my todo app
my todo app. Contribute to mekasu0124/Todo development by creating an account on GitHub.
You're in the right place already
just add in your property
And just FYI,
List<T>
is a class that fulfills a lot of interfaces, including IEnumerable<T>
and IList<T>
oh ok. so my next step is to
your database probably wants to expose the list of known items as a propertyand to do that I would do something like this?
well thats the usage
because the application is going to start with a blank json file, and has a chance to start with an empty json file if the user removes everything in their todo list, and then closes the app
ie, thats how you would use it when its done
yep
ok so forgive me. What's my next step now with pushing the existing json file contents (or a blank json list) out of Database.cs and to where it's supposed to go? because if I'm not mistaken, after it's pulled from the json file, it then goes to the TodoLIstViewModel.cs file so that the list can be rendered in the screen since MainWindow.axaml has
Content={Binding Content}"
You're not at that stage yet
oh ok
your current code for the database loads the file, then throws away the content
we need to make it stop doing that first
do you know what a class member is, and how its different from a local variable?
I'm going to say safe side and say no
how do we access the value here?
Console.Write(test.value);
I believeOkay lets try that
is there a compiler bot in this server?
Pobiega
REPL Result: Failure
Exception: CompilationErrorException
Compile: 720.937ms | Execution: 0.000ms | React with ❌ to remove this embed.
O_o
oops, errors
how did you do that?
!e
anyways, check the error message
what is it saying?ok one second
'Test' does not contain a definition for 'value'because
value
is a local variable in the constuctor. once the constructor is over, it ceases to existoooh ok. I got you
so I have to put it into a function?
No?
oh
a property
Pobiega
REPL Result: Success
Console Output
Compile: 642.090ms | Execution: 49.727ms | React with ❌ to remove this embed.
I'm honestly confused. you're able to run the code as it sits, but if I try to do it in my editor, I get a million errors
nevermind I fixed it
had to do it this way
but that makes sense. you have to get the information in order to use the information
the property means we can "save" the information
so its not discarded when the method ends
so I would need to do something like this, except List won't work
almost
which local variable contains the actual data?
items
incorrect
no sorry info
that is an empty list
correct
but why can't we use
List
?because I didn't give it a type inside of the
<>
so public List<string> Items {get;}
Right. So give it a type info 🙂
is
string
the right type thou?no because it's a list of strings
not as far as I can tell
it
it's a json object
TodoItem
maybe?oh yea. missed that part. my fault
so then question
remove the last line of your constructor
IEnumerable<TodoItem> items = new List<TodoItem>();
does nothingif
List<TodoItem> info = ...
has the information, then what's the purpose of the line IEnumerable<TodoItem> items = new List<TodoItem>();
?or well, nothing useful
there is no purpose
its entirely useless
so it's not needed
not at all
it just declares a new blank list, then throws it away
so I'd replace it with Items = info
ye!
because we opened the file, then read the information, then stored that information to a variable that has a getter so that it can "get" the information instead of allowing it to be discarded
am I on the right track?
kinda
but the getter isnt the important part
its the fact that the property is a class member, not a local variable
public List<TodoItem> Items { get; }
<- class member
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
<- local variable
right?yup
ok cool. that makes sense
anything declared inside a method is a local variable
anything declared on the top-level class scope is a member
kind of like the
self.variable = "some_info"
in python where the variable is declared in the def __init__()
method as opposed to a variable being created in a method of the class so like
yes
ok cool cool
but python doesnt have declaration of members as such
so now that we have the information in a class member, we can use it else where, right?
since y'know, not a typed language
right
yes!
ok so since the items coming from the json file are going to be a Date, Time, TodoItem; I then need to update my TodoItem.cs Models right? it's in the Models folder https://github.com/mekasu0124/Todo/blob/main/Models/TodoItem.cs
hm?
you're gonna read three things from the same file?
or do you mean that a TodoItem should also have a datetime?
right now it only has a description and the
IsChecked
boolthe display has 3 columns. Like in this image. Except it's not CheckBox anymore as it's been changed to a DataGrid. It's displaying 3 separate things, one to each column. The Date the TodoItem was created, the Time it was created, and the TodoItem description itself
jesus I almost died. foot fell asleep, got up to grab water from the kitchen, fell and just barely dodged a sharp table edge with my head.
don't die
that's no bueno
yeah good call
funny as hell, but don't die. that would be absolutely terrible
but to answer you question, the json file is basically going to look like this
so in C# we have a data type called
datetime
. its a date... AND a time.
do you wanna use that, or a DateOnly
and a TimeOnly
?
i'd rather keep the 3 separate if that's easily implementable because the column headers are going to be clickable to allow for sorting so like if someone has over 50 notes, they can sort by date, or by time
sure
I would like for it to sort by date, and then by time so like if they click the date tab, it sorts by date and if they click the time tab, then it keeps the date sorting and then sorts by time so that all the dates are still together, but idk how plausible that would be
an example would be
idk how plausible that would beI cant put it better myself 😄 I know that sort of sorting behaviour exists in stuff like... Excel but idk if the avalonia datagrid has support for it
we'll just stick to simple for now. The date tab can sort by date independently and the time tab can sort by time independently from the date tab and vise versa
okay
so yeah, make your TodoItem class represent the structure you want
I have this
thoughts?
bad.
oof
string
learn me lol
why string?
date and time isnt strings
DateOnly
and TimeOnly
because tbh I haven't learned datetime yet, or how to set those like that
no time like the present
ok so I changed them to
but they have red lines under the type casts
am I missing an import?
hm, whats the error?
CS0246: The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?)
huh. what .NET version is this?
it was added in .NET 6
0.10.21 I believe
oh
um
where do I find that
I believe it's 7 but could be 6 since the project was done on 0.10.21 as they haven't updated the docs yet for 0.11
oh I just needed
using System;
as an import?makes sense
a lot of types are in that namespace
ok so here's where I'm at
seems good
ok so here's where the item get's added when the user clicks "New Item" on the main screen https://github.com/mekasu0124/Todo/blob/main/ViewModels/AddItemViewModel.cs
mhm
so, you'll need to add a way for the user to specify date and time
both to the add view and the viewmodel
I did that on the axaml page, but unsure of what I need to do next
which tbh I'd rather have that auto populate if possible
probably doable too.
no nevermind
I'd rather leave it editable so that if they want to create a future tense note
so, you made them as
TextBox
could I make those with clickable icons? Like the date one would have a date icon on the right side to select a date and same for time?
Sure
https://docs.avaloniaui.net/docs/controls/datepicker
https://docs.avaloniaui.net/docs/controls/timepicker
is what I would use
ok so I implemented that, and am updating the repo now
https://github.com/mekasu0124/Todo/blob/main/Views/AddItemView.axaml it's been updated if this visual helps
I see the styles.. but you seem to have removed the pickers themselves?
just wish I knew how to center align the text on the DatePicker like it is on the TimePicker
oh sorry there they are
and change their foreground color to green
¯\_(ツ)_/¯
I'm a backend developer, I know nothing about styling
I can worry about it later. so now that I've updated my UI to allow the selection of date and time and item entry, when the Ok button is clicked, it's bound to
so what are my next moves?
well, the view model needs properties and fields to hold the data for the pickers
and how do I set it to check if there's a selection on date, time, and that an entry has been placed and if not, then show an error message?
the same way its done here for the description, I imagine
when you guys display code here, and it has the green
+
and the red -
how do you do that?instead of
cs
use diff
so here's what i've done thus far, but in my bottom x lines where I get and set, it doesn't like my
ref
things. It has a red line under date and timeremove the
{get;set;}
from the top declarations
they should be fields, not properties
yeok cool
so then I should probably do
right?
seems reasonable, yep
oops
edited it
hm, that
WhenAnyValue
thing will probably not work
It looks like the first is a selector, and the second is the validation functionye 😄
well quesiton
nevermind
so what can I use instead? or should I rewrite it
does that give you a hint?
because this
points to this
so do this for each item
yup
then Im thinking there is probably a way to combine them
so why doesn't it like the
IsNullOrWhiteSpace
on the date and time?
there's not definition for it, so how would I do that instead?
you have my interest lol
i'd rather have a var entryOk
and then push the entrythats what we are trying to make
bet
and yeah,
NullOrWhiteSpace
only makes sense for stringsright
DateOnly etc cant be null, and cant be... "whitespace"
since its not a string
so since we're using Picker's, how would we check that the value has been changed from the default?
well, looks like
TimePicker
has a ... TimeSpan? SelectedTime
property
thats a bit weird if you ask me, since timespans usually indicate a duration
but whatever, we can use that
so change your property for time to be TimeSpan?
so we can bind it
then in your validation, check that its not null and not default
then we create a TimeOnly
object from the values in the timespanin the cs or axaml?
because you lost me lol
okay so
the timepicker control works with a
TimeSpan?
as its value property
it doesnt know about TimeOnly
almost 😄
oof
the validation needs to be fixed, and I hope you changed the property below the constructor too
im just gonna clone your repo real quick
just did that lol
but I'm not sure what to do about the validation
thats not what I wrote :p
yea I fixed that typo
hm
that works fine on my machine
but that's the two error messages on
HasValue
and Value
you're using rider lolyes
but that doesnt matter here
since its the same compiler
i don't know honestly
I have exactly what you have
ooh
I see the issue
your types are not nullable
they must be
TimeSpan?
, not TimeSpan
and Date
should be... DateTimeOffset?
so I did
public DateOnly? Date {}
and public TimeOnly? Time {}
and that cleared it upye
and added in the var okEnabled with the concats
should I do this? and if so, then what would replace
descriptionOk);
?okEnabled
ofc
but it wont work just like that
Date and Time have different types than what we wantok what am I missing?
ah
so we need to do some conversions
ok I'm listening reading
lol
first, lets add a method for creating the result. its gonna get messy inside a lambda
ok I put it at the bottom
oh just kidding
it doesn't like it boss
look where you placed it
count them braces, boi
in the same place you did....
nah you didn't
you put it under the Ok and Cancel and so did i 😭
check the braces
mine is not inside the constructor
yours is
oh
sneaky little bastard
otay ficked it
and I need to have date and time inside with description right?
right
but we need to convert it first
okies
lol imma chickadee
hint:
DateOnly
and TimeOnly
has some very convenient static methodsDate = (DateOnly)Date?
check that hint again
😔
😕
idk but now I have another problem
so not only does it not like Date and Time, but it's throwing an error with this.RaiseAndSetIfChanged
should I convert them to strings instead?
no lol
rip
it doesnt like Date and Time because they are the wrong types
and
DateOnly?
should be DateTimeOffset?
you didnt make the fields at the top nullable
TimeSpan
and TimeSpan?
are not the same type
right?
yup
I dislike the names thou. I'm a bishop in the church of the "private fields should be prefixed with an underscore church", but thats a side issue
like that?
yes! 😄
but then they would need to be readonly
no
so, you should have no issues outside of your
CreateTodoItem
method nowyou didnt use the rename tool
so you have to fix the names at the bottom too
I saw that and ficked it
good
okay, so lets get to converting
ok 😄
this will be done inside the
CreateTodoItem
methodright
Pobiega
hint:
DateOnly
and TimeOnly
has some very convenient static methodsQuoted by
<@105026391237480448> from #Adding a delete function to Todo application (click here)
React with ❌ to remove this embed.
yep. still a little lost there
blame python
and js
ohhhh so parse it
no, its not a string
or would it be like
var convDate = new Date(_date);
close
but you didnt read the hint 😛
I can smell the bacon
gr
what are static methods?
"a method that keeps only one copy of the method at the Type level, not the object level"
so like Class.method
"only one copy" seems a bit weird, but yeah.
exactly
so what potentially useful static methods do DateOnly and TimeOnly have?
blame google https://www.google.com/search?client=opera&q=what+is+a+static+method+c%23&sourceid=opera&ie=UTF-8&oe=UTF-8
parse, tryparse, fromdatetime
fromdaynumber
that third one seems good, no?
fromdatetime?
considering we have a
DateTimeOffset
and we want a DateOnly
what about for TimeOnly? what method stand out as useful?DateOnly newDate = DateTimeOffset.Parse(_date);
we have to convert it?
parse?parse is for strings
think about the current type you have
what type do we currently have our time in?
well we have to convert from DateTimeOffset to DateOnly and we have to convert from TimeSpan to TimeOnly....
okay
and what static methods did
TimeOnly
have that seemed relevant?
and no parsing
grrrto convert from DateTimeOffset to DateOnly we'd have to do
DateOnly newDate = DateTimeOffset....
somethingnot really
i feel dumb
var date = DateOnly.FromDateTime(_date.Value.Date);
I literally would have never gotten that
so
really?
really
DateOnly.FromDateTime
when you have a DateTimeOffset
?
okay, time is easier
show me how to do the same for timeok
I feel really proud of this one
but you're gonna hate it
🥰
.. yep, you're right
I hate it
😁
TimeOnly.FromTimeSpan
just seems....
BETTERthat wasn't an option that showed up when I pressed the
.
😭cause you wrote TimeSpan
not TimeOnly
we HAVE a timespan. we WANT a TimeOnly
oh
sorry
var time = TimeOnly.FromTimeSpan(_time.Value.);
idk the last partno lastpart
just
_time.Value
?yes.
because
_time
is a Nullable<TimeSpan>
aka TimeSpan?
damn. I felt really proud of what I did with time lol
right
there we go.
doesn't description need to go at the end?
nope
order doesnt matter with initializers
oh ok
so what's the next step. I feel confident ^_^
i dunno
testing it?
😄
it's gonna error
so we created the database and fixed the TodoItem.cs model and now we've created the code to add the new entry to the database.json file, but it's going to error because of this
"because of this"?
now we've created the code to add the new entry to the database.json fileno we didnt we added code to add a new entry to your list
oh
not the json file
missing that part then
we never write to the json file, currently
so we need to fix where it's added then
Discord is being stupid on my computer. Idk what’s going on with it, but it is not wanting to load now and my phone took forever to connect
And now it’s taking several seconds for my messages to send
I think that’s my sign I need to go to bed anyways. It’s 3:40am my time and I have to be up at 12 for work. I’ll be back later tonight but I know that in the main window view model cs file, it also has an add method and I believe that’s where I need to work on next so that it gets added to the json file
discord shit its pants for me too.
ok so I'm back and I have a question. I was looking back over my Database.cs file, and I remember Kouhai saying something about having a save and load method in that file. They said that I would use
var content = JsonSerializer.Serialize(items)
- https://discord.com/channels/143867839282020352/1141209129654943754/1141542066241941605
then it was
You don't really need to do anything with ViewModels yet, until you add the logic for loading and saving json files. What you would want to do in your code is this: 1. Check if the database json file exists 2. If it exists- https://discord.com/channels/143867839282020352/1141209129654943754/1141626211030802533 We've already done the first one in the class constructor in Database.cs by doing so where did I, or do I, do #2 and #3 which is the Load and Save? And furthermore on the save, that's where we'd create the file as they said hereLoad
it into the application and populate the Todo item list using it's content 3. When a user adds/removes a todo item update the database andSave
it to a json file
3. You don't need to create the file until you actually try to save something.- https://discord.com/channels/143867839282020352/1141209129654943754/1141628451074351205 so now I'm looking back at the Database.cs file, and I'm wondering although we checked if the json file exists, and if it does we load the information, is that the Load part of it? If yes, where's the Save?
there is no save part yet
you have not written it 🙂
and yes, what you have in the ctor is effectively "Load"
but it should be moved out of the constructor and into an async method, ideally
but it should be moved out of the constructor and into an async method, ideallyso how would I do this?
I'm sure you could google that.
but its also not the most crucial thing
so leave it for now
ok so leaving it for now, how would I do the save function because this function has to create the database file if it doesn't exist (I can use an early escape for that) and save the information
well step 1 is making a new method
maybe call it...
Save
?
🙂what about this?
kinda. you dont need to create it before writing to it thou
so that entire check can be removed
it'll create the file on it's own if it doesn't exist?
yup
remove the
async
btwoh lol I was gonna be sneaky and just make it async already haha okie dokie
nope, bad idea
I know you said to leave the loading part in the constructor as is, but I'm curious why not go ahead and move it to a function if that's how it's supposed to be?
sure, go ahead
I could, but I don't exactly know how to call them
Read the error for why it doesnt like it.
Property or indexer 'Database.Items' cannot be assigned to -- it is read only
indeed.
So make it not readonly? 😛
{ get; private set; }
for exampleI wasn't taught this in the basics that I went through. I'm sorry
ok so now I have to figure out how to call my Load function in my constructor, right?
sure
would this work?
yup
ok question. Write once, re-use 100 times. Could I move my
string dbFile = "database.json";
from both methods to my class constructor and it work appropriately?not the constructor
but to the class itself
slap a
const
modifier on it too
oh, and private
so I could do
no
well yes, but its bad
so dont do that
ok so what would I do instead
make it a
private const string
on the class itself
and then you dont need to pass it aroundyes
ok so here's what I've got
seems good.
ok so I have:
- Load/Save functions in Database.cs
- TodoItem.cs Model with the DateOnly, TimeOnly, and string description
- We've created the var descriptionOk, var dateOk, var timeOk, var okEnabled, and did a CreateTodoItem() for conversions returning the new items in a TodoItem { }, and updated the get/set methods for Description, DateTimeOffset?, and TimeSpan? in the AddItemViewModel.cs
so what's left is the MainWindowVIewModel.cs and TodoListViewModel.cs files
so in my MainWindowViewModel, i would make these changes, right?
no
nononononon
no.
oh
no? no.
You already call load when the database is created, before its injected into the first viewmodel
do NOT call load on an already loaded database
ok that makes sense. so what would I do with that line?
change it to access the items.
yep
ok and wouldn't I want to do
ViewModelBase _content;
instead of ViewModelBase content;
?sure,
ok so we're here now. The next one is the
public TodoLIstViewModel List { get; }
which I'm pressuming I'd leave the samesure
so with these two things. I'm pressuming that line 1 is left as is, and the AddItem() has to be updated to match working with the json file and calling the save method, right?
something like that
ok so what are we thinking? I wanna see if I can take your idea and start figuring the code out first lol
AddItem
should probably also add the item to the databases list, then call a save
actually, perhaps its time to redesign this a bit.ok I"m all ears
You currently actually keep two independent lists of your items
the one inside your viewmodel, and the one in your database
Im thinking the one inside your viewmodel is... superflous and could just use the database one directly
ie, they should both be the same
idk what superflous means, but ok I'm down to only deal with one list
I'll warn you that I'm not a GUI developer and have no idea what is best practice when it comes to observables and MVVM
but it feels silly manually keeping two lists in sync
ok so do I need to change anything between
- /Services/Database.cs
- /Model/TodoItem.cs
- /ViewModels/AddItemViewModel.cs
yes 🙂
quite a bit
ok so before we do that, question
your database should expose an ObservableList<T> instead of a List<T>, for one
but its really late here, so I'm gonna bow out for now
would it be more beneficial to start the project over with a fresh project?
ok no problem 🙂 thanks for your help
Probably not.
its a minor change, it will just touch many places
maybe 15-20 lines at most, I think
ok that's not too bad, but I'll let you go and get some sleep 🙂
I messed up the repo and had to start it over. just an fyi
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.ok so I got the problems figured out, and the application is now finished. I have attempted to close this thread twice, but it won't close and states that the application did not respond so I've no clue how to close this channel past that. Thank you all for your help @Kouhai /人◕ ‿‿ ◕人\ and @Pobiega ❤️
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.