Making of a Software Application from Zero – Part 2 of n

In the first part I wrote a bit about what I want to do and did a small investigation to see if this is technically possible or not. Now it’s time to think a bit how the application will function, how it will run and based on these, how I will build it.

Big picture design

In essence, our application model is a list of screens, each screen having some properties (like layout, number of fields) and a list of fields. The core model is quite simple:

Read More »

Making of a Software Application from Zero – Part 1 of n

The Problem

I have a few Garmin devices to track my outdoor activities. During an activity, each device shows different screens, each screen having 1 to 10 data fields like speed, distance, power, rode grade, around 100 different data fields.

When it comes to changing these data fields, the only available option is to do this on the device and this is quite tedious especially when you have only two or three physical buttons and no touchscreen and you need to scroll and scroll through tens of fields and screens.

It would be nice to have an application on the PC or on the mobile to edit those fields but Garmin doesn’t offer one. Googling around didn’t reveal anything useful, only more people wishing there was one.

Motivation

Motivation is a key factor not only for starting to work on a new software application but even more for getting it done.

Read More »

Expandable WPF ToolTip

A colleague of mine came to me the other day with a challenging thing: the UX team is requesting to have some tool tips that, when pressing Ctrl key, should expand and show more details, more content.

This is tricky because of a number of reasons. First of all, tooltips don’t get focus so they can’t subscribe to any KeyDown events. Second, tooltips don’t inherit the DataContext from the parent control so the communication between the parent and the tooltip is a bit harder.

Read More »

File type associations in Windows 10

Ever since Windows 3.1, the association between a file type (like png, or txt) and an application has been done through a few keys in the Windows Registry, as explained in the documentation on MSDN.

HKEY_CLASSES_ROOT
    .jpg
        (Default) = Bearly.jpg
    .Bearly.jpg
       (Default) = BearlyPhotoViewer
    BearlyPhotoViewer
       (Default) = "Bearly.exe" "%1"

But starting with Windows 8 and continuing in Windows 10, this is not enough anymore. Windows now allows the user to associate a file type with a certain application only via the built-in GUI. The above registry keys, although still necessary, are not enough anymore to achieve this association.

Read More »

HTML full screen layout

Having more of a WPF background, it’s hard for me to understand why in HTML/CSS it’s so difficult to create full-screen UIs. In WPF, with a relative small number of layouts, it’s easy to arrange and align anything anywhere. Align a panel to the bottom of the screen, centering panels inside other panels or overlapping panels is easy to implement and easy to understand.

Creating full-screen UIs in HTML has always been complicated. But the show continues even today when IE is just a medieval story and there are different systems (flexbox, grid) that are supposed to make things easier.

Read More »

Easy CD with GitHub Workflows

When working on small utilities / tools kind of projects that you upload somewhere for others to be able to quickly get them, more often than not it becomes difficult to re-upload after you add a new feature, especially if some time has passed by since the last upload.

Here GitHub workflows come to help. Every time you push something to a specific branch, GitHub can trigger an action to run a small script that can compile your application, run unit tests, publish it and upload it to the desired download site.

One of my simplest workflows looks like this:

name: .NET Core
on:
  push:
    branches:
      - master
jobs:
  build:
    runs-on: windows-latest
    steps:
    - name: Display the path
      run: echo $PWD
      shell: bash
    - uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '3.1.100'
    - name: Publishing
      run: dotnet publish --configuration Release --self-contained:false
    - name: Installing lftp
      run: choco install lftp
    - name: Uploading to ftp
      run: lftp ${{ secrets.FTP_SERVER }} -u ${{ secrets.FTP_USERNAME }},${{ secrets.FTP_PASSWORD }} -e "set ftp:ssl-allow no; set ssl:verify-certificate no; mirror --reverse --continue --dereference -x ^\.git/$ ScreenZoomPlus\\bin\\Release\\netcoreapp3.1\\win-x86\\publish ScreenZoomPlus; quit"

(the file goes inside the repository, in the .github\workflows\name-of-the-file.yaml)

The file is kind of self descriptive (but there is also plenty of documentation):

The workflow is triggered every time something is pushed on the master branch. Usually, on my hobby-projects, I work on a dev branch and when I’m happy with what I did, I push everything to the master branch. Then the GitHub action kicks in.

Everything is run in like a VM environment, nothing is preinstalled, so first step is to make sure .Net core is there. Then dotnet publish is called to build and create the final executable with all the dependencies.

The tricky part was to upload the exe file to FTP. For that I install lftp (through Chocolatey) and lftp is able to upload to a FTP server.

Some variables (secrets.FTP_SERVER, FTP_USERNAME and FTP_PASSWORD) are then used in order not to keep the sensitive data in plain text in the repositories (modern CI/CD). These variables are defined in the Settings > Secrets section of the GitHub repository.

My git commands

1.

git commit --amend --date=now --no-edit

to change the date of the last (local) commit. –no-edit for not modifying the commit message, otherwise it will open a text editor to enter new commit message.

2.

git commit --amend -m '#572: Fixed crash'

to change the message of the last (local) commit.

3.

git merge feature/CoolFeature --no-ff

merge branch ‘feature/CoolFeature’ without fast-forwarding current branch

4.

git merge feature/CoolFeature --squash -m '#573: CoolFeature'

will combine all the commits of ‘feature/CoolFeature’ into one single commit and merge that commit to current branch.

Smoother log4net integration in .Net applications

Part 1

Many developers I’ve seen and many blogs on internet use the following line to declare a Logger in a class:

private static readonly log4net.ILog Log = 
    log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

Although it might bring some advantages in some cases (inheritance), most of the time it’s not really necessary to obtain the surrounding Type by calling reflection, so I generally prefer to use:

private static readonly log4net.ILog Log = 
    log4net.LogManager.GetLogger(typeof(MyClass));

where MyClass is the class I’m adding this as a member;

This is a bit prone to Copy/Paste mistakes when this line is taken from one class and pasted in another, and you forget to change the name of the class. So, to overcome this, I’ve made Visual Studio code snippet:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
      <Title>log</Title>
      <Author>[USERNAME]</Author>
      <Description>
      </Description>
      <HelpUrl>
      </HelpUrl>
      <Shortcut>log</Shortcut>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <ID>className</ID>
          <ToolTip>The full name of the type will be used as the name of the logger to retrieve.</ToolTip>
          <Default>
          </Default>
          <Function>ClassName()</Function>
        </Literal>
      </Declarations>
      <Code Language="csharp" Delimiter="$"><![CDATA[private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof($className$));]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

Put this into a file named log.snippet and save it to your Documents\Visual Studio 2017\Code Snippets\Visual C#\My Code Snippets\ folder and when you will write log in Visual Studio and press TAB twice, the line will be inserted with the name of the class correctly updated.

 

Part 2

There is an online, unofficial but complete, schema that can be used to have IntelliSense when editing the log4net.config file. You can download it here: http://csharptest.net/downloads/schema/log4net.xsd.

Custom window movement and resize in WPF

Windows can be moved on the screen by dragging their title bar; they can be resized by dragging their borders. But what if our window needs no title bar / borders (or they are custom drawn)? (aka WindowStyle="None").

Introducing MoveResizeBehavior, a WPF custom behavior that can be added to any FrameworkElement will provide move and resize functionality for its parent window.

<Border DockPanel.Dock="Top" Height="11" Margin="7"
        b:MoveResizeBehaviour.ResizeWindow="Top"/>
<Border DockPanel.Dock="Bottom" Height="11" Margin="7" 
        b:MoveResizeBehaviour.ResizeWindow="Bottom"/>
<Border DockPanel.Dock="Right" Width="11" Margin="7,0" 
        b:MoveResizeBehaviour.ResizeWindow="Right"/>
<Border DockPanel.Dock="Left" Width="11" Margin="7,0" 
        b:MoveResizeBehaviour.ResizeWindow="Left"/>
<Border Height="43" VerticalAlignment="Top" 
        b:MoveResizeBehaviour.MoveWindow="True"/>

where

xmlns:b="clr-namespace:AndreiPana.WPF.Utilities"

ResizeWindow can take one of the following values: None, Left, TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, while the MoveWindow can be either True or False.

Internally, the behaviors attach to the MouseDown event, so the FrameworkElements on which they will be set better not process themselves MouseDown, otherwise it might interfere with the behaviors.

A simple and small test project showing the functionality is available on Github but, for convenience, here’s the MoveResizeBehavior.cs file that you can copy/paste to any WPF project and used it as it is: