This guide walks you through adding custom logic to displays using CodeBehind, enabling interactive behaviors, data manipulation, and dynamic responses to user actions. CodeBehind allows you to write .NET code (C# or VB.NET) that executes alongside your display.
Prerequisites:
- Understanding of display creation
- Basic .NET programming knowledge
- Familiarity with tag operations
Understanding CodeBehind
What is CodeBehind?
CodeBehind is display-specific code that:
- Executes on the client side (where display runs)
- Responds to display lifecycle events
- Handles user interactions from display elements
- Accesses and manipulates display objects
- Manages local display variables (Custom Properties)
Language Support
- Portable Displays: C# or VB.NET (switchable)
- HTML5-only Displays: JavaScript also available
- Code Editor: Same interface as Scripts module
Pre-defined Methods
Display Lifecycle Methods
Every display CodeBehind includes these entry points:
csharp
public void DisplayOpening()
{
// Called when display is opening
// Initialize variables, setup handlers
}
public void DisplayIsOpen()
{
// Called periodically while display is open
// Update values, check conditions
}
public void DisplayClosing()
{
// Called when display is closing
// Cleanup, save state
}
Dialog-Specific Method
For dialog displays:
csharp
public int DialogOnOK()
{
// Called when OK button pressed
// Return 1 to allow close
// Return 0 to prevent close
if (ValidateInput())
return 1;
else
return 0;
}
Accessing Display Elements
Using GetControl Method
Access any display element by its Uid:
csharp
// Get reference to specific element
private Rectangle statusRect;
public void DisplayOpening()
{
// Access element by Uid
statusRect = this.CurrentDisplay.GetControl("22") as Rectangle;
// Modify properties
statusRect.Fill = Brushes.Green;
statusRect.Visibility = Visibility.Visible;
}
Finding Element Uid
- Select element in Draw environment
- Check Properties sidebar for Uid field
- Or set custom Uid for easier reference
Handling User Actions
Method 1: Action Dynamics
- Select element in display
- Add Action dynamic
- Set Event: MouseLeftButtonDown
- Set Action: RunScript
- Create method in CodeBehind:
csharp
public void Button1_Click(object sender, InputEventArgs e)
{
// Your code here
@Tag.Motor1.Start = 1;
@Info.Trace("Motor started");
}
Method 2: Using Sender Parameter
Handle multiple elements with one method:
csharp
public void AnyButton_Click(object sender, InputEventArgs e)
{
// Get the element that triggered event
var button = sender as Button;
// Use element properties
string buttonName = button.Name;
string uid = button.Uid;
// Take action based on which button
switch(uid)
{
case "StartBtn":
@Tag.Motor.Start = 1;
break;
case "StopBtn":
@Tag.Motor.Start = 0;
break;
}
}
Working with Symbols
Accessing Symbol Properties
csharp
private TSymbol mySymbol;
public void DisplayOpening()
{
// Get symbol by Uid
mySymbol = this.CurrentDisplay.GetControl("SymbolUID") as TSymbol;
// Access symbol properties
mySymbol.SetPropertyValue("State", 1);
// Note: #Source property not accessible via code
}
Dynamically Updating Symbols
csharp
public void UpdateSymbol(string uid, int state)
{
TSymbol symbol = this.CurrentDisplay.GetControl(uid) as TSymbol;
if (symbol != null)
{
symbol.SetPropertyValue("State", state);
symbol.SetPropertyValue("Value", @Tag.Temperature);
}
}
Moving Elements Programmatically
Using Margin Property
csharp
private Ellipse movingElement;
public void DisplayOpening()
{
movingElement = this.CurrentDisplay.GetControl("MovingDot") as Ellipse;
}
public void MoveElement(double x, double y)
{
// Move element to new position
movingElement.Margin = new Thickness(x, y, 0, 0);
}
// Called from button click
public void AnimateMovement(object sender, InputEventArgs e)
{
double newX = @Tag.XPosition;
double newY = @Tag.YPosition;
MoveElement(newX, newY);
}
Display Navigation
Opening Displays from Code
csharp
// Method 1: Using Display namespace (auto-updates if renamed)
@Display.AlarmScreen.Open();
// Method 2: Using Client namespace (manual string)
@Client.OpenDisplay("AlarmScreen");
// Method 3: Open as popup
@Display.MotorDetails.NewPopup();
// Method 4: With parameters
@Client.NewPopup("MotorDetails", "Motor1");
Closing Displays
csharp
// Close current display
this.CurrentDisplay.Close();
// Close with dialog result
this.CurrentDisplay.DialogResult = true;
this.CurrentDisplay.Close();
Custom Properties
Local Display Variables
Custom Properties are display-specific variables:
csharp
public void DisplayOpening()
{
// Set custom property
this.CurrentDisplay.SetCustomPropertyValue("LastUpdate", DateTime.Now);
this.CurrentDisplay.SetCustomPropertyValue("UserLevel", 2);
}
public void CheckProperty()
{
// Get custom property
object updateTime = this.CurrentDisplay.GetCustomPropertyValue("LastUpdate");
int level = Convert.ToInt32(
this.CurrentDisplay.GetCustomPropertyValue("UserLevel"));
}
// Get all as string (for debugging)
string allProps = this.CurrentDisplay.GetCustomPropertiesAsString();
Advanced Event Handling
Mouse Wheel Zoom
csharp
public void DisplayIsOpen()
{
// Attach to mouse wheel event (only once)
if (!wheelAttached)
{
this.CurrentDisplay.GetThis().PreviewMouseWheel += PreviewMouseWheelChanged;
wheelAttached = true;
}
}
private bool wheelAttached = false;
private void PreviewMouseWheelChanged(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
// Zoom based on wheel direction
if (e.Delta > 0)
@Display.MainPage.ZoomLevel += 0.1;
else if (e.Delta < 0)
@Display.MainPage.ZoomLevel -= 0.1;
}
Performance Considerations
Avoiding UI Blocking
Problem: CodeBehind runs on UI thread, long operations freeze display.
Solution 1: Use async methods
csharp
public async void LongOperation()
{
await Task.Run(() =>
{
// Heavy processing here
});
}
Solution 2: Move to Scripts
- Use Script Tasks for server-side processing
- Use Script Classes for shared logic
- Keep CodeBehind lightweight
Best Practices
? Keep methods short - Under 100ms execution ? Avoid loops in DisplayIsOpen() ? Cache element references in DisplayOpening() ? Use Scripts for heavy processing ? Minimize property access in loops
Common Patterns
Toggle Button State
csharp
private bool motorRunning = false;
public void ToggleMotor(object sender, InputEventArgs e)
{
motorRunning = !motorRunning;
@Tag.Motor.Running = motorRunning ? 1 : 0;
Button btn = sender as Button;
btn.Content = motorRunning ? "STOP" : "START";
}
Validate Input Before Closing
csharp
public int DialogOnOK()
{
TextBox input = this.CurrentDisplay.GetControl("InputBox") as TextBox;
if (string.IsNullOrEmpty(input.Text))
{
MessageBox.Show("Please enter a value");
return 0; // Prevent close
}
@Tag.UserInput = input.Text;
return 1; // Allow close
}
Dynamic Element Creation
csharp
public void DisplayOpening()
{
// Access the main canvas
Canvas canvas = this.CurrentDisplay.GetControl("MainCanvas") as Canvas;
// Create new rectangle
Rectangle rect = new Rectangle();
rect.Width = 100;
rect.Height = 50;
rect.Fill = Brushes.Blue;
// Position and add to canvas
Canvas.SetLeft(rect, 100);
Canvas.SetTop(rect, 100);
canvas.Children.Add(rect);
}
Troubleshooting
Display freezes during CodeBehind execution
- Use async/await for long operations
- Move heavy logic to Script Tasks
- Add @Info.Trace() to identify bottlenecks
Cannot find element with GetControl
- Verify Uid is correct
- Ensure element exists before accessing
- Check element type casting
Changes don't appear
- Ensure code runs (add trace statements)
- Check if running on correct thread
- Verify property changes are valid
Symbol properties not updating
- #Source property not accessible via code
- Use SetPropertyValue for other properties
- Verify property names match exactly
This guide covered adding custom logic to displays through CodeBehind, from basic event handling to advanced element manipulation, enabling rich interactive behaviors in your HMI application.