The Einhugur framework has similarities to Xojo in many ways
It has a Canvas, just like Xojo
And when you use its paint event you can do many of the same things like drawing text and shapes and pictures.
We can add a canvas just like other controls. And we can implement the paint event in the same way.
canvas = new Canvas(left,top,width,height);
canvas.Paint += canvas_Paint;
And the paint event would look like
private void canvas_Paint(object sender, PaintEventArgs args)
{
GraphicsContext g = args.Graphics;
// GraphicsContext is in many respect like Xojo's
// Graphics
}
Now you can change the fill colors, stroke colors (borders), and draw shapes & text. You also have built in rotation, clipping, and scaling. There are no bezier paths and some more complex drawing operations – yet.
One thing you will notice is that getting an RGB or RGBA color is a little different. Since colors in the Einhugur framework are based on CGColors internally they permit a really wide range of colors. Much more than the 0 – 255 based Xojo colors. So, each component of a RGB color is a floating point value in the range 0 – 1 like the following
yeah ok bad joke. I’ve been so dang busy with studying and work that I just haven’t had a reasonable chance to write any updates for the C# on macOS series.
But now that my Canadian Ski Patrol exams are done I can get back to some fun.
How to make our little project use a thread instead of a timer. The biggest place in the project you might notice this is in the level indicator.
Right now when you press & hold a menu item the updates to the level indicator will pause until you release the menu item. It sure would be nice to not have that occur.
And, the reality is that since we are using C#, most of what we need is already built in, or easy to acquire.
We’ll need to add a Nuget package
We’ll need to add the Xamarin Essentials package
And we’ll want to add a using clause so the compiler is alerted to the fact we’re going to use the Xamarin.Essentials package.
We need to declare a variable to hold out thread instance
We’ll change the window SetUpControl method to NOT create a timer but instead create a Thread.
This new line creates the thread and tell it what method to call when the thread runs. This method is one that gets called ASYNCHRONOUSLY – so it has some slightly special set up.
Note the ASYNC keyword on this method – thats the entire “special set up” π
The other thing I’ve done is refactored the old timer code so the thread and timer can both use the same method to update the indicator.
The only remaining thing to do now is start the thread running right where we used to have the Timer getting started.
Now because we have added some C# runtime references, and a using clause for System.Threading, you may see an error like
You can have VS take you right to the location of the error by right clicking the error and choosing “Go To Task”
We can correct the issue by simply fully qualifying the Type name to Einhugur.Threading.Timer
Once fixed you should be able to run and, note what when you press & hold the Edit item in the menu bar the level indicator continues to move up & down unlike what it did with the timer.
The speed with which the indicator moves up and down can be adjusted by changing the sleep value for he thread. The shorter the sleep period the faster indicator will update.
Anyone following along that has poked around what exists for the Einhugur C# UI Framework should notice that there IS a Timer object in there already.
Since its in the Einhugur.Threading namespace we need to add that using clause to our project
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using AppKit;
using CloudKit;
using Einhugur.Forms;
using Einhugur.Threading; // <<<<<<<<<<<<<<<
using Mono.Data.Sqlite;
using Npgsql;
Using one is as simple as declaring the property for it, creating it, setting its period, or interval, and adding a method for it to call whenever the Timer needs to run some action.
timer = new Timer();
timer.Interval = 0.05;
timer.Action += timer_Action;
One thing you dont need to do, since its not a visual control, is add it to the list of controls in SetUpControls. Since its actually a property on the layout thats enough for it to function exactly as we want.
However a timer by itself isn’t very interesting. Let’s have it drive something visual like a Level Indicator.
Indicators come in several styles. They are explained on this Apple page. But they are easy to play with. One gives you a segmented capacity, like an audio meter, one a continuous bar that has not marked gradation, one shows as a star rating. Try them all π
Certain settings, like critical fill color warning fill color, warning value and critical value may not make sense with some styles.
If you use the discrete and continue capacity styles they do. And when the value of the level indicator is above the warning, and less than critical that portion of the indicator in the warning color. Same for critical. We’ll set all these up and hook it to a timer that will reveal what I mean.
Using the settings above now all we need is the timer action method to do something to the level indicator. Lets make it count up and down to animate the level indicator.
private int increment = 1;
private void timer_Action(object sender, EventArgs a)
{
if (indicator.Value <= indicator.MinValue)
{
// reverse and go up
increment = 1;
}
else if (indicator.Value >= indicator.MaxValue)
{
// reverse & go down
increment = -1;
}
// change the level indicator
indicator.Value = indicator.Value + increment;
}
OK we have most everything set up. Instead of hooking this timer up to start when we push the button on our layout, which would be simple, lets start it when the window opens.
We dont want to put the call to the timers start method in the Setup of the controls. So how do we get this into what would be the equivalent of the Open event ?
If you right click on “Window” in our definition of the class
public class MainWindow : Window
and select “Go To Definition”
you get the assembly browser. In there you can see that a Window has an Opening method we can make use of. Perfect !
To override the default implementation we need to tell the compiler this IS an override
Often the easiest way to get the right definition for the method you want to override is to copy the declaration from the assembly browser, paste it into your code and change virtual to override.
And now as the timer runs its action event it affects the value of the level indicator. On the way up as the level indicator gets to the warning value the color changes to the warning fill color. And when it gets to the critical value it again changes. And as it goes back down in value the colors change again.
A new version of the Einhugur C# UI framework was released a short while ago.
Since the framework is still literally a work in progress some changes an dupes are to be expected. Listbox now exists and its constructor now takes an array of column titles as well as the initial dimensions. So you may need to update code like
list = new ListBox(10, 20, 80, 20 );
to add a new last parameter
list = new ListBox(10, 20, 80, 20, new string[] {"col 1", "col 2"} );
to set the column titles.
Dont be surprised won the road if there are several constructors to pick from where you dont have to set the titles right then.
With that out of the way lets turn to using the Mono.SQLite database – with one on disk !
In prior editions of the tutorial we used the in memory SQLite database. We had
sqlite_conn = new SqliteConnection("Data Source=:memory:");
To switch this to use a database on disk we just need to alter the connection string.
sqlite_conn = new SqliteConnection("URI=file:/Users/npalardy/testdb.sqlite;");
With the Mono driver for SQLite when you connect this will create the database if it doesnt already exist.
And .. tada ! Thats it π Instead of an in memory database now we are using one from disk. Here’s the project as it exists so far.
In the poll I posted half the respondents πSia they wanted to see how to use a different db. So we’ll alter this to use PostgreSQL since I happen to have a handy configuration already on my machine – but the steps will be similar for most databases.
First we need to grab a PostgreSQL database driver. We’ll add a NuGet package for this. Right click on Packages
We can filter using the field in the upper right to quickly find relevant packages
Note there are many PostgreSQL packages we can pick from. I honestly dont know all the pro’s and cons of the different packages. I have used the Nppqsql package ands so far it works as I need & expect.
Select that package and add it to the project. You may see several other packages also get aded. When a package depends on another to be able to work the packages that are depended on are also brought in automatically.
In Xojo terms we’ve basically added a “plugin” – but only to this project. Projects in VS have “per project plugins”
In our code we’ll need to addd a using clause to let the compiler know it should expect references to this new plugin in this code.
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using CloudKit;
using Einhugur.Forms;
using Mono.Data.Sqlite;
using Npgsql; // <<<<<<<<<<<<<<<<<
As well we need to adjust our connection so instead of using SQLite it uses PostgreSQL.
// I deliberately left the variable name the same so you can see how
// little needs adjustment
// Mono.Data.Sqlite.SqliteConnection sqlite_conn;
NpgsqlConnection sqlite_conn;
And the code where we create the connection needs to connect to the server
// sqlite_conn =
// new SqliteConnection("URI=file:/Users/npalardy/testdb.sqlite;");
sqlite_conn =
new NpgsqlConnection("Host=localhost;Username=XXXX;Password=XXXX;Database=norm_csharp");
The ONE other things we have to account for, since we’re using raw SQL. While the following will work for SQLite it wont work for PostgreSQL.
IDbCommand dbcmd = sqlite_conn.CreateCommand();
string sql = "create table if not exists employee ( firstname, lastname )";
SQLite is OK with an untyped create statement. PostgreSQL isn’t. So we do need to alter that as well.
IDbCommand dbcmd = sqlite_conn.CreateCommand();
string sql = "create table if not exists employee ( firstname varchar(50), lastname varchar(50) )";
And with that change everything should work again and now were using PostreSQL.
We could use a more generic data type for the now badly named sqlite_conn. Something more akin to Xojo’s Database. We could declare it as
System.Data.IDbConnection sqlite_conn;
And our code could create & assign either a new sqlite connection or PostgreSQL one
If you’ve been following along with my C# series you should be able to open your copy of the app we’ve been creating and update the Einhugur Forms nuget package.
This brings along some new controls to use as and some additions to existing ones
You’ll see there’s a dat picker, disclosure triangle, combobox and additions to the listbox for headers.
We’re still not to a “1.0 version” but we are working on moving things ahead as we can
Dont hesitate to note if there’s something specific you need, or see missing.
What we as users get told is “report your issues, upvote them as this influences priorities” So many do ; or did. I’ve got far too many people telling me I just gave up reporting them as they never get fixed. From names that if I listed them would surprise people.
Since I still use Xojo for client projects I report bugs and tell my clients to advocate for them getting fixed.
Great !
So report them upvote them and … they dont get put on a milestone to get fixed ?
There are several open bugs with more upvotes than open reports that are assigned to milestones
The top 3 open bugs have higher popularity than the items assigned to 2022r4
The top 6 open bugs are at least the same popularity as the top two items assigned to 2022r4
Trust me I know the “dodge” that gets rolled out Its not the only criteria we use. Sure but what criteria DO get used ? How else do you judge how many people might be affected if not by upvotes ? The IDE doesn’t report back how many people use XYZ in their code so how else can you judge the impact ? The number of duplicate reports ? Even Issues isn’t any better at detecting those than any prior system was.
Personally I’d like a less “hand wavy ignore the man behind the curtain” way of knowing how to influence priorities. Make upvotes ACTUALLY count for what gets put into a milestone. Clear prioritization criteria would be VERY welcome.
As I’ve often been told “Watch what they do. Not what they say.” We’re told to report upvote etc Yet that isn’t what we see getting addressed
If you’ve ever had to write a lot of output with strings you’ve probably had to concatenate them.
In VB you used &. In Xojo you use + and more than likely Str, Format, and the newer ToString.
Eventually when you write enough code you end up with things that look like
Var Explanation as string = "Hint: If you don't save," _
+ " you will lose your work" _
+ " since your last Save."
You use the line continuation characters and spread it across multiple lines. And if you need to actually insert user generated vales you end up with code that gets increasingly hard to read
Var Explanation as string = "Hint: If you don't " _
+ action _
+ "," _
+ " you will lose your work" _
+ " since your last" _
+ action _
+ "."
If you have to add in numbers or other items that require the use of Str, Format, ToString, etc it gets cumbersome*
What I’ve become really enamoured with in many other languages is a thing called string interpolation.
In C# the previous example might look like
string Explanation = $"Hint: If you don't {action}, you will lose your work since your last {action}."
I find this quite a bit easier to read. Note the $ before the first quit – this is the indicator to the compiler this string should be interpolated. When that occurs the runtime will replace {action} with the contents of a variable in scope and insert it right there !
Perhaps one of the most useful is multiline string literals
string rawStringLiteralDelimiter = """"
Raw string literals are delimited
by a string of at least three double quotes,
like this: """
"""";
Console.WriteLine(rawStringLiteralDelimiter);
will output – end of line and everything
You might be wondering What if what I want to insert isn’t a string ?
That still works too !
string action = "save";
int foo = 90;
string Explanation = $"Hint: If you don't {action}, you will lose your work since your last {foo}.";
Console.WriteLine(Explanation);
would output
Its very handy and very powerful as there are all kinds of formatting options you can use in interpolated strings.
And you can get away from writing + π
* I have written code where my own classes have operator_convert to automagically turn themselves into a string and that can make life a bit easier. It just doesnt happen with built in intrinsic types in Xojo. You can’t write :
dim I as integer
dim d as date
dim s as string = "Today," + d + " we counted " + I + "twiddles"
There are some interesting StringBuilder implementations but its still much more cumbersome than interpolation in many cases.
in this youtube video posted from the recent Developer Retreat https://youtu.be/mWEH0kjXZiA?t=2091 geoff talks about the roadmap in the screen shot below I took a screen shot since I knew it was going to change.
Items highlighted in red have disappeared from the new roadmap
Yet, on the testers channel, they already had a complete replacement for it (also screen shot when I learned of this)
I was more surprised that they talked about it in the keynote knowing that the roadmap was going to change and drop many items. Why not just say that in the keynote ?
Sure some people will be disappointed some things get removed. That happens. But why talk about the one that was posted as if “this is what’s coming” knowing its going to change ?
Since I’m writing this BEFORE 2022r3 ships I guess we’ll see what the fall out is once this becomes apparent.
One of the common things we need our applications to do is interact with a database. Sometimes its just a local SQLite, or other stand alone db engine like that, or some “Big Iron” database like Oracle, MS SQL Server, PostgreSQL, etc. Or maybe a NoSQL database like Mongo ? With Xojo there are only a handful of plugins Tod ell with the vast majority of databases. Either you use the ones provided by Xojo. Or the ones from MBS or Valentina, or CubeSQL. There just arent that many vendors or plugins to have to wade through
In C#, because its widely available on so many platforms, there are tons of NuGet packages to select from. I cant tell you all of them or which specific ones will / wont work on macOS. I haven’t tried them all π Typing in “database” to the search field in the package manager gives a scrolling list that is immense.
325,832 packages is much like saying “there are 325,832 plugins for Xojo” Christians good but I dont think even he’s got that many π
So there is a LOT to choose from and databases are no exception. This chapter isn’t a “how to do anything with any database in C#” but how to use one specific NuGet package to use SQLITE.
So lets get started !
I’m still adding to my standard “Getting Started project”
In order to “add a plugin” in Xojo you’d add it to the IDE’s Plugins, restart and it would be available in every project you opened. VS works differently and each solution can have a different list of packages and even different versions of the same package.
For our purpose we’re going to add a reference to our project to make this package usable in our code.
Select the references item and right click (or you can just select the Project > Add Reference… menu item)
For THIS project we’re going to add Mono.Data.SQLite and System.Data Check the check box next to them and they’ll be added to the list on the right
Press select & they’ll be added to your project.
Our app doesnt have a lot of code in it – yet – so tying a database into it is a little contrived. All we have is a window that we have textfields on and it just has a got focus event. Lets add a listbox to the window so we can put some data into something familiar.
I defined 2 constants for the default height and width of the window so I could reuse those elsewhere. And yes, control locking works just like in Xojo π
namespace getting_started
{
public class MainWindow : Window
{
const int kDefaultWidth = 600;
const int kDefaultHeight = 400;
And I altered my SetUpWindow method to use those constants instead of the hard coded literals.
What you should notice is how C# does a CAST – in Xojo its like
INTEGER( expression you want treated as an integer )
almost like a function call. In C# its
(INTEGER) expression you want treated as an integer
so you have to be sure to put brackets around the expression you want to cast. Often I just write
(INTEGER)( expression you want treated as an integer )
just to be safe. BU the “style gods” and language pedants get upset at this verbosity π
OK so now we have a list to show data in. We could load the data in the control creation method but we’ll add it elsewhere since thats more like what we would do in many cases. I’m going to add a button next to the lower text field, just because that seems a good place, and then hook the database code into the pushbutton push event. I added a declaration of the button with the listbox and other controls :
Button pushButton;
and added this code to the method creating all the controls
You should still be able to run & the button will just do nothing.
To use Sqlite in this code we will need to add a using statement.
using Mono.Data.Sqlite;
Typically these are all at the top of whatever source code file you’re working in. I like to group them
The Sqlite package we added uses a form of access to the DB that is very similar to what Xojo has, but its not identical.
The code we’re going to add will use local variables, but moving those to be application or window wide is a trivial operation so it really won’t matter much.
First we need to declare a variable to hold the Sqlite_connection – this would be analogous to defining a variable for the SQLiteDatabase object in Xojo.
Mono.Data.Sqlite.SqliteConnection sqlite_conn;
// in Xojo var sqlite_conn as SQLiteDatabase
Then we need to create the instance – this will attach to an in memory database. In Xojo this is different – we just dont open a database file
sqlite_conn = new SqliteConnection("Data Source=:memory:");
Then we actually tell this new connection to OPEN and attach to whatever database we set up as the data source
sqlite_conn.Open();
About this point you’re wondering Great but how do I check errors ? This is one are where .Net and Xojo, now, agree. .Net uses EXCEPTIONS for error handling ( at least with this database although I expect others are similar)
All the rest of the code we’ll add will be inside the TRY CATCH block following the opening of the connection.
The API that .Net uses for many database is ADO. Its a lot like what Xojo uses but has syntax differences. See https://learn.microsoft.com/en-us/sql/ado/guide/ado-programmer-s-guide?view=sql-server-ver16
About the biggest difference is that ADO seems a tad more low level. In some ways that makes certain thinks possible and in other ways it’s a pain because it means you write more code.
Onwards !
In order to RUN any sql command we need a command object – an IDbCommand. Then we configure that object and then tell it to execute.
Just as in Xojo there are several ways to execute sql statements – some that return results, some that return a single result, or ones that return no results at all. The previous code returns no results hence its a “non-query”.
And since this is an in memory database we’ll just add a few rows to it.
Now we have a database connection, a table, and some data in it. To read that data we’ll need the ADO equivalent of a Xojo recordset or rowset. This is an IDataReader. Also since the command were going to execute will return a reader well just a different method – just like in Xojo where you’d use SQLSelect or SelectSQL.
One thing to notice is that unlike Xojo’s rowset and recordset YOU dont have to remember to say “move to the next row” – and if you’ve ever forgotten that and wondered why your application runs into an infinite loop you’ll appreciate this.
Reader.Read not only moves to the next row of the returned rows it also returns a boolean value when it succeeds. This way you can move to the next row without having to write any code. Make sure you check out ALL the possible GetXXXXX methods that the reader has – they are extensive. In our simplfusage above we just retrieve, by column position, the strings returned and add that as a single row to the listbox.
As a good citizen we should ALWAYS dispose of the objects we created.
// clean up
reader.Dispose();
dbcmd.Dispose();
sqlite_conn.Close();
And – were done !
Note there are things I have not covered
creating a new database
configuring the connection to read from an existing db file (see For the 2.0 profile in the new assembly on https://www.mono-project.com/docs/database-access/providers/sqlite/)
yes I’m still working on this and am figuring out which of the gazillion database modules available for things like Oracle, SQLITE, etc work on macOS
With .Net Core and some of the supporting modules being fairly new to macOS not all of them work. There seem to be several choices for using any of the db’s from MS SQL Server, Oracle, PostgreSQL, Mysql, MariaDB etc all the way to ones I’ve never heard of