How to Add a Control to System.Windows.Controls.MenuItem Programmatically in WPF

I’m working on a new feature for Jenkins Toolset. It’s the feature that allows you to invoke a job with parameters. I need to add parameters to MenuItem according to parameter types. I have not completed the feature yet but I figured out a way to add controls programmatically, so here is the sample code.

var g = new System.Windows.Controls.Grid();
g.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition());
g.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition());

var lblText = new System.Windows.Controls.TextBlock();
g.Children.Add(lblText);
lblText.Text = $"{p.Name} ";
System.Windows.Controls.Grid.SetColumn(lblText, 0);

var txtBox = new System.Windows.Controls.TextBox();
txtBox.Width = 100;
g.Children.Add(txtBox);
System.Windows.Controls.Grid.SetColumn(txtBox, 1);

parItem.Header = g;

Here is the sample UI.

It took me a while to figure this out, so I thought I’d share it here in my blog. 🙂

Jenkins Toolset

I used to do a lot of automation with Jenkins. I worked for a software shop that had multiple instances of Jenkins for prod, stating and development with hundreds of jobs. It was not so easy to manage all of them without a tool, so I had decided to create a desktop software with WPF. I still have the project on GitHub today.

As the context menu indicates, it can do all of those things. It can list the jobs on the specified Jenkins instance and manage them. I made it downloadable on OCI’s Object Storage from here.

Here is the view of the builds of a job.

I would like to convert this project to a MAUI project eventually. I will write about the tool more later when I have some more time.

.NET 6

I don’t really talk about .NET on this blog, but I am very interested in .NET 6. I used to be a .NET developer and I used to write Windows Form applications, WPF, Windows Service and ASP .NET applications. The reason I have been away from .NET is because the current company I work for mainly uses Linux technology. (I applied for the job because I was very interested in the Linux realm.)

I am interested in .NET 6 (as of 9/13/2021, it is in preview 7) because it has a new feature of multi platform UI. When I develop software in the last few years, I have always cared for multi platform-ness. I tried Electron for the software I developed in WPF but it didn’t click with me because it’s mainly HTML and JavaScript. I should have pursued it but the thing is that I miss C# very much.

I downloaded the .NET 6 installer for macOS and installed it. After the installation, I opened terminal and ran dotnet. Here is the output I got.

I want to make sure I am using .NET 6 preview 7. So I run dotnet --version and I got the following output.

Now I am going to create a sample code. I run dotnet new --list to list the available project type.

I’m going to create a hello world sample code in console application. I run dotnet new console helloworld to create the sample project

Once dotnet new console helloworld is successful, you get a few files like this.

The Program.cs file already has a sample code like below.

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!"); 

To compile the code, you can run dotnet build in the same directory.

Once the compilation is successful, it creates bin/Debug/net6.0 directories. In net6.0 directory, it generates several compiled files like below.

As shown in the image, helloworld is an executable file, so you can be in net6.0 directory and execute it like ./helloworld But you can go back to 3 directories up and execute it like dotnet run.

According to the help, dotnet run builds and runs the project, so when you make changes to the code, you could just execute dotnet run to test things out.

I will continue to .NET 6 as time permits.

Python for C# Developers (control flow 1)

There are some differences in control flow in C# and Python though the concept in both languages is pretty much the same. Let’s take a look at them.

  1. Entry point
    The entry point to Python program can be expressed like the following. class is NOT necessary in Python, so if you are working on a simple script, I would just start to write logic in the file without a class. That said, I still like to encapsulate everything classes.

    class Main:
        def __init__(self):
            pass
    
        def say_hello(self):
            print('hello Python!')
    
    
    if __name__ == '__main__':
        entry_class = Main()
        entry_class.say_hello()
    
  2. if elif else
    Let’s think about the function that returns month name in a string.

        def get_month_name(num):
            if num == 1:
                return "January"
            elif num == 2:
                return "February"
            elif num == 3:
                return "March"
            elif num == 4:
                return "April"
            elif num == 5:
                return "May"
            elif num == 6:
                return "June"
            elif num == 7:
                return "July"
            elif num == 8:
                return "August"
            elif num == 9:
                return "September"
            elif num == 10:
                return "October"
            elif num == 11:
                return "November"
            elif num == 12:
                return "December"
            else:
                return "Invalid number" # Could throw an error

    I know the sample is ugly, but you get the idea. switch statement is NOT available in Python unfortunately but there is a workaround. I will write about in a separate blog post.

  3. while
    i = 0
    while i < 100:
        print(i)
        i += 1

    Not so elegant but while is still a necessary control statement.

  4. for
    nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    for n in nums:
        print(n)

    for in Python acts like foreach in C#. If you want to express something like for (int i = 0; i < nums.length; i++) {}, you can do something like the following.

    for n in range(1, 10):
        print(n)

    The sample shows that range function can be used to mimic “for (int i = 0; i < nums.length; i++) {}” in C#.

Python for C# Developers (try catch finally)

 

try, catch/except, finally is a necessary control flow in any modern programming language. Here is one in C#.

var obj = new MyObj();

try
{
    obj.func1("bad data"); // This line can cause an error.
    // Do some other work here if error didn't happen.
}
catch(SomeException exp)
{
   ExceptionHandler(exp);
}
finally
{
    obj.CleanUp();
}

Here is the sample in Python.

obj = MyObj()

try:
    obj.func1("bad data") # This line can cause an error.
except SomeError as exp:
    exception_handler(exp)
else:
    # Do some other work here if error didn't happen.
finally:
    obj.clean_up()

Notice that Python can have else clause but it’s not necessary to use it. I would use it if I want a very clear separation between “trying code” and the process after it.

There may be situations where you need to raise errors. You can simply use raise keyword like the example below.

raise ValueError("Entered value is not valid.")

There are quite a bit of builtin error types. They can be found in this document.

Lastly, if you need to create your own exception type, you can create a class that inherits from Exception class.

class MyError(Exception):
    pass

 

Python for C# Developers (function)

I think I wrote C# code the most in my career. C# is almost like a mother tongue to me just like Japanese language is though I’ve been speaking “foreign languages” like Groovy and Python quite a bit. When I have to think deep about something, I always go back to my mother tongue. I think I’m going to try it out with C#. In this series, I’m going to write some sample code in C# and then translate it into Python.

I won’t start like what variables are, strings are, integers are… those very basic concepts like in any other “tutorial” sites. I will go straight into what may be useful.

I’m going to talk about functions this time. Functions can be void or ones that return something. Let’s see them in C#.

using System.IO;

public class Functions
    {
        public void WriteString(string path, string text)
        {
            File.WriteAllText(path, text);
        }

        public int Add(int a, int b)
        {
            return a + b;
        }
    }

Now let’s see them in Python.

class Functions:
    def WriteString(self, path, text):
        with open(path, 'w') as f:
            f.write(text)

    def Add(self, a, b):
        return a + b

This is not really “Pythonic”. Let’s change the Python code into more Pythonic way.

class functions:
    def write_string(self, path, text):
        with open(path, 'w') as f:
            f.write(text)

    def add(self, a, b):
        return a + b

Python coding standard is, in short, lower case everything connecting words with underscore. The details can be found here.

It’s very easy to see what the differences are but I will try to explain in words.

return keyword returns a value to the caller of the function.

To create a class, Python uses the same “class” keyword. Whenever you use a keyword (such as if, while, for and etc…), it has to end with : (semi colon). When you define a function, it has to start with def. There is no distinction between function that returns value or not.

“with” keyword in Python is equivalent to using in C#. It makes sure that any resource (such as file, database or network connection) opened this way will be closed when the code gets out of the context. (context manager)