Author Archives: admin

Playing a metafile

To show a metafile

metaFile = new Metafile(path);
e.Graphics.DrawImage(metaFile, clientRect);    
metaFile.Dispose(); 
metaFile = null;

Although this code shows the metafile, you may notice strange scaling problems. The metafile is being drawn ignoring the aspect ratio!

You can get the original size using the following code

MetafileHeader header = metaFile.GetMetafileHeader();
Rect metaRect = header.Bounds;

Use the metaRect to adjust the clientRect to get the same aspect ratio and the scaling will be fine.

Get the application folder

To get the path where the executable is located use the following code:

Path.GetDirectoryName(Application.ExecutablePath);

The returned path does not contain a trailing backslash.

To add a filename or mask use Path.Combine

Allow a UserControl to handle arrow keys

By default the arrow keys are processed by the form, to use them yourself override the following method.

 protected override bool IsInputKey(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                    return (true);
             }
            return base.IsInputKey(keyData);
        }

					

Prevent panels to be repainted on a form resize

To prevent lots of flicker during resizing disable the real-time resizing of the child controls.
To do so override both the Form OnResizeBegin and OnResizeEnd events.

protected override void OnResizeBegin(EventArgs e) 
{
    SuspendLayout();
    base.OnResizeBegin(e);
}

protected override void OnResizeEnd(EventArgs e) 
{
    ResumeLayout();
    base.OnResizeEnd(e);
}

Reduce flickering by disabling OnPaintBackground

When an entire control is painted (e.g. hosting a windowed directx control) it can be beneficial to override OnPaintBackground

In case of a windowed directx control double buffering is not possible, in other cases it may not be desirable.

When the background is not shown because it’s overlapped entirely, not drawing it all will speedup things and reduce flicker.

Add the following to the control to override.

protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
{
}

Cannot modify the return value of System.Collections.Generic.List.this[int]‘ because it is not a variable

This error is shown when an attempt to assign a value to a property inside a struct.

e.g

System.Drawing.Point p = new Point();
p.X = 1; //legal of course

Listlist = new List();
list.Add(p);
list[0].X = 123; //Cannot modify the return value of System.Collections.Generic.List.this[int]‘ because it is not a variable

Remember structs are value types. They are processed like integers and only assigned as a whole.
Therefore when accessed from a list a copy of the struct is returned. Assigning to the copy is pointless, hence the error.

You can assign one member by using the following method

System.Drawing.Point p = list[0];
p.X = 123;
list[0] = p;

Calling a form control from a (worker)thread.

When using multiple threads controls on a form cannot be called directly. Doing so will throw an exception like ‘Cross-thread operation not valid: Control ‘dc’ accessed from a thread other than the thread it was created on.’

Fortunately there is a rather simple solution for this problem. Use the Invoke method to execute code in the GUI thread.

The invokemethod has the following prototype

public void Invoke(System.Delegate delegate);

For example: from a worker thread a button on a form (Form1)  must be pressed. From inside the thread use the following construct to perform it thread safe.

Invoke((MethodInvoker)delegate
{
   Button1.Click();
});

Invoke is a member of the Control class. To make it work use a control created in the same thread.  In the example above you can use Form1.Invoke() or Button1.Invoke() since they both are created in the same thread.

MethodInvoker is a default delegate definition. Its definition is  delegate void MethodInvoker;

External delegates:

Of course the delegate can be external as well, sometimes this is favorable to get cleaner code or if the delegate is frequently used.

delegate void ClickTheButton();
void clickTheButton()
{
   ...
}

Inside the method

ClickTheButton clickTheButtonDelegate = clickTheButton;
Invoke(clickTheButtonDelegate);

 

Delegates with parameters

Delegates can also be called with parameters . This uses the second form of Invoke()

public void Invoke(System.Delegate delegate, object [] args);

void clickTheButton(int value)
{
   ...
}

delegate void ClickTheButton(int);

Inside the method

 ClickTheButton clickTheButtonDelegate = clickTheButton;
 Invoke(clickTheButtonDelegate, new object[] {1});

Calculate a point at a distance perpendicular to a vector offset (2D)

To calculate a point at a distance perpendicular to a vector offset

Point perpendicular to offset

First the point on the vector P' at the required offset needs to be calculated. This easy to do:

P' = A + (B-A) * offset

P'_x = A_x + (B_x - A_x) * offset
P'_y = A_y + (B_y - A_y) * offset

The vector to move the point P’ on is perpendicular to \overline{AB}. This vector can be calculated using a 90 degree rotation matrix:

\begin{bmatrix}  cos \alpha   & -sin \alpha \\   sin \alpha  & cos \alpha \\ \end{bmatrix}   = \begin{bmatrix}  cos 90  & -sin 90 \\   sin 90  & cos 90\\ \end{bmatrix} = \begin{bmatrix}  0  & -1 \\   1  &  0 \\   \end{bmatrix}

The matrix shows in the the first row x'= -y and the second row shows y'= x

You can safely ignore the stuff above if you are only interested in getting the job done. The final formula is:

\begin{pmatrix}x'\\y' \end{pmatrix}= \begin{pmatrix}-y\\x \end{pmatrix}

We will call the vector (\overline{P'P}) vector C.

C =\begin{pmatrix}- (B_y - A_y)\\   B_x - A_x \end{pmatrix}

Vector C needs to be normalized (divided by it’s length / magnitude).

\hat{C} =\dfrac{C}{\left \|C \right \|}

The resulting formula becomes:

P = (A + (B-A) * offset) + \hat {C} * distance

A positive distance will get the point above the vector, a negative below the vector.

Check whether two 2D lines are coincident.

To check whether two 2D lines are coincident, the distance of C and D to AB need to be calculated. For a coincident line both need to be (almost) zero.

The z component of the cross vector can be used for this quite elegantly.

Note: The cross vector is noted as an x e.g. AxB

To check whether the two lines AB and CD are coincident, the z-component of two cross vectors need to be calculated. Both calculate the distance of the points on line CD to AB.

AB x AC is the area of the parallelogram AB AC, dividing it with the length of AB (|AB|) gives the shortest distance of point C to line AB.

AB x AD gives the area of the parallelogram AB AD

After dividing with |AB| this is also the shortest distance of D to line AB. If both distances are small enough the lines are coincident.

dist_{abc} = \overline{AB}_x·\overline{AC}_y -   \overline{AB}_y·\overline{AC}_x
dist_{abd} = \overline{AB}_x·\overline{AD}_y -   \overline{AB}_y·\overline{AD}_x