C
C#11mo ago
Mekasu0124

✅ 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
Kouhai
Kouhai11mo ago
Assuming you're using an ObservableCollection you can just call Remove on the ObservableCollection instance
Pobiega
Pobiega11mo ago
Don't upload zips, put your project on GitHub and share the URL instead
Mekasu0124
Mekasu012411mo ago
Pobiega
Pobiega11mo ago
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 view
Kouhai
Kouhai11mo ago
In the TodoListView.axaml remove the binding to the RemoveItem in the MainWindowViewModel and instead bind to a new method in TodoListViewModel.cs
<Button DockPanel.Dock="Bottom"
-- Command="{Binding #parent[Window].DataContext.RemoveItem}"
++ Command="{Binding RemoveSelectedItems}"
Content="Delete"/>
<Button DockPanel.Dock="Bottom"
-- Command="{Binding #parent[Window].DataContext.RemoveItem}"
++ Command="{Binding RemoveSelectedItems}"
Content="Delete"/>
And in the TodoListViewModel.cs to remove checked items
public void RemoveSelectedItems()
{
for(var i = Items.Count-1; i >= 0; i--)
{
if (Items[i].IsChecked)
Items.RemoveAt(i);
}
}
public void RemoveSelectedItems()
{
for(var i = Items.Count-1; i >= 0; i--)
{
if (Items[i].IsChecked)
Items.RemoveAt(i);
}
}
Pobiega
Pobiega11mo ago
^
Mekasu0124
Mekasu012411mo ago
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?
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public IEnumerable<TodoItem> GetItems() => new[]
{
new TodoItem { Description = "Walk the dog" },
new TodoItem { Description = "Buy some milk" },
new TodoItem { Description = "Learn Avalonia", IsChecked = true },
};
}
}
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public IEnumerable<TodoItem> GetItems() => new[]
{
new TodoItem { Description = "Walk the dog" },
new TodoItem { Description = "Buy some milk" },
new TodoItem { Description = "Learn Avalonia", IsChecked = true },
};
}
}
Kouhai
Kouhai11mo ago
Np! As you can see in the sample
for(var i = Items.Count-1; i >= 0; i--)
{
if (Items[i].IsChecked)
Items.RemoveAt(i);
}
for(var i = Items.Count-1; i >= 0; i--)
{
if (Items[i].IsChecked)
Items.RemoveAt(i);
}
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!
Mekasu0124
Mekasu012411mo ago
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
Kouhai
Kouhai11mo ago
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)
Mekasu0124
Mekasu012411mo ago
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?
Kouhai
Kouhai11mo ago
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 file
Mekasu0124
Mekasu012411mo ago
so quick question
Kouhai
Kouhai11mo ago
Sure
Mekasu0124
Mekasu012411mo ago
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 Application
Kouhai
Kouhai11mo ago
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. 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!
Mekasu0124
Mekasu012411mo ago
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 repo
Kouhai
Kouhai11mo ago
The 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!
Mekasu0124
Mekasu012411mo ago
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
I would like to have this, but would I need to get my sort button and having date and time added into my items first?
Kouhai
Kouhai11mo ago
Are Date and Time seperate here?
Mekasu0124
Mekasu012411mo ago
yes so like this is where I'm confused right now I Have this
Kouhai
Kouhai11mo ago
You can add to your TodoItem model, and simply bind on these two properties
public DateOnly Date { get; set; }
public TimeOnly TimeOfDay { get; set; }
public DateOnly Date { get; set; }
public TimeOnly TimeOfDay { get; set; }
Mekasu0124
Mekasu012411mo ago
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="300"
x:Class="Todo.Views.TodoListView">

<Grid
RowDefinitions="Auto,*,Auto,Auto"
ColumnDefinitions="*,*"
Background="#000000">

<Grid.Styles>
<Style Selector="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#00B71C" />
<Setter Property="BorderBrush" Value="#00B71C" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="20" />
</Style>

<Style Selector="CheckBox">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#00B71C" />
<Setter Property="BorderBrush" Value="#00B71C" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="10" />
</Style>
</Grid.Styles>

<Button Grid.Row="0" Content="Sort" />

<ItemsControl Items="{Binding Items}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Margin="4"
IsChecked="{Binding IsChecked}"
Content="{Binding Description}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

<Button Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Command="{Binding $parent[Window].DataContext.AddItem}"
Content="New Item"/>

<Button Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Command="{Binding RemoveSelectedItems}"
Content="Delete"/>
</Grid>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="300"
x:Class="Todo.Views.TodoListView">

<Grid
RowDefinitions="Auto,*,Auto,Auto"
ColumnDefinitions="*,*"
Background="#000000">

<Grid.Styles>
<Style Selector="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#00B71C" />
<Setter Property="BorderBrush" Value="#00B71C" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="20" />
</Style>

<Style Selector="CheckBox">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#00B71C" />
<Setter Property="BorderBrush" Value="#00B71C" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="10" />
</Style>
</Grid.Styles>

<Button Grid.Row="0" Content="Sort" />

<ItemsControl Items="{Binding Items}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Margin="4"
IsChecked="{Binding IsChecked}"
Content="{Binding Description}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

<Button Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Command="{Binding $parent[Window].DataContext.AddItem}"
Content="New Item"/>

<Button Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Command="{Binding RemoveSelectedItems}"
Content="Delete"/>
</Grid>
</UserControl>
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?
Kouhai
Kouhai11mo ago
You can have something like
<ItemsControl Items="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
++ <StackPanel Orientation="Horizontal">
<CheckBox Margin="4"
IsChecked="{Binding IsChecked}"
Content="{Binding Description}"/>
++ <Label Margin="4" Foreground="AliceBlue" Content="{Binding Date}"/>
++ </StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Items="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
++ <StackPanel Orientation="Horizontal">
<CheckBox Margin="4"
IsChecked="{Binding IsChecked}"
Content="{Binding Description}"/>
++ <Label Margin="4" Foreground="AliceBlue" Content="{Binding Date}"/>
++ </StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
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!
Mekasu0124
Mekasu012411mo ago
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
Date | Time | Todo
-----------------------------------
xxxx | xxxx | xxxxxxxxxxxxxxxxx
Date | Time | Todo
-----------------------------------
xxxx | xxxx | xxxxxxxxxxxxxxxxx
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?
<Border BorderBrush="#000000" BorderThickness="2">
<Grid RowDefinitions="Auto, *, Auto">
<Border Grid.Row="0" BorderBrush="Green" BorderThickness="2" Margin="5">
<Grid ColumnDefinitions="Auto, *, *">
<Button Grid.Column="0" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="Date" />
<Button Grid.Column="1" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="Time" />
<Label Grid.Column="2" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" Content="Todo Note" />
</Grid>
</Border>

<Border Grid.Row="1" BorderBrush="Black" BorderThickness="2" Margin="5">
<Grid ColumnDefinitions="Auto, *, *" RowDefinitions="Auto, Auto, Auto">
<CheckBox Margin=5 Grid.Row="0" Grid.Column="0" Content="12/12/2023 12:42 PM Some Todo Item" />
</Grid>
</Border>

<Border Grid.Row="2" BorderBrush="Orange" BorderThickness="2" Margin="5">
<Grid Grid.Row="2" RowDefinitions="*, *">
<Button Grid.Row="0" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="New Item" Width="250"/>
<Button Grid.Row="1" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="Delete Items" Width="250"/>
</Grid>
</Border>
</Grid>
</Border>
<Border BorderBrush="#000000" BorderThickness="2">
<Grid RowDefinitions="Auto, *, Auto">
<Border Grid.Row="0" BorderBrush="Green" BorderThickness="2" Margin="5">
<Grid ColumnDefinitions="Auto, *, *">
<Button Grid.Column="0" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="Date" />
<Button Grid.Column="1" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="Time" />
<Label Grid.Column="2" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" Content="Todo Note" />
</Grid>
</Border>

<Border Grid.Row="1" BorderBrush="Black" BorderThickness="2" Margin="5">
<Grid ColumnDefinitions="Auto, *, *" RowDefinitions="Auto, Auto, Auto">
<CheckBox Margin=5 Grid.Row="0" Grid.Column="0" Content="12/12/2023 12:42 PM Some Todo Item" />
</Grid>
</Border>

<Border Grid.Row="2" BorderBrush="Orange" BorderThickness="2" Margin="5">
<Grid Grid.Row="2" RowDefinitions="*, *">
<Button Grid.Row="0" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="New Item" Width="250"/>
<Button Grid.Row="1" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Content="Delete Items" Width="250"/>
</Grid>
</Border>
</Grid>
</Border>
Mekasu0124
Mekasu012411mo ago
Kouhai
Kouhai11mo ago
Grid would work nicely for this, but you could also just use DataGrid package if you want something like a table
Mekasu0124
Mekasu012411mo ago
how would I use the DataGrid?
Kouhai
Kouhai11mo ago
You'd need to install it first https://docs.avaloniaui.net/docs/controls/datagrid/ And you can have something like this
<DataGrid SelectionMode="Extended" AutoGenerateColumns="False" Items="{Binding Items}" >
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Header="Date" Binding="{Binding Date}" />
</DataGrid.Columns>
</DataGrid>
<DataGrid SelectionMode="Extended" AutoGenerateColumns="False" Items="{Binding Items}" >
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Header="Date" Binding="{Binding Date}" />
</DataGrid.Columns>
</DataGrid>
You can add more columns in <DataGrid.Columns>
DataGrid | Avalonia UI
The DataGrid control is a control that displays data in a customizable grid.
Kouhai
Kouhai11mo ago
Make sure to install a version matching your Avalonia's package version so 0.10.21
Mekasu0124
Mekasu012411mo ago
and those few lines of code would iterate through all of the entries and display them correctly?
Kouhai
Kouhai11mo ago
Yeah pretty much You can even combine it DatePicker and TimePicker controls and make it easy to modify dates and times
Mekasu0124
Mekasu012411mo ago
ok cool. give me a bit to try this out. I'll ping you when I'm able to get to it
Mekasu0124
Mekasu012411mo ago
is it this package?
Kouhai
Kouhai11mo ago
Yup but make sure to install the 0.10.21 version @mekasu0124
Mekasu0124
Mekasu012411mo ago
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 look
Kouhai
Kouhai11mo ago
You need to add the DataGrid style
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
++ <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
</Application.Styles>
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
++ <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
</Application.Styles>
And then run the application You can add the style in app.axaml
Mekasu0124
Mekasu012411mo ago
ok so now my only problem is figuring out how to style the headers of the grid, and making it scrollable content
Mekasu0124
Mekasu012411mo ago
and I have one more column than I should have. There should only be 3 columns, but there are 4?
Kouhai
Kouhai11mo ago
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.
Mekasu0124
Mekasu012411mo ago
gotcha gotcha
Kouhai
Kouhai11mo ago
To style the headers you can just do
<Style Selector="DataGridColumnHeader">
<Setter Property="..." Value="..." />
</Style>
<Style Selector="DataGridColumnHeader">
<Setter Property="..." Value="..." />
</Style>
Mekasu0124
Mekasu012411mo ago
There's no background property for them?
Kouhai
Kouhai11mo ago
There should be
Mekasu0124
Mekasu012411mo ago
It's not working for me oh I had the selector wrong
Kouhai
Kouhai11mo ago
Btw if you want to style the row you can use Selector="DataGridRow"
Mekasu0124
Mekasu012411mo ago
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
Kouhai
Kouhai11mo ago
For the item selection
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="Red" />
</Style>
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="Red" />
</Style>
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 property
Mekasu0124
Mekasu012411mo ago
foreground isn't a property of it
Kouhai
Kouhai11mo ago
Foreground is a property of DataGridRow but not Rectangle so you need to have two separate style selectors for background (Fill) and foreground
Mekasu0124
Mekasu012411mo ago
got 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
MODiX
MODiX11mo ago
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 Save
I would like to have this, but would I need to get my sort button and having date and time added into my items first?
React with ❌ to remove this embed.
Mekasu0124
Mekasu012411mo ago
https://github.com/mekasu0124/Todo or should I redo the code behind for the ViewModels first?
Kouhai
Kouhai11mo ago
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.
Mekasu0124
Mekasu012411mo ago
yea so here comes the fun part 1. How and Where do I check if the 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 with when the app loads so far I have in my Database.cs file
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public void CheckExists() {}

public void CreateIfNotExists() {}

public void LoadIfDoesExist() {}
}
}
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public void CheckExists() {}

public void CreateIfNotExists() {}

public void LoadIfDoesExist() {}
}
}
but this file, Database.cs would need to be called before the program showed an image
Kouhai
Kouhai11mo ago
1. 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 something
Mekasu0124
Mekasu012411mo ago
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public void Save() { }
public void Load() { }
}
}
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public void Save() { }
public void Load() { }
}
}
ok 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#
Kouhai
Kouhai11mo ago
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 specified
Mekasu0124
Mekasu012411mo ago
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public void Save()
{
string databaseFile = "database.json";

if (File.Exists(databaseFile))
{
// save information
}
else
{
File.Create(databaseFile).Close();
}
}
}
}
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public void Save()
{
string databaseFile = "database.json";

if (File.Exists(databaseFile))
{
// save information
}
else
{
File.Create(databaseFile).Close();
}
}
}
}
ok here's my start
Kouhai
Kouhai11mo ago
Well, you don't really need to check if the file exists before saving, you can just overwrite the old file
Mekasu0124
Mekasu012411mo ago
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 file
this 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?
Mekasu0124
Mekasu012411mo ago
so like this file would need to push the information to the Database.json file
Kouhai
Kouhai11mo ago
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
Mekasu0124
Mekasu012411mo ago
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
Kouhai
Kouhai11mo ago
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
Mekasu0124
Mekasu012411mo ago
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
string dbFile = "database.json";

public void Main()
{
if (File.Exists(dbFile))
{
JsonSerializer.Deserialize(dbFile, _);
}
}
}
}
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
string dbFile = "database.json";

public void Main()
{
if (File.Exists(dbFile))
{
JsonSerializer.Deserialize(dbFile, _);
}
}
}
}
like that?
Kouhai
Kouhai11mo ago
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
["hello", "world"]
["hello", "world"]
Deserialize<string[]> Would work, because the json file is an array of strings
Mekasu0124
Mekasu012411mo ago
I'm confused
namespace Todo.Services
{
public class Database
{
new dbFile = "database.json";
}
}
namespace Todo.Services
{
public class Database
{
new dbFile = "database.json";
}
}
?
Kouhai
Kouhai11mo ago
class AddItemViewModel : ViewModelBase
{
string description;

++ public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

public string Description
{
get => description;
set => this.RaiseAndSetIfChanged(ref description, value);
}

public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
class AddItemViewModel : ViewModelBase
{
string description;

++ public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

public string Description
{
get => description;
set => this.RaiseAndSetIfChanged(ref description, value);
}

public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
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# 😅
Mekasu0124
Mekasu012411mo ago
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
namespace Todo.Services
{
public class Database
{
public Database()
{
}
}
}
namespace Todo.Services
{
public class Database
{
public Database()
{
}
}
}
so you said my next move is to check and load the file information if it exists right?
namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
List<string> dbInfo = JsonSerializer.Deserialize(dbFile, string[]);
}
}
}
}
namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
List<string> dbInfo = JsonSerializer.Deserialize(dbFile, string[]);
}
}
}
}
it doesn't like the string[]
Kouhai
Kouhai11mo ago
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.
Mekasu0124
Mekasu012411mo ago
namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllLines(dbFile);
List<string> strings = JsonSerializer.Deserialize(dbInfo, List<string>);
}
}
}
}
namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllLines(dbFile);
List<string> strings = JsonSerializer.Deserialize(dbInfo, List<string>);
}
}
}
}
this is getting frustrating lol I'm really not understanding what I'm doing wrong....
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile)
{
var dbInfo = File.ReadAllText(dbFile);
List<string> info = JsonSerializer.Deserializer<List<string>>(dbInfo);
}
}
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile)
{
var dbInfo = File.ReadAllText(dbFile);
List<string> info = JsonSerializer.Deserializer<List<string>>(dbInfo);
}
}
is it this instead?
public Database()
{
string dbFile = "database.json";

var dbInfo = File.Exists(dbFile) ? File.ReadAllText(dbFile) : null;
List<string> info = JsonSerializer.Deserialize<List<string>>(dbInfo);
}
public Database()
{
string dbFile = "database.json";

var dbInfo = File.Exists(dbFile) ? File.ReadAllText(dbFile) : null;
List<string> info = JsonSerializer.Deserialize<List<string>>(dbInfo);
}
I reduced it lol 😂 I just don't know what would go in place of null
Kouhai
Kouhai11mo ago
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
var dbInfo = File.Exists(dbFile) ? File.ReadAllText(dbFile) : "[]";
var dbInfo = File.Exists(dbFile) ? File.ReadAllText(dbFile) : "[]";
Though only executing the deserilization code when the files exists is better
Mekasu0124
Mekasu012411mo ago
1. 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 arguments
so 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 better
so it's better to have the if/else instead of a one-liner
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
string info = JsonSerializer.Deserialize<string>(dbFile);
}
}
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
string info = JsonSerializer.Deserialize<string>(dbFile);
}
}
so this is where I need to be then, right?
Kouhai
Kouhai11mo ago
so that's why I'm using File.ReadAllText()? RIght? So it reads all the text instead of multiple strings at once
Yup, 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 strings
Mekasu0124
Mekasu012411mo ago
ok 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?
Kouhai
Kouhai11mo ago
It does need to be a list List<string> means a list of strings List<int> means a list of ints
Mekasu0124
Mekasu012411mo ago
right
Kouhai
Kouhai11mo ago
So to deserialize it you need a list of todo items
Mekasu0124
Mekasu012411mo ago
ohhhh ok so since we're deserializing to List<TodoItem> the variable has to match it
Kouhai
Kouhai11mo ago
Right, a short hand way is just var ... = ... var infers the type
Mekasu0124
Mekasu012411mo ago
so I can choose between var and List<TodoItem>
Kouhai
Kouhai11mo ago
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"
Mekasu0124
Mekasu012411mo ago
right. python doesn't have the variable type like c# does
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
}
}
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
}
}
ok so I'm here now. What's my next step?
Kouhai
Kouhai11mo ago
Next step is keeping the deserialized data available so when MainWindow loads it can get the items from it
Mekasu0124
Mekasu012411mo ago
so I need to make it IEnumerable like I did here?
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public IEnumerable<TodoItem> GetItems() => new[]
{
new TodoItem { Description = "Walk the dog" },
new TodoItem { Description = "Buy some milk" },
new TodoItem { Description = "Learn Avalonia", IsChecked = true },
};
}
}
using System.Collections.Generic;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public IEnumerable<TodoItem> GetItems() => new[]
{
new TodoItem { Description = "Walk the dog" },
new TodoItem { Description = "Buy some milk" },
new TodoItem { Description = "Learn Avalonia", IsChecked = true },
};
}
}
Kouhai
Kouhai11mo ago
You just need a way to get the data Be it IEnumerable<TodoItem> List<TodoItem> TodoItem[]
Mekasu0124
Mekasu012411mo ago
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?
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
IEnumerable<TodoItem> items = new List<TodoItem>();
}
}
}
}
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Todo.Models;

namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
IEnumerable<TodoItem> items = new List<TodoItem>();
}
}
}
}
Here's what I have now. So what's my next step?
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
so pass the items variable to a function like
public IEnumerable<TodoItem> GetItems() => new[]
{
new TodoItem { Description = "Walk the dog" },
new TodoItem { Description = "Buy some milk" },
new TodoItem { Description = "Learn Avalonia", IsChecked = true },
};
public IEnumerable<TodoItem> GetItems() => new[]
{
new TodoItem { Description = "Walk the dog" },
new TodoItem { Description = "Buy some milk" },
new TodoItem { Description = "Learn Avalonia", IsChecked = true },
};
but it would be something like
namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile):
List<TodoItems> info = JsonSerializer.Deseralize<List<TodoItem>>(dbInfo);
IENumerable<TodoItem> items = new List<TodoItem>();
}
}
public IEnumerable<TodoItem> GetItems(items) => new[]
{
// somethin ghere???
}
}
}
namespace Todo.Services
{
public class Database
{
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile):
List<TodoItems> info = JsonSerializer.Deseralize<List<TodoItem>>(dbInfo);
IENumerable<TodoItem> items = new List<TodoItem>();
}
}
public IEnumerable<TodoItem> GetItems(items) => new[]
{
// somethin ghere???
}
}
}
right?
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
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?
Pobiega
Pobiega11mo ago
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...
var database = new Database();
var items = database.TodoItems.Where(x => ...);
var database = new Database();
var items = database.TodoItems.Where(x => ...);
Mekasu0124
Mekasu012411mo ago
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.
Pobiega
Pobiega11mo ago
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?
Mekasu0124
Mekasu012411mo ago
Database db = new Database(); right?
Pobiega
Pobiega11mo ago
that would be creating the new database instance but how do we then make db.TodoItems work?
Mekasu0124
Mekasu012411mo ago
namespace Todo.Models
{
public class TodoItem
{
public string Description { get; set; }
public bool IsChecked { get; set; }
}
}
namespace Todo.Models
{
public class TodoItem
{
public string Description { get; set; }
public bool IsChecked { get; set; }
}
}
using something like this?
Pobiega
Pobiega11mo ago
Something like that indeed
Mekasu0124
Mekasu012411mo ago
getters and setters for the information?
Pobiega
Pobiega11mo ago
public string Description { get; set; } is a string property called Description so your Database should have an IList<TodoItem> property, imho
Mekasu0124
Mekasu012411mo ago
I'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.
Pobiega
Pobiega11mo ago
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>
Mekasu0124
Mekasu012411mo ago
oh ok. so my next step is to
your database probably wants to expose the list of known items as a property
and to do that I would
var database = new Database();
var items = database.TodoItems.Where(x => ...);
var database = new Database();
var items = database.TodoItems.Where(x => ...);
do something like this?
Pobiega
Pobiega11mo ago
well thats the usage
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
ie, thats how you would use it when its done yep
Mekasu0124
Mekasu012411mo ago
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}"
Pobiega
Pobiega11mo ago
You're not at that stage yet
Mekasu0124
Mekasu012411mo ago
oh ok
Pobiega
Pobiega11mo ago
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?
Mekasu0124
Mekasu012411mo ago
I'm going to say safe side and say no
Pobiega
Pobiega11mo ago
public class Test
{
public Test()
{
var value = Random.Shared.Next(1000);
}
}

var test = new Test();
public class Test
{
public Test()
{
var value = Random.Shared.Next(1000);
}
}

var test = new Test();
how do we access the value here?
Mekasu0124
Mekasu012411mo ago
Console.Write(test.value); I believe
Pobiega
Pobiega11mo ago
Okay lets try that
Mekasu0124
Mekasu012411mo ago
is there a compiler bot in this server?
MODiX
MODiX11mo ago
Pobiega
REPL Result: Failure
public class Test
{
public Test()
{
var value = Random.Shared.Next(1000);
}
}

var test = new Test();
Console.WriteLine(test.value);
public class Test
{
public Test()
{
var value = Random.Shared.Next(1000);
}
}

var test = new Test();
Console.WriteLine(test.value);
Exception: CompilationErrorException
- 'Test' does not contain a definition for 'value' and no accessible extension method 'value' accepting a first argument of type 'Test' could be found (are you missing a using directive or an assembly reference?)
- 'Test' does not contain a definition for 'value' and no accessible extension method 'value' accepting a first argument of type 'Test' could be found (are you missing a using directive or an assembly reference?)
Compile: 720.937ms | Execution: 0.000ms | React with ❌ to remove this embed.
Mekasu0124
Mekasu012411mo ago
O_o
Pobiega
Pobiega11mo ago
oops, errors
Mekasu0124
Mekasu012411mo ago
how did you do that?
Pobiega
Pobiega11mo ago
!e anyways, check the error message what is it saying?
Mekasu0124
Mekasu012411mo ago
ok one second
Pobiega
Pobiega11mo ago
'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 exist
Mekasu0124
Mekasu012411mo ago
oooh ok. I got you so I have to put it into a function?
Pobiega
Pobiega11mo ago
No?
Mekasu0124
Mekasu012411mo ago
oh
Pobiega
Pobiega11mo ago
a property
MODiX
MODiX11mo ago
Pobiega
REPL Result: Success
public class Test
{
public int Value { get; }

public Test()
{
var value = Random.Shared.Next(1000);
Value = value;
}
}

var test = new Test();
Console.WriteLine(test.Value);
public class Test
{
public int Value { get; }

public Test()
{
var value = Random.Shared.Next(1000);
Value = value;
}
}

var test = new Test();
Console.WriteLine(test.Value);
Console Output
330
330
Compile: 642.090ms | Execution: 49.727ms | React with ❌ to remove this embed.
Mekasu0124
Mekasu012411mo ago
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
Mekasu0124
Mekasu012411mo ago
nevermind I fixed it
var test = new Test();
Console.Write(test.Value);

public class Test
{
public int Value { get; }
public Test()
{
var value = Random.Shared.Next(1000);
Value = value;
}
}
var test = new Test();
Console.Write(test.Value);

public class Test
{
public int Value { get; }
public Test()
{
var value = Random.Shared.Next(1000);
Value = value;
}
}
had to do it this way but that makes sense. you have to get the information in order to use the information
Pobiega
Pobiega11mo ago
the property means we can "save" the information so its not discarded when the method ends
Mekasu0124
Mekasu012411mo ago
namespace Todo.Services
{
public class Database
{
public List Items { get; }
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
IEnumerable<TodoItem> items = new List<TodoItem>();
Items = items;
}
}
}
}
namespace Todo.Services
{
public class Database
{
public List Items { get; }
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
IEnumerable<TodoItem> items = new List<TodoItem>();
Items = items;
}
}
}
}
so I would need to do something like this, except List won't work
Pobiega
Pobiega11mo ago
almost which local variable contains the actual data?
Mekasu0124
Mekasu012411mo ago
items
Pobiega
Pobiega11mo ago
incorrect
Mekasu0124
Mekasu012411mo ago
no sorry info
Pobiega
Pobiega11mo ago
that is an empty list correct but why can't we use List?
Mekasu0124
Mekasu012411mo ago
because I didn't give it a type inside of the <> so public List<string> Items {get;}
Pobiega
Pobiega11mo ago
Right. So give it a type info 🙂 is string the right type thou?
Mekasu0124
Mekasu012411mo ago
no because it's a list of strings
Pobiega
Pobiega11mo ago
not as far as I can tell
Mekasu0124
Mekasu012411mo ago
it it's a json object
Pobiega
Pobiega11mo ago
TodoItem maybe?
Mekasu0124
Mekasu012411mo ago
oh yea. missed that part. my fault
namespace Todo.Services
{
public class Database
{
public List<TodoItem> Items { get; }
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
IEnumerable<TodoItem> items = new List<TodoItem>();
}
}
}
}
namespace Todo.Services
{
public class Database
{
public List<TodoItem> Items { get; }
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
IEnumerable<TodoItem> items = new List<TodoItem>();
}
}
}
}
so then question
Pobiega
Pobiega11mo ago
remove the last line of your constructor IEnumerable<TodoItem> items = new List<TodoItem>(); does nothing
Mekasu0124
Mekasu012411mo ago
if List<TodoItem> info = ... has the information, then what's the purpose of the line IEnumerable<TodoItem> items = new List<TodoItem>();?
Pobiega
Pobiega11mo ago
or well, nothing useful there is no purpose its entirely useless
Mekasu0124
Mekasu012411mo ago
so it's not needed
Pobiega
Pobiega11mo ago
not at all it just declares a new blank list, then throws it away
Mekasu0124
Mekasu012411mo ago
so I'd replace it with Items = info
Pobiega
Pobiega11mo ago
ye!
Mekasu0124
Mekasu012411mo ago
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?
Pobiega
Pobiega11mo ago
kinda but the getter isnt the important part its the fact that the property is a class member, not a local variable
Mekasu0124
Mekasu012411mo ago
public List<TodoItem> Items { get; } <- class member List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo); <- local variable right?
Pobiega
Pobiega11mo ago
yup
Mekasu0124
Mekasu012411mo ago
ok cool. that makes sense
Pobiega
Pobiega11mo ago
anything declared inside a method is a local variable anything declared on the top-level class scope is a member
Mekasu0124
Mekasu012411mo ago
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
class MyClass:
def __init__(self):
self.name = "Mekasu" # <- this is a class member
def some_function(self):
username = "mekasu0124" # <- this is a local variable
class MyClass:
def __init__(self):
self.name = "Mekasu" # <- this is a class member
def some_function(self):
username = "mekasu0124" # <- this is a local variable
Pobiega
Pobiega11mo ago
yes
Mekasu0124
Mekasu012411mo ago
ok cool cool
Pobiega
Pobiega11mo ago
but python doesnt have declaration of members as such
Mekasu0124
Mekasu012411mo ago
so now that we have the information in a class member, we can use it else where, right?
Pobiega
Pobiega11mo ago
since y'know, not a typed language
Mekasu0124
Mekasu012411mo ago
right
Pobiega
Pobiega11mo ago
yes!
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
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 bool
Mekasu0124
Mekasu012411mo ago
the 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
Pobiega
Pobiega11mo ago
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.
Mekasu0124
Mekasu012411mo ago
Mekasu0124
Mekasu012411mo ago
don't die that's no bueno
Pobiega
Pobiega11mo ago
yeah good call
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
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?
Mekasu0124
Mekasu012411mo ago
{
"1": {
"date": <date_object>,
"time": <time_object>,
"description": "Walk The Dog"
}
}
{
"1": {
"date": <date_object>,
"time": <time_object>,
"description": "Walk The Dog"
}
}
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
Pobiega
Pobiega11mo ago
sure
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
idk how plausible that would be
I 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
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
okay so yeah, make your TodoItem class represent the structure you want
Mekasu0124
Mekasu012411mo ago
// ./Models/TodoItem.cs
namespace Todo.Models
{
public class TodoItem
{
public string Description { get; set; }
public string Date { get; set; }
public string Time { get; set; }
}
}
// ./Models/TodoItem.cs
namespace Todo.Models
{
public class TodoItem
{
public string Description { get; set; }
public string Date { get; set; }
public string Time { get; set; }
}
}
I have this thoughts?
Pobiega
Pobiega11mo ago
bad.
Mekasu0124
Mekasu012411mo ago
oof
Pobiega
Pobiega11mo ago
string
Mekasu0124
Mekasu012411mo ago
learn me lol
Pobiega
Pobiega11mo ago
why string? date and time isnt strings DateOnly and TimeOnly
Mekasu0124
Mekasu012411mo ago
because tbh I haven't learned datetime yet, or how to set those like that
Pobiega
Pobiega11mo ago
no time like the present
Mekasu0124
Mekasu012411mo ago
ok so I changed them to
public DateOnly Date {get;set;}
public TimeOnly Time {get;set;}
public DateOnly Date {get;set;}
public TimeOnly Time {get;set;}
but they have red lines under the type casts am I missing an import?
Pobiega
Pobiega11mo ago
hm, whats the error?
Mekasu0124
Mekasu012411mo ago
CS0246: The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?)
Pobiega
Pobiega11mo ago
huh. what .NET version is this? it was added in .NET 6
Mekasu0124
Mekasu012411mo ago
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?
Pobiega
Pobiega11mo ago
makes sense a lot of types are in that namespace
Mekasu0124
Mekasu012411mo ago
using System;

namespace Todo.Models
{
public class TodoItem
{
public DateOnly Date { get; set; }
public TimeOnly Time { get; set; }
public string Description { get; set; }
}
}
using System;

namespace Todo.Models
{
public class TodoItem
{
public DateOnly Date { get; set; }
public TimeOnly Time { get; set; }
public string Description { get; set; }
}
}
ok so here's where I'm at
Pobiega
Pobiega11mo ago
seems good
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
I did that on the axaml page, but unsure of what I need to do next
Mekasu0124
Mekasu012411mo ago
which tbh I'd rather have that auto populate if possible
Pobiega
Pobiega11mo ago
probably doable too.
Mekasu0124
Mekasu012411mo ago
no nevermind I'd rather leave it editable so that if they want to create a future tense note
Pobiega
Pobiega11mo ago
so, you made them as TextBox
Mekasu0124
Mekasu012411mo ago
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?
Mekasu0124
Mekasu012411mo ago
ok so I implemented that, and am updating the repo now
Pobiega
Pobiega11mo ago
I see the styles.. but you seem to have removed the pickers themselves?
Mekasu0124
Mekasu012411mo ago
just wish I knew how to center align the text on the DatePicker like it is on the TimePicker
Pobiega
Pobiega11mo ago
oh sorry there they are
Mekasu0124
Mekasu012411mo ago
and change their foreground color to green
Pobiega
Pobiega11mo ago
¯\_(ツ)_/¯ I'm a backend developer, I know nothing about styling
Mekasu0124
Mekasu012411mo ago
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
using System.Reactive;
using ReactiveUI;
using Todo.Models;

namespace Todo.ViewModels
{
class AddItemViewModel : ViewModelBase
{
string description;

public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

public string Description
{
get => description;
set => this.RaiseAndSetIfChanged(ref description, value);
}

public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
}
using System.Reactive;
using ReactiveUI;
using Todo.Models;

namespace Todo.ViewModels
{
class AddItemViewModel : ViewModelBase
{
string description;

public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

public string Description
{
get => description;
set => this.RaiseAndSetIfChanged(ref description, value);
}

public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
}
so what are my next moves?
Pobiega
Pobiega11mo ago
well, the view model needs properties and fields to hold the data for the pickers
Mekasu0124
Mekasu012411mo ago
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?
Pobiega
Pobiega11mo ago
the same way its done here for the description, I imagine
Mekasu0124
Mekasu012411mo ago
when you guys display code here, and it has the green + and the red - how do you do that?
Pobiega
Pobiega11mo ago
instead of cs use diff
+ hello
- world
+ hello
- world
Pobiega
Pobiega11mo ago
Mekasu0124
Mekasu012411mo ago
using System;
using System.Reactive;
using ReactiveUI;
using Todo.Models;

namespace Todo.ViewModels
{
class AddItemViewModel : ViewModelBase
{
string description;
- DateOnly date { get; set; }
- TimeOnly time { get; set; }
+ DateOnly date;
+ TimeOnly time;

public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

public string Description
{
get => description;
set => this.RaiseAndSetIfChanged(ref description, value);
}
+ public DateOnly Date
+ {
+ get => date;
+ set => this.RaiseAndSetIfChanged(ref date, value);
+ }
+ public TimeOnly Time
+ {
+ get => time;
+ set => this.RaiseAndSetIfChanged(ref time, value);
+ }

public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
}
using System;
using System.Reactive;
using ReactiveUI;
using Todo.Models;

namespace Todo.ViewModels
{
class AddItemViewModel : ViewModelBase
{
string description;
- DateOnly date { get; set; }
- TimeOnly time { get; set; }
+ DateOnly date;
+ TimeOnly time;

public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

public string Description
{
get => description;
set => this.RaiseAndSetIfChanged(ref description, value);
}
+ public DateOnly Date
+ {
+ get => date;
+ set => this.RaiseAndSetIfChanged(ref date, value);
+ }
+ public TimeOnly Time
+ {
+ get => time;
+ set => this.RaiseAndSetIfChanged(ref time, value);
+ }

public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
}
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 time
Pobiega
Pobiega11mo ago
remove the {get;set;} from the top declarations they should be fields, not properties ye
Mekasu0124
Mekasu012411mo ago
ok cool so then I should probably do
public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
+ x => x.Date,
+ x => x.Time,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
+ () => new TodoItem { Description = Description, Date = Date, Time = Time },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}
public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
+ x => x.Date,
+ x => x.Time,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
+ () => new TodoItem { Description = Description, Date = Date, Time = Time },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}
right?
Pobiega
Pobiega11mo ago
seems reasonable, yep
Mekasu0124
Mekasu012411mo ago
oops edited it
Pobiega
Pobiega11mo ago
hm, that WhenAnyValue thing will probably not work It looks like the first is a selector, and the second is the validation function
Mekasu0124
Mekasu012411mo ago
Pobiega
Pobiega11mo ago
ye 😄
Mekasu0124
Mekasu012411mo ago
well quesiton nevermind so what can I use instead? or should I rewrite it
Pobiega
Pobiega11mo ago
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var okEnabled = descriptionOk;
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var okEnabled = descriptionOk;
does that give you a hint?
Mekasu0124
Mekasu012411mo ago
because this
public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}
public AddItemViewModel()
{
var okEnabled = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}
points to this
public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
so do this for each item
Pobiega
Pobiega11mo ago
yup then Im thinking there is probably a way to combine them
Mekasu0124
Mekasu012411mo ago
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var dateOk = this.WhenAnyValue(
x => x.Date,
x => !DateOnly.IsNullOrWhiteSpace(x));

var timeOk = this.WhenAnyValue(
x => x.Time,
x => !TimeOnly.IsNullOrWhiteSpace(x));
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var dateOk = this.WhenAnyValue(
x => x.Date,
x => !DateOnly.IsNullOrWhiteSpace(x));

var timeOk = this.WhenAnyValue(
x => x.Time,
x => !TimeOnly.IsNullOrWhiteSpace(x));
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 entry
Pobiega
Pobiega11mo ago
thats what we are trying to make
Mekasu0124
Mekasu012411mo ago
bet
Pobiega
Pobiega11mo ago
and yeah, NullOrWhiteSpace only makes sense for strings
Mekasu0124
Mekasu012411mo ago
right
Pobiega
Pobiega11mo ago
DateOnly etc cant be null, and cant be... "whitespace" since its not a string
Mekasu0124
Mekasu012411mo ago
so since we're using Picker's, how would we check that the value has been changed from the default?
Pobiega
Pobiega11mo ago
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 timespan
Mekasu0124
Mekasu012411mo ago
in the cs or axaml? because you lost me lol
Pobiega
Pobiega11mo ago
okay so the timepicker control works with a TimeSpan? as its value property it doesnt know about TimeOnly
Mekasu0124
Mekasu012411mo ago
string description;
DateOnly date;
- TimeOnly time;
+ TimeSpan time;

public AddItemViewModel()
{
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var dateOk = this.WhenAnyValue(
x => x.Date,
x => !DateOnly.IsNullOrWhiteSpace(x));

var timeOk = this.WhenAnyValue(
x => x.Time,
- x => !TimeOnly ? ...);
+ x => !TimeSpan? SelectedTime);

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
descriptionOk);
Cancel = ReactiveCommand.Create(() => { });
}
string description;
DateOnly date;
- TimeOnly time;
+ TimeSpan time;

public AddItemViewModel()
{
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var dateOk = this.WhenAnyValue(
x => x.Date,
x => !DateOnly.IsNullOrWhiteSpace(x));

var timeOk = this.WhenAnyValue(
x => x.Time,
- x => !TimeOnly ? ...);
+ x => !TimeSpan? SelectedTime);

Ok = ReactiveCommand.Create(
() => new TodoItem { Description = Description },
descriptionOk);
Cancel = ReactiveCommand.Create(() => { });
}
Pobiega
Pobiega11mo ago
almost 😄
Mekasu0124
Mekasu012411mo ago
oof
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
just did that lol but I'm not sure what to do about the validation
Pobiega
Pobiega11mo ago
var timeOk = this.WhenAnyValue(
x => x.Time,
x => x.HasValue && x.Value != default);
var timeOk = this.WhenAnyValue(
x => x.Time,
x => x.HasValue && x.Value != default);
Mekasu0124
Mekasu012411mo ago
Pobiega
Pobiega11mo ago
thats not what I wrote :p
Mekasu0124
Mekasu012411mo ago
Mekasu0124
Mekasu012411mo ago
yea I fixed that typo
Pobiega
Pobiega11mo ago
hm that works fine on my machine
Pobiega
Pobiega11mo ago
Mekasu0124
Mekasu012411mo ago
but that's the two error messages on HasValue and Value you're using rider lol
Pobiega
Pobiega11mo ago
yes but that doesnt matter here since its the same compiler
Mekasu0124
Mekasu012411mo ago
i don't know honestly
Mekasu0124
Mekasu012411mo ago
Mekasu0124
Mekasu012411mo ago
I have exactly what you have
Pobiega
Pobiega11mo ago
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var dateOk = this.WhenAnyValue(
x => x.Date,
x => x.HasValue && x.Value != default);

var timeOk = this.WhenAnyValue(
x => x.Time,
x => x.HasValue && x.Value != default);

var okEnabled = descriptionOk
.Concat(dateOk)
.Concat(timeOk);
var descriptionOk = this.WhenAnyValue(
x => x.Description,
x => !string.IsNullOrWhiteSpace(x));

var dateOk = this.WhenAnyValue(
x => x.Date,
x => x.HasValue && x.Value != default);

var timeOk = this.WhenAnyValue(
x => x.Time,
x => x.HasValue && x.Value != default);

var okEnabled = descriptionOk
.Concat(dateOk)
.Concat(timeOk);
ooh I see the issue your types are not nullable they must be TimeSpan?, not TimeSpan and Date should be... DateTimeOffset?
Mekasu0124
Mekasu012411mo ago
so I did public DateOnly? Date {} and public TimeOnly? Time {} and that cleared it up
Pobiega
Pobiega11mo ago
ye
Mekasu0124
Mekasu012411mo ago
and added in the var okEnabled with the concats
- Ok = ReactiveCommand.Create(
- () => new TodoItem { Description = Description },
- descriptionOk);
+ Ok = ReactiveCommand.Create(
+ () => new TodoItem { Description = Description, Date = Date, Time = Time },
+ descriptionOk);
Cancel = ReactiveCommand.Create(() => { });
- Ok = ReactiveCommand.Create(
- () => new TodoItem { Description = Description },
- descriptionOk);
+ Ok = ReactiveCommand.Create(
+ () => new TodoItem { Description = Description, Date = Date, Time = Time },
+ descriptionOk);
Cancel = ReactiveCommand.Create(() => { });
should I do this? and if so, then what would replace descriptionOk);?
Pobiega
Pobiega11mo ago
okEnabled ofc but it wont work just like that Date and Time have different types than what we want
Mekasu0124
Mekasu012411mo ago
ok what am I missing? ah
Pobiega
Pobiega11mo ago
so we need to do some conversions
Mekasu0124
Mekasu012411mo ago
ok I'm listening reading lol
Pobiega
Pobiega11mo ago
first, lets add a method for creating the result. its gonna get messy inside a lambda
Ok = ReactiveCommand.Create(
CreateTodoItem,
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

private TodoItem CreateTodoItem()
{
return new TodoItem { Description = Description };
}
Ok = ReactiveCommand.Create(
CreateTodoItem,
okEnabled);
Cancel = ReactiveCommand.Create(() => { });
}

private TodoItem CreateTodoItem()
{
return new TodoItem { Description = Description };
}
Mekasu0124
Mekasu012411mo ago
public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }

public void ConvertOutput()
{

}
public ReactiveCommand<Unit, TodoItem> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }

public void ConvertOutput()
{

}
ok I put it at the bottom oh just kidding
Mekasu0124
Mekasu012411mo ago
it doesn't like it boss
Pobiega
Pobiega11mo ago
look where you placed it count them braces, boi
Mekasu0124
Mekasu012411mo ago
in the same place you did....
Pobiega
Pobiega11mo ago
nah you didn't
Mekasu0124
Mekasu012411mo ago
you put it under the Ok and Cancel and so did i 😭
Pobiega
Pobiega11mo ago
check the braces mine is not inside the constructor yours is
Mekasu0124
Mekasu012411mo ago
oh sneaky little bastard otay ficked it and I need to have date and time inside with description right?
Pobiega
Pobiega11mo ago
right but we need to convert it first
Mekasu0124
Mekasu012411mo ago
okies lol imma chickadee
Pobiega
Pobiega11mo ago
hint: DateOnly and TimeOnly has some very convenient static methods
Mekasu0124
Mekasu012411mo ago
Date = (DateOnly)Date?
Pobiega
Pobiega11mo ago
check that hint again
Mekasu0124
Mekasu012411mo ago
😔 😕 idk but now I have another problem
Mekasu0124
Mekasu012411mo ago
so not only does it not like Date and Time, but it's throwing an error with this.RaiseAndSetIfChanged
Mekasu0124
Mekasu012411mo ago
should I convert them to strings instead?
Pobiega
Pobiega11mo ago
no lol
Mekasu0124
Mekasu012411mo ago
rip
Pobiega
Pobiega11mo ago
it doesnt like Date and Time because they are the wrong types and DateOnly? should be DateTimeOffset?
Mekasu0124
Mekasu012411mo ago
Pobiega
Pobiega11mo ago
you didnt make the fields at the top nullable TimeSpan and TimeSpan? are not the same type
Mekasu0124
Mekasu012411mo ago
class AddItemViewModel : ViewModelBase
{
string _description;
DateTimeOffset? _date;
TimeSpan? _time;
class AddItemViewModel : ViewModelBase
{
string _description;
DateTimeOffset? _date;
TimeSpan? _time;
right?
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
like that?
Pobiega
Pobiega11mo ago
yes! 😄
Mekasu0124
Mekasu012411mo ago
but then they would need to be readonly
Pobiega
Pobiega11mo ago
no so, you should have no issues outside of your CreateTodoItem method now
Mekasu0124
Mekasu012411mo ago
Pobiega
Pobiega11mo ago
you didnt use the rename tool so you have to fix the names at the bottom too
Mekasu0124
Mekasu012411mo ago
I saw that and ficked it
Pobiega
Pobiega11mo ago
good okay, so lets get to converting
Mekasu0124
Mekasu012411mo ago
ok 😄
Pobiega
Pobiega11mo ago
this will be done inside the CreateTodoItem method
Mekasu0124
Mekasu012411mo ago
right
MODiX
MODiX11mo ago
Pobiega
hint: DateOnly and TimeOnly has some very convenient static methods
React with ❌ to remove this embed.
Mekasu0124
Mekasu012411mo ago
yep. still a little lost there blame python and js ohhhh so parse it
Pobiega
Pobiega11mo ago
no, its not a string
Mekasu0124
Mekasu012411mo ago
or would it be like var convDate = new Date(_date);
Pobiega
Pobiega11mo ago
close but you didnt read the hint 😛
Mekasu0124
Mekasu012411mo ago
I can smell the bacon gr
Pobiega
Pobiega11mo ago
what are static methods?
Mekasu0124
Mekasu012411mo ago
"a method that keeps only one copy of the method at the Type level, not the object level" so like Class.method
Pobiega
Pobiega11mo ago
"only one copy" seems a bit weird, but yeah. exactly so what potentially useful static methods do DateOnly and TimeOnly have?
Pobiega
Pobiega11mo ago
that third one seems good, no?
Mekasu0124
Mekasu012411mo ago
fromdatetime?
Pobiega
Pobiega11mo ago
considering we have a DateTimeOffset and we want a DateOnly what about for TimeOnly? what method stand out as useful?
Mekasu0124
Mekasu012411mo ago
DateOnly newDate = DateTimeOffset.Parse(_date); we have to convert it? parse?
Pobiega
Pobiega11mo ago
parse is for strings think about the current type you have what type do we currently have our time in?
Mekasu0124
Mekasu012411mo ago
well we have to convert from DateTimeOffset to DateOnly and we have to convert from TimeSpan to TimeOnly....
Pobiega
Pobiega11mo ago
okay and what static methods did TimeOnly have that seemed relevant? and no parsing grrr
Mekasu0124
Mekasu012411mo ago
to convert from DateTimeOffset to DateOnly we'd have to do DateOnly newDate = DateTimeOffset.... something
Pobiega
Pobiega11mo ago
not really
Mekasu0124
Mekasu012411mo ago
i feel dumb
Pobiega
Pobiega11mo ago
var date = DateOnly.FromDateTime(_date.Value.Date);
Mekasu0124
Mekasu012411mo ago
I literally would have never gotten that so
Pobiega
Pobiega11mo ago
really?
Mekasu0124
Mekasu012411mo ago
really
Pobiega
Pobiega11mo ago
DateOnly.FromDateTime when you have a DateTimeOffset ? okay, time is easier show me how to do the same for time
Mekasu0124
Mekasu012411mo ago
ok I feel really proud of this one but you're gonna hate it
var date = DateOnly.FromDateTime(_date.Value.Date);
var time = TimeSpan.FromHours(_time.Value.Hours) + TimeSpan.FromMinutes(_time.Value.Minutes) + TimeSpan.FromSeconds(_time.Value.Seconds);
var date = DateOnly.FromDateTime(_date.Value.Date);
var time = TimeSpan.FromHours(_time.Value.Hours) + TimeSpan.FromMinutes(_time.Value.Minutes) + TimeSpan.FromSeconds(_time.Value.Seconds);
🥰
Pobiega
Pobiega11mo ago
.. yep, you're right I hate it
Mekasu0124
Mekasu012411mo ago
😁
Pobiega
Pobiega11mo ago
TimeOnly.FromTimeSpan just seems.... BETTER
Mekasu0124
Mekasu012411mo ago
that wasn't an option that showed up when I pressed the . 😭
Pobiega
Pobiega11mo ago
cause you wrote TimeSpan not TimeOnly we HAVE a timespan. we WANT a TimeOnly
Mekasu0124
Mekasu012411mo ago
oh sorry var time = TimeOnly.FromTimeSpan(_time.Value.); idk the last part
Pobiega
Pobiega11mo ago
no lastpart
Mekasu0124
Mekasu012411mo ago
just _time.Value?
Pobiega
Pobiega11mo ago
yes. because _time is a Nullable<TimeSpan> aka TimeSpan?
Mekasu0124
Mekasu012411mo ago
damn. I felt really proud of what I did with time lol right
private TodoItem CreateTodoItem()
{
var date = DateOnly.FromDateTime(_date.Value.Date);
var time = TimeOnly.FromTimeSpan(_time.Value);

return new TodoItem { Description = Description, Date = date, Time = time };
}
private TodoItem CreateTodoItem()
{
var date = DateOnly.FromDateTime(_date.Value.Date);
var time = TimeOnly.FromTimeSpan(_time.Value);

return new TodoItem { Description = Description, Date = date, Time = time };
}
Pobiega
Pobiega11mo ago
there we go.
Mekasu0124
Mekasu012411mo ago
doesn't description need to go at the end?
Pobiega
Pobiega11mo ago
nope order doesnt matter with initializers
Mekasu0124
Mekasu012411mo ago
oh ok so what's the next step. I feel confident ^_^
Pobiega
Pobiega11mo ago
i dunno testing it? 😄
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
"because of this"?
Mekasu0124
Mekasu012411mo ago
// ./ViewModels/TodoListViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Todo.Models;

namespace Todo.ViewModels
{
public class TodoListViewModel : ViewModelBase
{
public TodoListViewModel(IEnumerable<TodoItem> items)
{
Items = new ObservableCollection<TodoItem>(items);
}

public ObservableCollection<TodoItem> Items { get; }

public void RemoveSelectedItems()
{
for (var i = Items.Count - 1; i >= 0; i--)
{
if (Items[i].IsChecked)
{
Items.RemoveAt(i);
}
}
}
}
}
// ./ViewModels/TodoListViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Todo.Models;

namespace Todo.ViewModels
{
public class TodoListViewModel : ViewModelBase
{
public TodoListViewModel(IEnumerable<TodoItem> items)
{
Items = new ObservableCollection<TodoItem>(items);
}

public ObservableCollection<TodoItem> Items { get; }

public void RemoveSelectedItems()
{
for (var i = Items.Count - 1; i >= 0; i--)
{
if (Items[i].IsChecked)
{
Items.RemoveAt(i);
}
}
}
}
}
Pobiega
Pobiega11mo ago
now we've created the code to add the new entry to the database.json file
no we didnt we added code to add a new entry to your list
Mekasu0124
Mekasu012411mo ago
oh
Pobiega
Pobiega11mo ago
not the json file
Mekasu0124
Mekasu012411mo ago
missing that part then
Pobiega
Pobiega11mo ago
we never write to the json file, currently
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
discord shit its pants for me too.
Mekasu0124
Mekasu012411mo ago
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 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
- https://discord.com/channels/143867839282020352/1141209129654943754/1141626211030802533 We've already done the first one in the class constructor in Database.cs by doing
public List<TodoItem> Items { get; }
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
public List<TodoItem> Items { get; }
public Database()
{
string dbFile = "database.json";
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
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 here
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?
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
but it should be moved out of the constructor and into an async method, ideally
so how would I do this?
Pobiega
Pobiega11mo ago
I'm sure you could google that. but its also not the most crucial thing so leave it for now
Mekasu0124
Mekasu012411mo ago
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
Pobiega
Pobiega11mo ago
well step 1 is making a new method maybe call it... Save? 🙂
Mekasu0124
Mekasu012411mo ago
namespace Todo.Services
{
public class Database
{
public List<TodoItem> Items { get; }
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
- public async void Save()
+ public void Save()
{
string dbFile = "database.json";
-
- if (!File.Exists(dbFile))
- {
- File.Create(dbFile);
- }

var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFIle, content);
}
}
}
namespace Todo.Services
{
public class Database
{
public List<TodoItem> Items { get; }
public Database()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
- public async void Save()
+ public void Save()
{
string dbFile = "database.json";
-
- if (!File.Exists(dbFile))
- {
- File.Create(dbFile);
- }

var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFIle, content);
}
}
}
what about this?
Pobiega
Pobiega11mo ago
kinda. you dont need to create it before writing to it thou so that entire check can be removed
Mekasu0124
Mekasu012411mo ago
it'll create the file on it's own if it doesn't exist?
Pobiega
Pobiega11mo ago
yup remove the async btw
Mekasu0124
Mekasu012411mo ago
oh lol I was gonna be sneaky and just make it async already haha okie dokie
Pobiega
Pobiega11mo ago
nope, bad idea
Mekasu0124
Mekasu012411mo ago
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?
Pobiega
Pobiega11mo ago
sure, go ahead
Mekasu0124
Mekasu012411mo ago
I could, but I don't exactly know how to call them
namespace Todo.Services
{
public class Database
{
public List<TodoItem> Items { get; }
public Database()
{

}
public void Load()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info; // doens't like this
}
}
public void Save()
{
string dbFile = "database.json";
var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFile, content);
}
}
}
namespace Todo.Services
{
public class Database
{
public List<TodoItem> Items { get; }
public Database()
{

}
public void Load()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info; // doens't like this
}
}
public void Save()
{
string dbFile = "database.json";
var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFile, content);
}
}
}
Pobiega
Pobiega11mo ago
Read the error for why it doesnt like it.
Mekasu0124
Mekasu012411mo ago
Property or indexer 'Database.Items' cannot be assigned to -- it is read only
Pobiega
Pobiega11mo ago
indeed. So make it not readonly? 😛 { get; private set; } for example
Mekasu0124
Mekasu012411mo ago
I 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?
Pobiega
Pobiega11mo ago
sure
Mekasu0124
Mekasu012411mo ago
public List<TodoItem> Items { get; private set; }
public Database()
{
Load();
}
public void Load()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
public void Save()
{
string dbFile = "database.json";
var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFile, content);
}
public List<TodoItem> Items { get; private set; }
public Database()
{
Load();
}
public void Load()
{
string dbFile = "database.json";

if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
public void Save()
{
string dbFile = "database.json";
var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFile, content);
}
would this work?
Pobiega
Pobiega11mo ago
yup
Mekasu0124
Mekasu012411mo ago
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?
Pobiega
Pobiega11mo ago
not the constructor but to the class itself slap a const modifier on it too oh, and private
Mekasu0124
Mekasu012411mo ago
so I could do
public Database()
{
string dbFile = "database.json";
Load(dbFile);
}
public void Load(string fileName)
{
// rest of code
}
public Database()
{
string dbFile = "database.json";
Load(dbFile);
}
public void Load(string fileName)
{
// rest of code
}
Pobiega
Pobiega11mo ago
no well yes, but its bad so dont do that
Mekasu0124
Mekasu012411mo ago
ok so what would I do instead
Pobiega
Pobiega11mo ago
make it a private const string on the class itself and then you dont need to pass it around
Mekasu0124
Mekasu012411mo ago
public class Database
{
private const string dbFile = "database.json";
public List<TodoItem> Items { get; private set; }
public class Database
{
private const string dbFile = "database.json";
public List<TodoItem> Items { get; private set; }
Pobiega
Pobiega11mo ago
yes
Mekasu0124
Mekasu012411mo ago
namespace Todo.Services
{
public class Database
{
private const string dbFile = "database.json";
public List<TodoItem> Items { get; private set; }
public Database()
{
Load();
}
public void Load()
{
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
public void Save()
{
var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFile, content);
}
}
}
namespace Todo.Services
{
public class Database
{
private const string dbFile = "database.json";
public List<TodoItem> Items { get; private set; }
public Database()
{
Load();
}
public void Load()
{
if (File.Exists(dbFile))
{
var dbInfo = File.ReadAllText(dbFile);
List<TodoItem> info = JsonSerializer.Deserialize<List<TodoItem>>(dbInfo);
Items = info;
}
}
public void Save()
{
var content = JsonSerializer.Serialize(Items);
File.WriteAllText(dbFile, content);
}
}
}
ok so here's what I've got
Pobiega
Pobiega11mo ago
seems good.
Mekasu0124
Mekasu012411mo ago
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
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
- ViewModelBase content;
+ ViewModelBase _content;

public MainWindowViewModel(Database db)
{
- Content = List = new TodoListViewModel(db.GetItems());
+ Content = List = new TOdoListViewModel(db.Load());
}

public ViewModelBase Content
{
- get => content;
+ get => _content;
- private set => this.RaiseAndSetIfChanged(ref content, value);
+ private set => this.RaiseAndSetIfChanged(ref _content, value);
}

public TodoListViewModel List { get; }

public void AddItem()
{
var vm = new AddItemViewModel();

Observable.Merge(
vm.Ok,
vm.Cancel.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
List.Items.Add(model);
}

Content = List;
});

Content = vm;
}
}
}
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
- ViewModelBase content;
+ ViewModelBase _content;

public MainWindowViewModel(Database db)
{
- Content = List = new TodoListViewModel(db.GetItems());
+ Content = List = new TOdoListViewModel(db.Load());
}

public ViewModelBase Content
{
- get => content;
+ get => _content;
- private set => this.RaiseAndSetIfChanged(ref content, value);
+ private set => this.RaiseAndSetIfChanged(ref _content, value);
}

public TodoListViewModel List { get; }

public void AddItem()
{
var vm = new AddItemViewModel();

Observable.Merge(
vm.Ok,
vm.Cancel.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
List.Items.Add(model);
}

Content = List;
});

Content = vm;
}
}
}
so in my MainWindowViewModel, i would make these changes, right?
Pobiega
Pobiega11mo ago
no nononononon no.
Mekasu0124
Mekasu012411mo ago
oh
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
ok that makes sense. so what would I do with that line?
Pobiega
Pobiega11mo ago
change it to access the items.
Mekasu0124
Mekasu012411mo ago
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db.Items);
}
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db.Items);
}
Pobiega
Pobiega11mo ago
yep
Mekasu0124
Mekasu012411mo ago
ok and wouldn't I want to do ViewModelBase _content; instead of ViewModelBase content;?
Pobiega
Pobiega11mo ago
sure,
Mekasu0124
Mekasu012411mo ago
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db.Items);
}

public ViewModelBase Content
{
get => _content;
private set => this.RaiseAndSetIfChanged(ref _content, value);
}
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db.Items);
}

public ViewModelBase Content
{
get => _content;
private set => this.RaiseAndSetIfChanged(ref _content, value);
}
ok so we're here now. The next one is the public TodoLIstViewModel List { get; } which I'm pressuming I'd leave the same
Pobiega
Pobiega11mo ago
sure
Mekasu0124
Mekasu012411mo ago
public TodoListViewModel List { get; }

public void AddItem()
{
var vm = new AddItemViewModel();

Observable.Merge(
vm.Ok,
vm.Cancel.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
List.Items.Add(model);
}

Content = List;
});

Content = vm;
}
public TodoListViewModel List { get; }

public void AddItem()
{
var vm = new AddItemViewModel();

Observable.Merge(
vm.Ok,
vm.Cancel.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
List.Items.Add(model);
}

Content = List;
});

Content = vm;
}
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?
Pobiega
Pobiega11mo ago
something like that
Mekasu0124
Mekasu012411mo ago
ok so what are we thinking? I wanna see if I can take your idea and start figuring the code out first lol
Pobiega
Pobiega11mo ago
AddItem should probably also add the item to the databases list, then call a save actually, perhaps its time to redesign this a bit.
Mekasu0124
Mekasu012411mo ago
ok I"m all ears
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
idk what superflous means, but ok I'm down to only deal with one list
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
ok so do I need to change anything between - /Services/Database.cs - /Model/TodoItem.cs - /ViewModels/AddItemViewModel.cs
Pobiega
Pobiega11mo ago
yes 🙂 quite a bit
Mekasu0124
Mekasu012411mo ago
ok so before we do that, question
Pobiega
Pobiega11mo ago
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
Mekasu0124
Mekasu012411mo ago
would it be more beneficial to start the project over with a fresh project? ok no problem 🙂 thanks for your help
Pobiega
Pobiega11mo ago
Probably not. its a minor change, it will just touch many places maybe 15-20 lines at most, I think
Mekasu0124
Mekasu012411mo ago
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
Accord
Accord11mo ago
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.
Mekasu0124
Mekasu012411mo ago
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 ❤️
Accord
Accord11mo ago
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.