Skip to content
Jean-Philippe Bruyère edited this page Nov 3, 2021 · 3 revisions

Berfor choosing the right base class for your new control, you have to answer two questions:

  • Will my control accept children?
  • do I need it to be templated?

The following graph will guide you to the right ancestor for your needs:

Widget


For the purpose of this example, let's create a simple gauge that will display an integer value as a vertical bar between 0 and a maximum value.

Creating the class

You are free to choose your namespace, crow will search the control among all exported types of the entry assembly, and the IML language don't check namespaces.

using System;
using Crow;

namespace Tests
{
	public class SimpleGauge : Widget
	{
		public SimpleGauge (){}
		public SimpleGauge (Interface iface): base (iface){}
	}
}

We implement here the constructor accepting the interface as argument to be able to create this control from the c# source. If no constructor is provided, the compiler will create the default parameterless one that will allow us to instantiate the control from IML. But if you choose to implement the interface constructor, you must implement also the parameterless one that will not be created automatically by the compiler in that case.

Adding properties

For simplicity, lets start with a simple gauge between fixed bounds (from 0 to 100), and let's create a Level property.

int level;
public int Level
{
	get { return level; }
	set {
		if (level == value)
			return;
		level = value;
		NotifyValueChanged ("Level", level);
	}
}

There are three steps in the setting of a Crow property:

  1. First, we have to test against the actual value (level==value), this is mandatory for the binding system to work property in the case of a two way binding.
  2. Next, we handle what to do with the value, here we store it in a local variable.
  3. Finaly, we have to notify Crow of the value change with NotifyValueChanged. It is a helper function defined in Widget to trigger the ValueChanged events. The property name passed in parameter is case sensitive.

Drawing

Now, let's draw our level on screen, for this, we override the onDraw method of Widget:

protected override void onDraw (Context gr)
{
	base.onDraw (gr);

This function has the current drawing backend context as argument, for now the only 2d vector library supported is Cairo. Add also the using directive on top: using Cairo;

To draw something, we first need to know the bounds of the available surface, the ClientRectangle property of the ILayoutable interface implemented in the base class has it in the local coordinate system of our widget, taking care of the margin defined.

Rectangle bounds = ClientRectangle;

We will draw the Level vertically from bottom to top, so we devide the available height by the maximum, and we multiply by our value, we also have to increment the rectangle y value by the void on top of the gauge.

int gaugeHeight = r.Height / 100 * level;
r.Y += r.Height - gaugeHeight;
r.Height = gaugeHeight;
Foreground.SetAsSource (gr);
gr.Rectangle (r);
gr.Fill ();

Foreground type is Fill, it is not used by default in the base class, so we may use it as the source color for our gauge. The Fill class offers a helper method to set it as the source of drawing operation. Once the source color set, we call the Rectangle command, and then tell the backend what to do with it by calling its Fill method.

That's it, you have a brand new graphical object ready for production, so let's instantiate one, you may reuse your first application and in the onLoad override, create a gauge:

protected override void OnLoad (EventArgs e)
{
	base.OnLoad (e);

	AddWidget (new SimpleGauge (CurrentInterface)
	{
		Level = 40,
		Width = 30, Height = "50%",
		Foreground = Color.UnitedNationsBlue,
		Background = Color.Jet
	});
}

You may also instantiate a new gauge with IML either by loading if from an xml file, or by passing directely an valid IML string to the LoadIMLFragment helper function from the CrowWindow:

LoadIMLFragment (@"<SimpleGauge Level='40' Margin='5' Background='Jet' Foreground='Gray' Width='30' Height='50%'/>");

A valid minimal IML file could be:

<SimpleGauge Level='40' Margin='5' Background='Jet' Foreground='Gray' Width='30' Height='50%'/>

Source

Clone this wiki locally