Bug 553181 - Allow customization of validation icon and color
calculation

Add the new SWTValidationUiService which encapsulates getting validation
icons and colors. This allows replacing the logic by registering a
custom service implementation with higher priority.
The default implementation delegates to the SWTValidationHelper.
Added three new protected final methods to the
AbstractControlSWTRenderer which use the new service. Deprecated the old
methods which request a severity as an argument.

Copied the SWTValidationHelper to bundle emfforms.swt.core and
deprecated the original version in emf.ecp.edit.swt.

Added unit tests for the new service and added unit tests for
AbstractControlSWTRenderer.

Change-Id: Ie284e9a34db6799a975850252ab5e879f8ea431b
Signed-off-by: Lucas Koehler <lkoehler@eclipsesource.com>
diff --git a/bundles/org.eclipse.emf.ecp.edit.swt/META-INF/MANIFEST.MF b/bundles/org.eclipse.emf.ecp.edit.swt/META-INF/MANIFEST.MF
index c77c64f..0b45685 100644
--- a/bundles/org.eclipse.emf.ecp.edit.swt/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.emf.ecp.edit.swt/META-INF/MANIFEST.MF
@@ -41,7 +41,8 @@
  org.eclipse.emf.databinding.edit;bundle-version="[1.3.0,2.0.0)",
  org.eclipse.emfforms.localization;bundle-version="[1.23.0,1.24.0)",
  org.eclipse.emfforms.core.services.editsupport;bundle-version="[1.23.0,1.24.0)",
- org.eclipse.emfforms.core.services;bundle-version="[1.23.0,1.24.0)"
+ org.eclipse.emfforms.core.services;bundle-version="[1.23.0,1.24.0)",
+ org.eclipse.emfforms.swt.core;bundle-version="[1.23.0,1.24.0)"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-ActivationPolicy: lazy
 Import-Package: org.eclipse.core.commands;version="0.0.0",
diff --git a/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/internal/swt/util/SWTControl.java b/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/internal/swt/util/SWTControl.java
index 65fb41f..9285755 100644
--- a/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/internal/swt/util/SWTControl.java
+++ b/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/internal/swt/util/SWTControl.java
@@ -24,12 +24,12 @@
 import org.eclipse.emf.ecore.EStructuralFeature.Setting;
 import org.eclipse.emf.ecp.edit.internal.swt.Activator;
 import org.eclipse.emf.ecp.edit.spi.ECPAbstractControl;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
 import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
 import org.eclipse.emf.ecp.view.spi.renderer.RenderingResultRow;
 import org.eclipse.emf.edit.command.SetCommand;
 import org.eclipse.emf.edit.domain.EditingDomain;
 import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.layout.GridLayoutFactory;
diff --git a/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/spi/swt/util/SWTValidationHelper.java b/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/spi/swt/util/SWTValidationHelper.java
index cbed7f9..424643c 100644
--- a/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/spi/swt/util/SWTValidationHelper.java
+++ b/bundles/org.eclipse.emf.ecp.edit.swt/src/org/eclipse/emf/ecp/edit/spi/swt/util/SWTValidationHelper.java
@@ -38,8 +38,10 @@
  *
  * @author jfaltermeier
  * @since 1.5
- *
+ * @deprecated use org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper or
+ *             org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService instead
  */
+@Deprecated
 public class SWTValidationHelper {
 
 	/**
diff --git a/bundles/org.eclipse.emf.ecp.view.categorization.swt/src/org/eclipse/emf/ecp/view/spi/categorization/swt/AbstractJFaceTreeRenderer.java b/bundles/org.eclipse.emf.ecp.view.categorization.swt/src/org/eclipse/emf/ecp/view/spi/categorization/swt/AbstractJFaceTreeRenderer.java
index deb8514..fda8663 100644
--- a/bundles/org.eclipse.emf.ecp.view.categorization.swt/src/org/eclipse/emf/ecp/view/spi/categorization/swt/AbstractJFaceTreeRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.categorization.swt/src/org/eclipse/emf/ecp/view/spi/categorization/swt/AbstractJFaceTreeRenderer.java
@@ -26,7 +26,6 @@
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecp.edit.internal.swt.util.OverlayImageDescriptor;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
 import org.eclipse.emf.ecp.view.internal.categorization.swt.Activator;
 import org.eclipse.emf.ecp.view.spi.categorization.model.VAbstractCategorization;
 import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorizableElement;
@@ -53,6 +52,7 @@
 import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.layout.GridLayoutFactory;
 import org.eclipse.jface.resource.ImageDescriptor;
diff --git a/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java
index ed19456..7b3830a 100644
--- a/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java
@@ -35,8 +35,8 @@
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecp.edit.internal.swt.controls.TableViewerColumnBuilder;
-import org.eclipse.emf.ecp.edit.spi.DeleteService;
 import org.eclipse.emf.ecp.edit.spi.ConditionalDeleteService;
+import org.eclipse.emf.ecp.edit.spi.DeleteService;
 import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
 import org.eclipse.emf.ecp.edit.spi.ReferenceService;
 import org.eclipse.emf.ecp.view.model.common.edit.provider.CustomReflectiveItemProviderAdapterFactory;
@@ -1234,7 +1234,7 @@
 				if (getVElement().getDiagnostic() == null) {
 					return;
 				}
-				validationIcon.setImage(getValidationIcon(getVElement().getDiagnostic().getHighestSeverity()));
+				validationIcon.setImage(getValidationIcon());
 				validationIcon.setToolTipText(ECPTooltipModifierHelper.modifyString(getVElement().getDiagnostic()
 					.getMessage(), null));
 			}
diff --git a/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer.java
index 7c2a91c..aaada25 100644
--- a/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer.java
@@ -25,13 +25,13 @@
 import org.eclipse.emf.databinding.EMFDataBindingContext;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
 import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
 import org.eclipse.emf.ecp.view.spi.model.LabelAlignment;
 import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
 import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
 import org.eclipse.emf.ecp.view.spi.model.VControl;
 import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
+import org.eclipse.emf.ecp.view.spi.model.VElement;
 import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
 import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
 import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
@@ -52,6 +52,8 @@
 import org.eclipse.emfforms.spi.swt.core.EMFFormsControlProcessorService;
 import org.eclipse.emfforms.spi.swt.core.SWTDataElementIdHelper;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
 import org.eclipse.jface.databinding.swt.WidgetProperties;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Color;
@@ -81,6 +83,7 @@
 	private final Map<Integer, Color> severityBackgroundColorMap = new LinkedHashMap<Integer, Color>();
 	private final Map<Integer, Color> severityForegroundColorMap = new LinkedHashMap<Integer, Color>();
 	private final Map<Integer, Image> severityIconMap = new LinkedHashMap<Integer, Image>();
+	private final SWTValidationUiService validationUiService;
 
 	/**
 	 * Default constructor.
@@ -96,17 +99,12 @@
 	public AbstractControlSWTRenderer(VCONTROL vElement, ViewModelContext viewContext, ReportService reportService,
 		EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
 		VTViewTemplateProvider vtViewTemplateProvider) {
-		super(vElement, viewContext, reportService);
-		this.emfFormsDatabinding = emfFormsDatabinding;
-		this.emfFormsLabelProvider = emfFormsLabelProvider;
-		this.vtViewTemplateProvider = vtViewTemplateProvider;
-		viewModelDBC = new EMFDataBindingContext();
-		viewContext.registerRootDomainModelChangeListener(this);
-		isDisposed = false;
+		this(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider,
+			viewContext.getService(SWTValidationUiService.class));
 	}
 
 	/**
-	 * Default constructor.
+	 * Additional constructor allowing to specify a custom {@link SWTValidationHelper}.
 	 *
 	 * @param vElement the view model element to be rendered
 	 * @param viewContext the view context
@@ -125,6 +123,31 @@
 	}
 
 	/**
+	 * Default constructor.
+	 *
+	 * @param vElement the view model element to be rendered
+	 * @param viewContext the view context
+	 * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
+	 * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
+	 * @param reportService The {@link ReportService}
+	 * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
+	 * @param validationUiService The {@link SWTValidationUiService}
+	 * @since 1.23
+	 */
+	public AbstractControlSWTRenderer(VCONTROL vElement, ViewModelContext viewContext, ReportService reportService,
+		EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
+		VTViewTemplateProvider vtViewTemplateProvider, SWTValidationUiService validationUiService) {
+		super(vElement, viewContext, reportService);
+		this.emfFormsDatabinding = emfFormsDatabinding;
+		this.emfFormsLabelProvider = emfFormsLabelProvider;
+		this.vtViewTemplateProvider = vtViewTemplateProvider;
+		this.validationUiService = validationUiService;
+		viewModelDBC = new EMFDataBindingContext();
+		viewContext.registerRootDomainModelChangeListener(this);
+		isDisposed = false;
+	}
+
+	/**
 	 * The {@link EMFFormsDatabinding} to use.
 	 *
 	 * @return The EMFFormsDatabinding
@@ -320,7 +343,10 @@
 	 *
 	 * @param severity the severity of the {@link org.eclipse.emf.common.util.Diagnostic}
 	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 * @deprecated use {@link #getValidationIcon()} for default behavior or use the
+	 *             {@link SWTValidationUiService} if you need to get the color for a specific diagnostic.
 	 */
+	@Deprecated
 	protected final Image getValidationIcon(int severity) {
 		if (!severityIconMap.containsKey(severity)) {
 			final Image validationIcon = swtValidationHelper.getValidationIcon(severity, getVElement(),
@@ -331,11 +357,24 @@
 	}
 
 	/**
+	 * Returns the validation icon for the current validation result of this control's {@link VElement}.
+	 *
+	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 * @since 1.23
+	 */
+	protected final Image getValidationIcon() {
+		return validationUiService.getValidationIcon(getVElement(), getViewModelContext());
+	}
+
+	/**
 	 * Returns the background color for a control with the given validation severity.
 	 *
 	 * @param severity severity the severity of the {@link org.eclipse.emf.common.util.Diagnostic}
 	 * @return the color to be used as a background color
+	 * @deprecated use {@link #getValidationBackgroundColor()} for default behavior or use the
+	 *             {@link SWTValidationUiService} if you need to get the color for a specific diagnostic.
 	 */
+	@Deprecated
 	protected final Color getValidationBackgroundColor(int severity) {
 		if (isDisposed) {
 			return null;
@@ -350,12 +389,28 @@
 	}
 
 	/**
+	 * Returns the background color for the current validation result of this control's {@link VElement}.
+	 *
+	 * @return the color to be used as a background color
+	 * @since 1.23
+	 */
+	protected final Color getValidationBackgroundColor() {
+		if (isDisposed) {
+			return null;
+		}
+		return validationUiService.getValidationBackgroundColor(getVElement(), getViewModelContext());
+	}
+
+	/**
 	 * Returns the foreground color for a control with the given validation severity.
 	 *
 	 * @param severity severity the severity of the {@link org.eclipse.emf.common.util.Diagnostic}
 	 * @return the color to be used as a foreground color
 	 * @since 1.10
+	 * @deprecated use {@link #getValidationForegroundColor()} for default behavior or use the
+	 *             {@link SWTValidationUiService} if you need to get the color for a specific diagnostic.
 	 */
+	@Deprecated
 	protected final Color getValidationForegroundColor(int severity) {
 		if (isDisposed) {
 			return null;
@@ -371,6 +426,20 @@
 	}
 
 	/**
+	 * Returns the foreground color for the current validation result of this control's {@link VElement}.
+	 *
+	 * @return the color to be used as a foreground color
+	 * @since 1.23
+	 */
+	protected final Color getValidationForegroundColor() {
+		if (isDisposed) {
+			return null;
+		}
+		return validationUiService.getValidationForegroundColor(getVElement(), getViewModelContext());
+
+	}
+
+	/**
 	 * Creates a new {@link DataBindingContext}.
 	 *
 	 * @return a new {@link DataBindingContext} each time this method is called
diff --git a/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/SimpleControlSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/SimpleControlSWTRenderer.java
index e3a56d6..daad69a 100644
--- a/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/SimpleControlSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.core.swt/src/org/eclipse/emf/ecp/view/spi/core/swt/SimpleControlSWTRenderer.java
@@ -19,7 +19,6 @@
 import org.eclipse.core.databinding.observable.IObserving;
 import org.eclipse.core.databinding.observable.value.IObservableValue;
 import org.eclipse.core.databinding.property.value.IValueProperty;
-import org.eclipse.emf.common.util.Diagnostic;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecp.view.internal.core.swt.Activator;
@@ -540,15 +539,10 @@
 		if (validationIcon.isDisposed()) {
 			return;
 		}
-		int highestSeverity = Diagnostic.OK;
-		// no diagnostic set
-		if (getVElement().getDiagnostic() != null) {
-			highestSeverity = getVElement().getDiagnostic().getHighestSeverity();
-		}
 
-		validationIcon.setImage(getValidationIcon(highestSeverity));
-		setValidationColor(editControl, getValidationBackgroundColor(highestSeverity));
-		setValidationForegroundColor(editControl, getValidationForegroundColor(highestSeverity));
+		validationIcon.setImage(getValidationIcon());
+		setValidationColor(editControl, getValidationBackgroundColor());
+		setValidationForegroundColor(editControl, getValidationForegroundColor());
 		if (getVElement().getDiagnostic() == null) {
 			validationIcon.setToolTipText(null);
 		} else {
diff --git a/bundles/org.eclipse.emf.ecp.view.custom.ui.swt.di/src/org/eclipse/emf/ecp/view/custom/ui/swt/di/renderer/DICustomControlSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.custom.ui.swt.di/src/org/eclipse/emf/ecp/view/custom/ui/swt/di/renderer/DICustomControlSWTRenderer.java
index 08de5f9..8a7b253 100644
--- a/bundles/org.eclipse.emf.ecp.view.custom.ui.swt.di/src/org/eclipse/emf/ecp/view/custom/ui/swt/di/renderer/DICustomControlSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.custom.ui.swt.di/src/org/eclipse/emf/ecp/view/custom/ui/swt/di/renderer/DICustomControlSWTRenderer.java
@@ -23,6 +23,7 @@
 import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
 import org.eclipse.emfforms.spi.common.report.ReportService;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.osgi.framework.Bundle;
@@ -36,6 +37,8 @@
 public class DICustomControlSWTRenderer extends CustomControlSWTRenderer {
 
 	/**
+	 * Legacy constructor.
+	 *
 	 * @param vElement the view model element to be rendered
 	 * @param viewContext the view context
 	 * @param factory the {@link ReportService}
@@ -44,6 +47,19 @@
 		super(vElement, viewContext, factory);
 	}
 
+	/**
+	 * Default constructor.
+	 *
+	 * @param vElement the view model element to be rendered
+	 * @param viewContext the view context
+	 * @param factory the {@link ReportService}
+	 * @param validationUiService the {@link SWTValidationUiService} to use
+	 */
+	public DICustomControlSWTRenderer(VCustomControl vElement, ViewModelContext viewContext, ReportService factory,
+		SWTValidationUiService validationUiService) {
+		super(vElement, viewContext, factory, validationUiService);
+	}
+
 	private IEclipseContext eclipseContext;
 
 	/**
diff --git a/bundles/org.eclipse.emf.ecp.view.custom.ui.swt/src/org/eclipse/emf/ecp/view/spi/custom/swt/CustomControlSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.custom.ui.swt/src/org/eclipse/emf/ecp/view/spi/custom/swt/CustomControlSWTRenderer.java
index fac6b21..69b6625 100644
--- a/bundles/org.eclipse.emf.ecp.view.custom.ui.swt/src/org/eclipse/emf/ecp/view/spi/custom/swt/CustomControlSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.custom.ui.swt/src/org/eclipse/emf/ecp/view/spi/custom/swt/CustomControlSWTRenderer.java
@@ -15,7 +15,6 @@
 package org.eclipse.emf.ecp.view.spi.custom.swt;
 
 import org.eclipse.core.runtime.Platform;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
 import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
 import org.eclipse.emf.ecp.view.spi.custom.model.VCustomControl;
 import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
@@ -27,6 +26,7 @@
 import org.eclipse.emfforms.spi.swt.core.EMFFormsControlProcessorService;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
@@ -43,8 +43,10 @@
  */
 public class CustomControlSWTRenderer extends AbstractSWTRenderer<VCustomControl> {
 
+	private final SWTValidationUiService validationUiService;
+
 	/**
-	 * Default Constructor.
+	 * Legacy Constructor.
 	 *
 	 * @param vElement the view element to be rendered
 	 * @param viewContext The view model context
@@ -53,7 +55,22 @@
 	 */
 	public CustomControlSWTRenderer(final VCustomControl vElement, final ViewModelContext viewContext,
 		ReportService reportService) {
+		this(vElement, viewContext, reportService, viewContext.getService(SWTValidationUiService.class));
+	}
+
+	/**
+	 * Default Constructor.
+	 *
+	 * @param vElement the view element to be rendered
+	 * @param viewContext The view model context
+	 * @param reportService the ReportService to use
+	 * @param validationUiService the {@link SWTValidationUiService} to use
+	 * @since 1.23
+	 */
+	public CustomControlSWTRenderer(final VCustomControl vElement, final ViewModelContext viewContext,
+		ReportService reportService, SWTValidationUiService validationUiService) {
 		super(vElement, viewContext, reportService);
+		this.validationUiService = validationUiService;
 	}
 
 	private ECPAbstractCustomControlSWT swtCustomControl;
@@ -261,7 +278,7 @@
 				final VDiagnostic diag = getVElement().getDiagnostic();
 
 				if (diag != null && validationIcon != null && !validationIcon.isDisposed()) {
-					validationIcon.setImage(getValidationIcon(diag.getHighestSeverity()));
+					validationIcon.setImage(getValidationIcon());
 					validationIcon.setToolTipText(diag.getMessage());
 				}
 				if (swtCustomControl != null) {
@@ -271,7 +288,7 @@
 		});
 	}
 
-	private Image getValidationIcon(int severity) {
-		return SWTValidationHelper.INSTANCE.getValidationIcon(severity, getVElement(), getViewModelContext());
+	private Image getValidationIcon() {
+		return validationUiService.getValidationIcon(getVElement(), getViewModelContext());
 	}
 }
diff --git a/bundles/org.eclipse.emf.ecp.view.table.ui.swt/src/org/eclipse/emf/ecp/view/spi/table/swt/TableControlSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.table.ui.swt/src/org/eclipse/emf/ecp/view/spi/table/swt/TableControlSWTRenderer.java
index 1e5358f..6e92318 100644
--- a/bundles/org.eclipse.emf.ecp.view.table.ui.swt/src/org/eclipse/emf/ecp/view/spi/table/swt/TableControlSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.table.ui.swt/src/org/eclipse/emf/ecp/view/spi/table/swt/TableControlSWTRenderer.java
@@ -62,8 +62,8 @@
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.EStructuralFeature.Setting;
 import org.eclipse.emf.ecore.InternalEObject;
-import org.eclipse.emf.ecp.edit.spi.DeleteService;
 import org.eclipse.emf.ecp.edit.spi.ConditionalDeleteService;
+import org.eclipse.emf.ecp.edit.spi.DeleteService;
 import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
 import org.eclipse.emf.ecp.edit.spi.ReferenceService;
 import org.eclipse.emf.ecp.edit.spi.swt.table.ECPCellEditor;
@@ -146,6 +146,7 @@
 import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
 import org.eclipse.emfforms.spi.swt.table.AbstractTableViewerComposite;
 import org.eclipse.emfforms.spi.swt.table.CellLabelProviderFactory;
 import org.eclipse.emfforms.spi.swt.table.ColumnConfiguration;
@@ -2354,7 +2355,7 @@
 				return;
 			}
 
-			validationIcon.setImage(getValidationIcon(getVElement().getDiagnostic().getHighestSeverity()));
+			validationIcon.setImage(getValidationIcon());
 			showValidationSummaryTooltip(setting.get(), showValidationSummaryTooltip);
 
 			if (updates != null) {
@@ -2796,31 +2797,24 @@
 			return attributeMaps[0].get(object);
 		}
 
-		/**
-		 * {@inheritDoc}
-		 *
-		 * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
-		 */
 		@Override
 		public Color getForeground(Object element) {
 			return table.getForeground();
 		}
 
-		/**
-		 * {@inheritDoc}
-		 *
-		 * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
-		 */
 		@Override
 		public Color getBackground(Object element) {
 			final VDiagnostic vDiagnostic = vTableControl.getDiagnostic();
+			final SWTValidationUiService validationUiService = getViewModelContext()
+				.getService(SWTValidationUiService.class);
 			if (vDiagnostic == null) {
-				return getValidationBackgroundColor(Diagnostic.OK);
+				return validationUiService.getValidationBackgroundColor(Diagnostic.OK_INSTANCE, vTableControl,
+					getViewModelContext());
 			}
 			final List<Diagnostic> diagnostic = vDiagnostic.getDiagnostic((EObject) element, feature);
-			return getValidationBackgroundColor(diagnostic.size() == 0 ? Diagnostic.OK
-				: diagnostic.get(0)
-					.getSeverity());
+			final Diagnostic iconDiagnostic = diagnostic.size() == 0 ? Diagnostic.OK_INSTANCE : diagnostic.get(0);
+			return validationUiService.getValidationBackgroundColor(iconDiagnostic, vTableControl,
+				getViewModelContext());
 		}
 
 		/**
@@ -3145,16 +3139,21 @@
 
 		@Override
 		public void update(ViewerCell cell) {
-			Integer mostSevere = Diagnostic.OK;
 			final VDiagnostic vDiagnostic = vTableControl.getDiagnostic();
 			if (vDiagnostic == null) {
 				return;
 			}
 			final List<Diagnostic> diagnostics = vDiagnostic.getDiagnostics((EObject) cell.getElement());
+			Diagnostic cellDiagnostic;
 			if (diagnostics.size() != 0) {
-				mostSevere = diagnostics.get(0).getSeverity();
+				cellDiagnostic = diagnostics.get(0);
+			} else {
+				// If there is no diagnostic, we assume everything is ok
+				cellDiagnostic = Diagnostic.OK_INSTANCE;
 			}
-			cell.setImage(getValidationIcon(mostSevere));
+			final Image validationIcon = getViewModelContext().getService(SWTValidationUiService.class)
+				.getValidationIcon(cellDiagnostic, getVElement(), getViewModelContext());
+			cell.setImage(validationIcon);
 		}
 
 		@Override
diff --git a/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java
index 2dcfaa2..3b3bb3e 100644
--- a/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java
@@ -49,11 +49,10 @@
 import org.eclipse.emf.ecore.util.EcoreUtil;
 import org.eclipse.emf.ecp.common.spi.ChildrenDescriptorCollector;
 import org.eclipse.emf.ecp.edit.internal.swt.util.OverlayImageDescriptor;
-import org.eclipse.emf.ecp.edit.spi.DeleteService;
 import org.eclipse.emf.ecp.edit.spi.ConditionalDeleteService;
+import org.eclipse.emf.ecp.edit.spi.DeleteService;
 import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
 import org.eclipse.emf.ecp.edit.spi.ReferenceService;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
 import org.eclipse.emf.ecp.ui.view.swt.ECPSWTViewRenderer;
 import org.eclipse.emf.ecp.view.internal.swt.ContextMenuViewModelService;
 import org.eclipse.emf.ecp.view.internal.treemasterdetail.ui.swt.Activator;
@@ -93,6 +92,7 @@
 import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
diff --git a/bundles/org.eclipse.emfforms.editor.ecore/src/org/eclipse/emfforms/internal/editor/ecore/controls/TypedElementBoundsRenderer.java b/bundles/org.eclipse.emfforms.editor.ecore/src/org/eclipse/emfforms/internal/editor/ecore/controls/TypedElementBoundsRenderer.java
index f3b2a53..047a50c 100644
--- a/bundles/org.eclipse.emfforms.editor.ecore/src/org/eclipse/emfforms/internal/editor/ecore/controls/TypedElementBoundsRenderer.java
+++ b/bundles/org.eclipse.emfforms.editor.ecore/src/org/eclipse/emfforms/internal/editor/ecore/controls/TypedElementBoundsRenderer.java
@@ -365,11 +365,10 @@
 			return;
 		}
 
-		validationIcon.setImage(getValidationIcon(getVElement().getDiagnostic().getHighestSeverity()));
+		validationIcon.setImage(getValidationIcon());
 		validationIcon.setToolTipText(getVElement().getDiagnostic().getMessage());
 
-		setValidationColor(editControl, getValidationBackgroundColor(getVElement().getDiagnostic()
-			.getHighestSeverity()));
+		setValidationColor(editControl, getValidationBackgroundColor());
 	}
 
 	/**
diff --git a/bundles/org.eclipse.emfforms.swt.categorization.expandbar/src/org/eclipse/emfforms/internal/swt/categorization/expandbar/ExpandBarCategorizationElementRenderer.java b/bundles/org.eclipse.emfforms.swt.categorization.expandbar/src/org/eclipse/emfforms/internal/swt/categorization/expandbar/ExpandBarCategorizationElementRenderer.java
index 934b9df..5645081 100644
--- a/bundles/org.eclipse.emfforms.swt.categorization.expandbar/src/org/eclipse/emfforms/internal/swt/categorization/expandbar/ExpandBarCategorizationElementRenderer.java
+++ b/bundles/org.eclipse.emfforms.swt.categorization.expandbar/src/org/eclipse/emfforms/internal/swt/categorization/expandbar/ExpandBarCategorizationElementRenderer.java
@@ -26,7 +26,7 @@
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.databinding.EMFDataBindingContext;
 import org.eclipse.emf.databinding.edit.EMFEditObservables;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
 import org.eclipse.emf.ecp.view.spi.categorization.model.VAbstractCategorization;
 import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorizableElement;
 import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorization;
diff --git a/bundles/org.eclipse.emfforms.swt.control.multiattribute/src/org/eclipse/emfforms/spi/view/control/multiattribute/MultiAttributeSWTRenderer.java b/bundles/org.eclipse.emfforms.swt.control.multiattribute/src/org/eclipse/emfforms/spi/view/control/multiattribute/MultiAttributeSWTRenderer.java
index 28d2ee7..4296c3a 100644
--- a/bundles/org.eclipse.emfforms.swt.control.multiattribute/src/org/eclipse/emfforms/spi/view/control/multiattribute/MultiAttributeSWTRenderer.java
+++ b/bundles/org.eclipse.emfforms.swt.control.multiattribute/src/org/eclipse/emfforms/spi/view/control/multiattribute/MultiAttributeSWTRenderer.java
@@ -717,7 +717,7 @@
 				if (getVElement().getDiagnostic() == null) {
 					return;
 				}
-				validationIcon.setImage(getValidationIcon(getVElement().getDiagnostic().getHighestSeverity()));
+				validationIcon.setImage(getValidationIcon());
 				validationIcon.setToolTipText(ECPTooltipModifierHelper.modifyString(getVElement().getDiagnostic()
 					.getMessage(), null));
 			}
diff --git a/bundles/org.eclipse.emfforms.swt.core/.settings/org.eclipse.pde.ds.annotations.prefs b/bundles/org.eclipse.emfforms.swt.core/.settings/org.eclipse.pde.ds.annotations.prefs
new file mode 100644
index 0000000..73a356b
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/.settings/org.eclipse.pde.ds.annotations.prefs
@@ -0,0 +1,8 @@
+classpath=true
+dsVersion=V1_3
+eclipse.preferences.version=1
+enabled=true
+generateBundleActivationPolicyLazy=true
+path=OSGI-INF
+validationErrorLevel=error
+validationErrorLevel.missingImplicitUnbindMethod=error
diff --git a/bundles/org.eclipse.emfforms.swt.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.emfforms.swt.core/META-INF/MANIFEST.MF
index d327eb8..c03698f 100644
--- a/bundles/org.eclipse.emfforms.swt.core/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.emfforms.swt.core/META-INF/MANIFEST.MF
@@ -5,6 +5,7 @@
 Bundle-Version: 1.23.0.qualifier
 Bundle-Vendor: Eclipse Modeling Project
 Export-Package: org.eclipse.emfforms.internal.swt.core;version="1.23.0";x-internal:=true,
+ org.eclipse.emfforms.internal.swt.core.ui;version="1.23.0";x-internal:=true,
  org.eclipse.emfforms.spi.swt.core;version="1.23.0",
  org.eclipse.emfforms.spi.swt.core.data;version="1.23.0",
  org.eclipse.emfforms.spi.swt.core.layout;version="1.23.0",
@@ -16,15 +17,20 @@
  org.eclipse.emf.ecp.view.model.common;bundle-version="[1.23.0,1.24.0)",
  org.eclipse.emf.ecp.ui.view;bundle-version="[1.23.0,1.24.0)",
  org.eclipse.core.runtime;bundle-version="[3.7.0,4.0.0)",
- org.eclipse.emf.ecp.view.util.swt;bundle-version="[1.23.0,1.24.0)"
+ org.eclipse.emf.ecp.view.util.swt;bundle-version="[1.23.0,1.24.0)",
+ org.eclipse.emf.ecp.view.template.model;bundle-version="[1.23.0,1.24.0)"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Service-Component: OSGI-INF/rendererFactory.xml,OSGI-INF/containerRevealProvider.xml,OSGI-INF/controlRevealProvider.xml
+Service-Component: OSGI-INF/rendererFactory.xml,
+ OSGI-INF/containerRevealProvider.xml,
+ OSGI-INF/controlRevealProvider.xml,
+ OSGI-INF/org.eclipse.emfforms.internal.swt.core.ui.SWTValidationUiServiceImpl.xml
 Bundle-ActivationPolicy: lazy
 Import-Package: org.eclipse.emf.ecp.view.model.common;version="[1.23.0,1.24.0)",
  org.eclipse.emf.ecp.view.model.common.di.annotations;version="[1.23.0,1.24.0)",
  org.eclipse.emfforms.bazaar;version="[1.23.0,1.24.0)",
  org.eclipse.emfforms.spi.common.report;version="[1.23.0,1.24.0)",
  org.eclipse.jface.layout;version="0.0.0",
+ org.eclipse.jface.resource;version="0.0.0",
  org.eclipse.jface.viewers;version="0.0.0",
  org.eclipse.swt;version="0.0.0",
  org.eclipse.swt.custom;version="0.0.0",
@@ -34,3 +40,4 @@
  org.eclipse.swt.widgets;version="0.0.0",
  org.osgi.service.component.annotations;version="[1.2.0,2.0.0)";resolution:=optional
 Automatic-Module-Name: org.eclipse.emfforms.swt.core
+Bundle-Activator: org.eclipse.emfforms.internal.swt.core.Activator
diff --git a/bundles/org.eclipse.emfforms.swt.core/OSGI-INF/org.eclipse.emfforms.internal.swt.core.ui.SWTValidationUiServiceImpl.xml b/bundles/org.eclipse.emfforms.swt.core/OSGI-INF/org.eclipse.emfforms.internal.swt.core.ui.SWTValidationUiServiceImpl.xml
new file mode 100644
index 0000000..cddf3e5
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/OSGI-INF/org.eclipse.emfforms.internal.swt.core.ui.SWTValidationUiServiceImpl.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.emfforms.internal.swt.core.ui.SWTValidationUiServiceImpl">
+   <service>
+      <provide interface="org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService"/>
+   </service>
+   <implementation class="org.eclipse.emfforms.internal.swt.core.ui.SWTValidationUiServiceImpl"/>
+</scr:component>
\ No newline at end of file
diff --git a/bundles/org.eclipse.emfforms.swt.core/build.properties b/bundles/org.eclipse.emfforms.swt.core/build.properties
index 6907575..e93b71f 100644
--- a/bundles/org.eclipse.emfforms.swt.core/build.properties
+++ b/bundles/org.eclipse.emfforms.swt.core/build.properties
@@ -2,6 +2,7 @@
 bin.includes = META-INF/,\
                .,\
                OSGI-INF/,\
-               about.html
+               about.html,\
+               icons/
 source.. = src/
 src.includes = about.html
diff --git a/bundles/org.eclipse.emfforms.swt.core/icons/error_decorate.png b/bundles/org.eclipse.emfforms.swt.core/icons/error_decorate.png
new file mode 100644
index 0000000..28847c4
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/icons/error_decorate.png
Binary files differ
diff --git a/bundles/org.eclipse.emfforms.swt.core/icons/info_decorate.gif b/bundles/org.eclipse.emfforms.swt.core/icons/info_decorate.gif
new file mode 100644
index 0000000..d485366
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/icons/info_decorate.gif
Binary files differ
diff --git a/bundles/org.eclipse.emfforms.swt.core/icons/validation_error.png b/bundles/org.eclipse.emfforms.swt.core/icons/validation_error.png
new file mode 100644
index 0000000..fd79d15
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/icons/validation_error.png
Binary files differ
diff --git a/bundles/org.eclipse.emfforms.swt.core/icons/validation_warning.png b/bundles/org.eclipse.emfforms.swt.core/icons/validation_warning.png
new file mode 100644
index 0000000..628cf2d
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/icons/validation_warning.png
Binary files differ
diff --git a/bundles/org.eclipse.emfforms.swt.core/icons/warning_decorate.png b/bundles/org.eclipse.emfforms.swt.core/icons/warning_decorate.png
new file mode 100644
index 0000000..14caf6c
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/icons/warning_decorate.png
Binary files differ
diff --git a/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/Activator.java b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/Activator.java
new file mode 100644
index 0000000..a24553d
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/Activator.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Lucas Koehler - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.emfforms.internal.swt.core;
+
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ */
+public class Activator extends Plugin {
+	private static final String NULL = "NULL"; //$NON-NLS-1$
+
+	/** The plug-in ID. **/
+	public static final String PLUGIN_ID = "org.eclipse.emfforms.swt.core"; //$NON-NLS-1$
+
+	/** The shared instance. **/
+	private static Activator plugin;
+
+	/**
+	 * The constructor.
+	 */
+	public Activator() {
+	}
+
+	@Override
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+		plugin = this;
+	}
+
+	@Override
+	public void stop(BundleContext context) throws Exception {
+		imageRegistry.values().forEach(Image::dispose);
+		if (viewTemplateReference != null) {
+			context.ungetService(viewTemplateReference);
+		}
+		super.stop(context);
+		plugin = null;
+	}
+
+	/**
+	 * Returns the shared instance.
+	 *
+	 * @return the shared instance
+	 */
+	public static Activator getDefault() {
+		return plugin;
+	}
+
+	private final Map<String, Image> imageRegistry = new LinkedHashMap<String, Image>(20, .8F, true) {
+		private static final long serialVersionUID = 1L;
+
+		// This method is called just after a new entry has been added
+		@Override
+		public boolean removeEldestEntry(Map.Entry<String, Image> eldest) {
+			return size() > 20;
+		}
+
+		@Override
+		public Image remove(Object arg0) {
+			final Image image = super.remove(arg0);
+			image.dispose();
+			return image;
+		}
+
+	};
+
+	/**
+	 * Loads an image based on the provided {@link URL} form this bundle. The url may be null, then an empty image is
+	 * returned.
+	 *
+	 * @param url the {@link URL} to load the {@link Image} from
+	 * @return the {@link Image}
+	 */
+	public static Image getImage(URL url) {
+		if (!getDefault().imageRegistry.containsKey(url == null ? NULL : url.toExternalForm())) {
+			final ImageDescriptor createFromURL = ImageDescriptor.createFromURL(url);
+			getDefault().imageRegistry.put(url == null ? NULL : url.toExternalForm(), createFromURL.createImage());
+		}
+		return getDefault().imageRegistry.get(url == null ? NULL : url.toExternalForm());
+
+	}
+
+	private ServiceReference<VTViewTemplateProvider> viewTemplateReference;
+
+	/**
+	 * Returns the currentInstance of the {@link VTViewTemplateProvider}.
+	 *
+	 * @return the {@link VTViewTemplateProvider}
+	 */
+	public VTViewTemplateProvider getVTViewTemplateProvider() {
+		if (viewTemplateReference == null) {
+			viewTemplateReference = plugin.getBundle().getBundleContext()
+				.getServiceReference(VTViewTemplateProvider.class);
+		}
+		if (viewTemplateReference != null) {
+			return plugin.getBundle().getBundleContext().getService(viewTemplateReference);
+		}
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ui/SWTValidationUiServiceImpl.java b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ui/SWTValidationUiServiceImpl.java
new file mode 100644
index 0000000..6e2b07d
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ui/SWTValidationUiServiceImpl.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Lucas Koehler - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emfforms.internal.swt.core.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
+import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
+import org.eclipse.emf.ecp.view.spi.model.VElement;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Default implementation of the {@link SWTValidationUiService} which delegates to the {@link SWTValidationHelper} to
+ * get the validation icons and colors.
+ *
+ * @author Lucas Koehler
+ *
+ */
+@Component
+public class SWTValidationUiServiceImpl implements SWTValidationUiService {
+
+	private final Map<Integer, Color> severityBackgroundColorMap = new HashMap<Integer, Color>();
+	private final Map<Integer, Color> severityForegroundColorMap = new HashMap<Integer, Color>();
+	private final Map<Integer, Image> severityIconMap = new HashMap<Integer, Image>();
+	private SWTValidationHelper validationHelper = SWTValidationHelper.INSTANCE;
+
+	/** Default constructor. */
+	public SWTValidationUiServiceImpl() {
+		// Nothing to do here
+	}
+
+	/**
+	 * Test constructor that allows specifying a custom {@link SWTValidationHelper}.
+	 *
+	 * @param validationHelper The custom {@link SWTValidationHelper}
+	 */
+	SWTValidationUiServiceImpl(SWTValidationHelper validationHelper) {
+		this.validationHelper = validationHelper;
+	}
+
+	@Override
+	public Image getValidationIcon(Diagnostic diagnostic, VElement vElement, ViewModelContext viewModelContext) {
+		final int severity = severity(diagnostic);
+		if (!severityIconMap.containsKey(severity)) {
+			final Image validationIcon = validationHelper.getValidationIcon(severity, vElement, viewModelContext);
+			severityIconMap.put(severity, validationIcon);
+		}
+		return severityIconMap.get(severity);
+	}
+
+	@Override
+	public Image getValidationIcon(VElement vElement, ViewModelContext viewModelContext) {
+		return getValidationIcon(highestSeverityDiagnostic(vElement), vElement, viewModelContext);
+	}
+
+	@Override
+	public Color getValidationForegroundColor(Diagnostic diagnostic, VElement vElement,
+		ViewModelContext viewModelContext) {
+		final int severity = severity(diagnostic);
+		if (!severityForegroundColorMap.containsKey(severity)) {
+			final Color validationForegroundColor = validationHelper.getValidationForegroundColor(severity, vElement,
+				viewModelContext);
+			severityForegroundColorMap.put(severity, validationForegroundColor);
+		}
+		return severityForegroundColorMap.get(severity);
+	}
+
+	@Override
+	public Color getValidationForegroundColor(VElement vElement, ViewModelContext viewModelContext) {
+		return getValidationForegroundColor(highestSeverityDiagnostic(vElement), vElement, viewModelContext);
+	}
+
+	@Override
+	public Color getValidationBackgroundColor(Diagnostic diagnostic, VElement vElement,
+		ViewModelContext viewModelContext) {
+		final int severity = severity(diagnostic);
+		if (!severityBackgroundColorMap.containsKey(severity)) {
+			final Color validationBackgroundColor = validationHelper.getValidationBackgroundColor(severity, vElement,
+				viewModelContext);
+			severityBackgroundColorMap.put(severity, validationBackgroundColor);
+		}
+		return severityBackgroundColorMap.get(severity);
+	}
+
+	@Override
+	public Color getValidationBackgroundColor(VElement vElement, ViewModelContext viewModelContext) {
+		return getValidationBackgroundColor(highestSeverityDiagnostic(vElement), vElement, viewModelContext);
+	}
+
+	private static Diagnostic highestSeverityDiagnostic(VElement element) {
+		Diagnostic mostSevere = Diagnostic.OK_INSTANCE;
+		final VDiagnostic vDiagnostic = element.getDiagnostic();
+		if (vDiagnostic != null && vDiagnostic.getDiagnostics().size() > 0) {
+			for (final Object o : vDiagnostic.getDiagnostics()) {
+				final Diagnostic diagnostic = (Diagnostic) o;
+				mostSevere = mostSevere.getSeverity() >= diagnostic.getSeverity() ? mostSevere : diagnostic;
+			}
+		}
+		return mostSevere;
+	}
+
+	/** Wrap getting a Diagnostic's severity to make the call null-safe. */
+	private static int severity(Diagnostic diagnostic) {
+		// If there is no diagnostic, we assume everything is ok.
+		return diagnostic != null ? diagnostic.getSeverity() : Diagnostic.OK;
+	}
+}
diff --git a/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/spi/swt/core/ui/SWTValidationHelper.java b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/spi/swt/core/ui/SWTValidationHelper.java
new file mode 100644
index 0000000..a49f7e7
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/spi/swt/core/ui/SWTValidationHelper.java
@@ -0,0 +1,639 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Faltermeier - initial API and implementation
+ * Eugen Neufeld - VTViewTemplate implementation
+ ******************************************************************************/
+package org.eclipse.emfforms.spi.swt.core.ui;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
+import org.eclipse.emf.ecp.view.spi.model.VElement;
+import org.eclipse.emf.ecp.view.template.model.VTStyleProperty;
+import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
+import org.eclipse.emf.ecp.view.template.style.validation.model.VTValidationFactory;
+import org.eclipse.emf.ecp.view.template.style.validation.model.VTValidationStyleProperty;
+import org.eclipse.emfforms.internal.swt.core.Activator;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Helper class for accessing icons and colours for validations.
+ *
+ * @author jfaltermeier
+ * @since 1.23
+ *
+ */
+public class SWTValidationHelper {
+
+	/**
+	 * The instance of the SWTValidationHelper.
+	 */
+	public static final SWTValidationHelper INSTANCE = new SWTValidationHelper();
+	private final Map<String, Color> colorMap = new LinkedHashMap<String, Color>();
+	private VTValidationStyleProperty defaultValidationStyle;
+
+	/**
+	 * Returns the background color for a control with the given validation severity, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param severity severity the severity of the {@link Diagnostic}
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the color to be used as a background color
+	 */
+	public Color getValidationBackgroundColor(int severity, VElement vElement, ViewModelContext viewModelContext) {
+		final VTValidationStyleProperty defaultStyle = getDefaultValidationStyle();
+		String colorHex = null;
+
+		switch (severity) {
+		case Diagnostic.OK:
+			colorHex = getOkColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.INFO:
+			colorHex = getInfoColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.WARNING:
+			colorHex = getWarningColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.ERROR:
+			colorHex = getErrorColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.CANCEL:
+			colorHex = getCancelColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		default:
+			throw new IllegalArgumentException(
+				"The specified severity value " + severity + " is invalid. See Diagnostic class."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		if (colorHex == null) {
+			return null;
+		}
+		if (!colorMap.containsKey(colorHex)) {
+			colorMap.put(colorHex, getColor(colorHex));
+		}
+		return colorMap.get(colorHex);
+	}
+
+	/**
+	 * Returns the foreground color for a control with the given validation severity, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param severity severity the severity of the {@link Diagnostic}
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the color to be used as a foreground color
+	 * @since 1.10
+	 */
+	public Color getValidationForegroundColor(int severity, VElement vElement, ViewModelContext viewModelContext) {
+		final VTValidationStyleProperty defaultStyle = getDefaultValidationStyle();
+		String colorHex = null;
+
+		switch (severity) {
+		case Diagnostic.OK:
+			colorHex = getOkForegroundColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.INFO:
+			colorHex = getInfoForegroundColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.WARNING:
+			colorHex = getWarningForegroundColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.ERROR:
+			colorHex = getErrorForegroundColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.CANCEL:
+			colorHex = getCancelForegroundColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		default:
+			throw new IllegalArgumentException(
+				"The specified severity value " + severity + " is invalid. See Diagnostic class."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		if (colorHex == null) {
+			return null;
+		}
+		if (!colorMap.containsKey(colorHex)) {
+			colorMap.put(colorHex, getColor(colorHex));
+		}
+		return colorMap.get(colorHex);
+	}
+
+	/**
+	 * Returns the background color for a control with the given validation severity.
+	 *
+	 * @param severity severity the severity of the {@link Diagnostic}
+	 * @return the color to be used as a background color
+	 */
+	public Color getValidationBackgroundColor(int severity) {
+		return getValidationBackgroundColor(severity, null, null);
+	}
+
+	/**
+	 * Returns the foreground color for a control with the given validation severity.
+	 *
+	 * @param severity severity the severity of the {@link Diagnostic}
+	 * @return the color to be used as a foreground color
+	 * @since 1.10
+	 */
+	public Color getValidationForegroundColor(int severity) {
+		return getValidationForegroundColor(severity, null, null);
+	}
+
+	/**
+	 * Returns the hex color for a control with the given validation severity, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param severity severity the severity of the {@link Diagnostic}
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the hex value to be used
+	 * @since 1.9
+	 */
+	public String getValidationColorHEX(int severity, VElement vElement, ViewModelContext viewModelContext) {
+		final VTValidationStyleProperty defaultStyle = getDefaultValidationStyle();
+		String colorHex = null;
+
+		switch (severity) {
+		case Diagnostic.OK:
+			colorHex = getOkColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.INFO:
+			colorHex = getInfoColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.WARNING:
+			colorHex = getWarningColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.ERROR:
+			colorHex = getErrorColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.CANCEL:
+			colorHex = getCancelColorHEX(defaultStyle, vElement, viewModelContext);
+			break;
+		default:
+			throw new IllegalArgumentException(
+				"The specified severity value " + severity + " is invalid. See Diagnostic class."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		return colorHex;
+	}
+
+	/**
+	 * Returns the validation icon matching the given severity, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param severity the severity of the {@link Diagnostic}
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 */
+	public Image getValidationIcon(int severity, VElement vElement, ViewModelContext viewModelContext) {
+		final VTValidationStyleProperty defaultStyle = getDefaultValidationStyle();
+		String imageUrl = null;
+
+		switch (severity) {
+		case Diagnostic.OK:
+			imageUrl = getOkImageURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.INFO:
+			imageUrl = getInfoImageURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.WARNING:
+			imageUrl = getWarningImageURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.ERROR:
+			imageUrl = getErrorImageURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.CANCEL:
+			imageUrl = getCancelImageURL(defaultStyle, vElement, viewModelContext);
+			break;
+		default:
+			throw new IllegalArgumentException(
+				"The specified severity value " + severity + " is invalid. See Diagnostic class."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		if (imageUrl == null) {
+			return null;
+		}
+		try {
+			return Activator.getImage(new URL(imageUrl));
+		} catch (final MalformedURLException ex) {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns the validation icon matching the given severity.
+	 *
+	 * @param severity the severity of the {@link Diagnostic}
+	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 */
+	public Image getValidationIcon(int severity) {
+		return getValidationIcon(severity, null, null);
+	}
+
+	/**
+	 * Returns the validation overlay icon matching the given severity, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param severity the severity of the {@link Diagnostic}
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 */
+	public ImageDescriptor getValidationOverlayDescriptor(int severity, VElement vElement,
+		ViewModelContext viewModelContext) {
+		final VTValidationStyleProperty defaultStyle = getDefaultValidationStyle();
+		String imageUrl = null;
+
+		switch (severity) {
+		case Diagnostic.OK:
+			imageUrl = getOkOverlayURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.INFO:
+			imageUrl = getInfoOverlayURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.WARNING:
+			imageUrl = getWarningOverlayURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.ERROR:
+			imageUrl = getErrorOverlayURL(defaultStyle, vElement, viewModelContext);
+			break;
+		case Diagnostic.CANCEL:
+			imageUrl = getCancelOverlayURL(defaultStyle, vElement, viewModelContext);
+			break;
+		default:
+			throw new IllegalArgumentException(
+				"The specified severity value " + severity + " is invalid. See Diagnostic class."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		if (imageUrl == null) {
+			return null;
+		}
+		try {
+			return ImageDescriptor.createFromURL(new URL(imageUrl));
+		} catch (final MalformedURLException ex) {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns an image descriptor which can be used as an overlay for validation icons.
+	 *
+	 * @param severity the severity of the validation
+	 * @return the descriptor
+	 */
+	public ImageDescriptor getValidationOverlayDescriptor(int severity) {
+		return getValidationOverlayDescriptor(severity, null, null);
+	}
+
+	private String getOkColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getOkColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getOkColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getOkForegroundColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getOkForegroundColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getOkForegroundColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getInfoColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getInfoColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getInfoColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getInfoForegroundColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getInfoForegroundColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getInfoForegroundColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getWarningColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getWarningColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getWarningColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getWarningForegroundColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getWarningForegroundColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getWarningForegroundColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getErrorColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getErrorColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getErrorColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getErrorForegroundColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getErrorForegroundColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getErrorForegroundColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getCancelColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getCancelColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getCancelColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getCancelForegroundColorHEX(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String colorHex = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			colorHex = defaultStyle.getCancelForegroundColorHEX();
+		}
+		if (validationStyleProperty != null) {
+			colorHex = validationStyleProperty.getCancelForegroundColorHEX();
+		}
+		return colorHex;
+	}
+
+	private String getOkImageURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String imageURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			imageURL = defaultStyle.getOkImageURL();
+		}
+		if (validationStyleProperty != null) {
+			imageURL = validationStyleProperty.getOkImageURL();
+		}
+		return imageURL;
+	}
+
+	private String getInfoImageURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String imageURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			imageURL = defaultStyle.getInfoImageURL();
+		}
+		if (validationStyleProperty != null) {
+			imageURL = validationStyleProperty.getInfoImageURL();
+		}
+		return imageURL;
+	}
+
+	private String getWarningImageURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String imageURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			imageURL = defaultStyle.getWarningImageURL();
+		}
+		if (validationStyleProperty != null) {
+			imageURL = validationStyleProperty.getWarningImageURL();
+		}
+		return imageURL;
+	}
+
+	private String getErrorImageURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String imageURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			imageURL = defaultStyle.getErrorImageURL();
+		}
+		if (validationStyleProperty != null) {
+			imageURL = validationStyleProperty.getErrorImageURL();
+		}
+		return imageURL;
+	}
+
+	private String getCancelImageURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String imageURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			imageURL = defaultStyle.getCancelImageURL();
+		}
+		if (validationStyleProperty != null) {
+			imageURL = validationStyleProperty.getCancelImageURL();
+		}
+		return imageURL;
+	}
+
+	private String getOkOverlayURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String overlayURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			overlayURL = defaultStyle.getOkOverlayURL();
+		}
+		if (validationStyleProperty != null) {
+			overlayURL = validationStyleProperty.getOkOverlayURL();
+		}
+		return overlayURL;
+	}
+
+	private String getInfoOverlayURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String overlayURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			overlayURL = defaultStyle.getInfoOverlayURL();
+		}
+		if (validationStyleProperty != null) {
+			overlayURL = validationStyleProperty.getInfoOverlayURL();
+		}
+		return overlayURL;
+	}
+
+	private String getWarningOverlayURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String overlayURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			overlayURL = defaultStyle.getWarningOverlayURL();
+		}
+		if (validationStyleProperty != null) {
+			overlayURL = validationStyleProperty.getWarningOverlayURL();
+		}
+		return overlayURL;
+	}
+
+	private String getErrorOverlayURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String overlayURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			overlayURL = defaultStyle.getErrorOverlayURL();
+		}
+		if (validationStyleProperty != null) {
+			overlayURL = validationStyleProperty.getErrorOverlayURL();
+		}
+		return overlayURL;
+	}
+
+	private String getCancelOverlayURL(VTValidationStyleProperty defaultStyle, VElement vElement,
+		ViewModelContext viewModelContext) {
+		String overlayURL = null;
+		final VTValidationStyleProperty validationStyleProperty = getValidationStyleProperty(vElement,
+			viewModelContext);
+		if (defaultStyle != null) {
+			overlayURL = defaultStyle.getCancelOverlayURL();
+		}
+		if (validationStyleProperty != null) {
+			overlayURL = validationStyleProperty.getCancelOverlayURL();
+		}
+		return overlayURL;
+	}
+
+	private VTValidationStyleProperty getValidationStyleProperty(VElement vElement, ViewModelContext viewModelContext) {
+		VTValidationStyleProperty validationStyleProperty = null;
+		if (vElement != null && viewModelContext != null) {
+			final VTViewTemplateProvider vtViewTemplateProvider = Activator.getDefault().getVTViewTemplateProvider();
+			if (vtViewTemplateProvider == null) {
+				return validationStyleProperty;
+			}
+			final Set<VTStyleProperty> styleProperties = vtViewTemplateProvider.getStyleProperties(vElement,
+				viewModelContext);
+			for (final VTStyleProperty styleProperty : styleProperties) {
+				if (VTValidationStyleProperty.class.isInstance(styleProperty)) {
+					validationStyleProperty = VTValidationStyleProperty.class
+						.cast(styleProperty);
+					break;
+				}
+			}
+		}
+		return validationStyleProperty;
+	}
+
+	/**
+	 * @return The default validation style to apply if none is specified for a {@link VElement}.
+	 */
+	private VTValidationStyleProperty getDefaultValidationStyle() {
+		if (defaultValidationStyle == null) {
+			defaultValidationStyle = VTValidationFactory.eINSTANCE.createValidationStyleProperty();
+			defaultValidationStyle.setOkColorHEX("ffffff"); //$NON-NLS-1$
+			defaultValidationStyle.setErrorColorHEX("ff0000"); //$NON-NLS-1$
+			defaultValidationStyle.setWarningColorHEX("FFD800");//$NON-NLS-1$
+			defaultValidationStyle.setErrorImageURL(Activator.getDefault().getBundle()
+				.getResource("icons/validation_error.png").toExternalForm()); //$NON-NLS-1$
+			defaultValidationStyle.setErrorOverlayURL(Activator.getDefault().getBundle()
+				.getResource("icons/error_decorate.png").toExternalForm()); //$NON-NLS-1$
+			defaultValidationStyle.setWarningImageURL(Activator.getDefault().getBundle()
+				.getResource("icons/validation_warning.png").toExternalForm()); //$NON-NLS-1$
+			defaultValidationStyle.setWarningOverlayURL(Activator.getDefault().getBundle()
+				.getResource("icons/warning_decorate.png").toExternalForm()); //$NON-NLS-1$
+			defaultValidationStyle.setInfoOverlayURL(Activator.getDefault().getBundle()
+				.getResource("icons/info_decorate.gif").toExternalForm()); //$NON-NLS-1$
+		}
+		return defaultValidationStyle;
+	}
+
+	/**
+	 * @param colorHex
+	 * @return
+	 */
+	private Color getColor(String colorHex) {
+		final String redString = colorHex.substring(0, 2);
+		final String greenString = colorHex.substring(2, 4);
+		final String blueString = colorHex.substring(4, 6);
+		final int red = Integer.parseInt(redString, 16);
+		final int green = Integer.parseInt(greenString, 16);
+		final int blue = Integer.parseInt(blueString, 16);
+		return new Color(Display.getDefault(), red, green, blue);
+	}
+
+}
diff --git a/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/spi/swt/core/ui/SWTValidationUiService.java b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/spi/swt/core/ui/SWTValidationUiService.java
new file mode 100644
index 0000000..5de1704
--- /dev/null
+++ b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/spi/swt/core/ui/SWTValidationUiService.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Lucas Koehler - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emfforms.spi.swt.core.ui;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
+import org.eclipse.emf.ecp.view.spi.model.VElement;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Service implementations provide visualizations (icons and colors) for validation results.
+ *
+ * @author Lucas Koehler
+ * @since 1.23
+ *
+ */
+public interface SWTValidationUiService {
+
+	/**
+	 * Returns the validation icon matching the given Diagnostic, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param diagnostic the {@link Diagnostic} defining the validation visualization
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 */
+	Image getValidationIcon(Diagnostic diagnostic, VElement vElement, ViewModelContext viewModelContext);
+
+	/**
+	 * Returns the validation icon matching the highest severity of the given VElement's diagnostic.
+	 *
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed
+	 */
+	Image getValidationIcon(VElement vElement, ViewModelContext viewModelContext);
+
+	/**
+	 * Returns the foreground color for a control with the given Diagnostic, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param diagnostic the {@link Diagnostic} defining the validation visualization
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the color to be used as a foreground color
+	 */
+	Color getValidationForegroundColor(Diagnostic diagnostic, VElement vElement, ViewModelContext viewModelContext);
+
+	/**
+	 * Returns the foreground color for a control matching the highest severity of the given VElement's diagnostic.
+	 *
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the color to be used as a foreground color
+	 */
+	Color getValidationForegroundColor(VElement vElement, ViewModelContext viewModelContext);
+
+	/**
+	 * Returns the background color for a control with the given Diagnostic, VElement
+	 * and view model context, if applicable.
+	 *
+	 * @param diagnostic the {@link Diagnostic} defining the validation visualization
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the color to be used as a background color
+	 */
+	Color getValidationBackgroundColor(Diagnostic diagnostic, VElement vElement, ViewModelContext viewModelContext);
+
+	/**
+	 * Returns the background color for a control matching the highest severity of the given VElement's diagnostic.
+	 *
+	 * @param vElement The {@link VElement} that is being rendered
+	 * @param viewModelContext The corresponding {@link ViewModelContext}
+	 * @return the color to be used as a background color
+	 */
+	Color getValidationBackgroundColor(VElement vElement, ViewModelContext viewModelContext);
+	// TODO more methods?
+}
diff --git a/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/META-INF/MANIFEST.MF b/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/META-INF/MANIFEST.MF
index 99b76ba..084ae9f 100644
--- a/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/META-INF/MANIFEST.MF
@@ -10,7 +10,8 @@
  org.eclipse.emfforms.swt.treemasterdetail;bundle-version="[1.23.0,1.24.0)",
  org.eclipse.emfforms.common;bundle-version="[1.23.0,1.24.0)",
  org.eclipse.emf.ecp.common;bundle-version="[1.23.0,1.24.0)",
- org.eclipse.emf.ecp.edit.swt;bundle-version="[1.23.0,1.24.0)"
+ org.eclipse.emf.ecp.edit.swt;bundle-version="[1.23.0,1.24.0)",
+ org.eclipse.emfforms.swt.core;bundle-version="[1.23.0,1.24.0)"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Automatic-Module-Name: org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default
 Import-Package: org.eclipse.jface.resource;version="0.0.0",
diff --git a/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/src/org/eclipse/emfforms/internal/swt/treemasterdetail/decorator/validation/ecp/ECPValidationServiceLabelDecorator.java b/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/src/org/eclipse/emfforms/internal/swt/treemasterdetail/decorator/validation/ecp/ECPValidationServiceLabelDecorator.java
index 72a75af..bb6e6f7 100644
--- a/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/src/org/eclipse/emfforms/internal/swt/treemasterdetail/decorator/validation/ecp/ECPValidationServiceLabelDecorator.java
+++ b/bundles/org.eclipse.emfforms.swt.treemasterdetail.decorator.validation.default/src/org/eclipse/emfforms/internal/swt/treemasterdetail/decorator/validation/ecp/ECPValidationServiceLabelDecorator.java
@@ -19,7 +19,7 @@
 import org.eclipse.emf.common.notify.Notifier;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
 import org.eclipse.emfforms.spi.swt.treemasterdetail.diagnostic.DiagnosticCache;
 import org.eclipse.emfforms.spi.swt.treemasterdetail.diagnostic.DiagnosticCache.ValidationListener;
 import org.eclipse.jface.resource.DeviceResourceManager;
diff --git a/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer_PTest.java b/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer_PTest.java
index 8a1444b..8bd9679 100644
--- a/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer_PTest.java
+++ b/tests/org.eclipse.emf.ecp.view.core.swt.tests/src/org/eclipse/emf/ecp/view/spi/core/swt/AbstractControlSWTRenderer_PTest.java
@@ -15,9 +15,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -31,7 +33,6 @@
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.EcorePackage;
-import org.eclipse.emf.ecp.edit.spi.swt.util.SWTValidationHelper;
 import org.eclipse.emf.ecp.test.common.DefaultRealm;
 import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
 import org.eclipse.emf.ecp.view.spi.model.LabelAlignment;
@@ -54,6 +55,8 @@
 import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
 import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationUiService;
 import org.eclipse.emfforms.swt.common.test.AbstractControl_PTest.TestObservableValue;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Color;
@@ -232,8 +235,10 @@
 		assertEquals(SWT.RIGHT, Label.class.cast(render).getAlignment());
 	}
 
+	@SuppressWarnings("deprecation")
 	@Test
-	public void testgetValidationBackgroundColor() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
+	public void testgetValidationBackgroundColorLegacy()
+		throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
 		/* setup */
 		final SWTValidationHelper validationHelper = Mockito.mock(SWTValidationHelper.class);
 
@@ -275,8 +280,10 @@
 
 	}
 
+	@SuppressWarnings("deprecation")
 	@Test
-	public void testgetValidationForeGroundColor() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
+	public void testgetValidationForeGroundColorLegacy()
+		throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
 		/* setup */
 		final SWTValidationHelper validationHelper = Mockito.mock(SWTValidationHelper.class);
 
@@ -317,8 +324,9 @@
 
 	}
 
+	@SuppressWarnings("deprecation")
 	@Test
-	public void testgetValidationIcon() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
+	public void testgetValidationIconLegacy() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
 		/* setup */
 		final SWTValidationHelper validationHelper = Mockito.mock(SWTValidationHelper.class);
 
@@ -469,6 +477,98 @@
 		assertEquals(SWT.RIGHT, Label.class.cast(render).getAlignment());
 	}
 
+	/** Verify retrieving the validation icon for a VElement is forwarded to the {@link SWTValidationUiService}. */
+	@Test
+	public void getValidationIcon() {
+		final SWTValidationUiService validationUiService = mock(SWTValidationUiService.class);
+
+		renderer = new TestAbstractControlSWTRenderer(
+			vControl,
+			viewModelContext,
+			reportService,
+			emfFormsDatabinding,
+			emfFormsLabelProvider,
+			viewTemplateProvider, validationUiService);
+		renderer.init();
+
+		final Image expected = new Image(Display.getCurrent(), 1, 1);
+		when(validationUiService.getValidationIcon(vControl, viewModelContext)).thenReturn(expected);
+
+		final Image result = renderer.getValidationIcon();
+		assertSame(expected, result);
+	}
+
+	/**
+	 * Verify retrieving the validation foreground color for a VElement is forwarded to the
+	 * {@link SWTValidationUiService}.
+	 */
+	@Test
+	public void getValidationForegroundColor() {
+		final SWTValidationUiService validationUiService = mock(SWTValidationUiService.class);
+
+		renderer = new TestAbstractControlSWTRenderer(
+			vControl,
+			viewModelContext,
+			reportService,
+			emfFormsDatabinding,
+			emfFormsLabelProvider,
+			viewTemplateProvider, validationUiService);
+		renderer.init();
+
+		final Color expected = new Color(Display.getCurrent(), 1, 2, 3);
+		when(validationUiService.getValidationForegroundColor(vControl, viewModelContext)).thenReturn(expected);
+
+		final Color result = renderer.getValidationForegroundColor();
+		assertSame(expected, result);
+	}
+
+	/**
+	 * Verify retrieving the validation background color for a VElement is forwarded to the
+	 * {@link SWTValidationUiService}.
+	 */
+	@Test
+	public void getValidationBackgroundColor() {
+		final SWTValidationUiService validationUiService = mock(SWTValidationUiService.class);
+
+		renderer = new TestAbstractControlSWTRenderer(
+			vControl,
+			viewModelContext,
+			reportService,
+			emfFormsDatabinding,
+			emfFormsLabelProvider,
+			viewTemplateProvider, validationUiService);
+		renderer.init();
+
+		final Color expected = new Color(Display.getCurrent(), 1, 2, 3);
+		when(validationUiService.getValidationBackgroundColor(vControl, viewModelContext)).thenReturn(expected);
+
+		final Color result = renderer.getValidationBackgroundColor();
+		assertSame(expected, result);
+	}
+
+	/**
+	 * Test that the {@link SWTValidationUiService} implementation is retrieved from the view model context if it is not
+	 * given in the constructor.
+	 */
+	@Test
+	public void validationUiServiceRetrieval() {
+		final SWTValidationUiService customService = mock(SWTValidationUiService.class);
+		final Image expected = new Image(Display.getCurrent(), 1, 1);
+		when(customService.getValidationIcon(vControl, viewModelContext)).thenReturn(expected);
+		when(viewModelContext.getService(SWTValidationUiService.class)).thenReturn(customService);
+		renderer = new TestAbstractControlSWTRenderer(
+			vControl,
+			viewModelContext,
+			reportService,
+			emfFormsDatabinding,
+			emfFormsLabelProvider,
+			viewTemplateProvider);
+		renderer.init();
+		final Image result = renderer.getValidationIcon();
+		assertSame(expected, result);
+
+	}
+
 	private class TestAbstractControlSWTRenderer extends AbstractControlSWTRenderer<VControl> {
 
 		TestAbstractControlSWTRenderer(
@@ -503,6 +603,22 @@
 				vtViewTemplateProvider, swtValidationHelper);
 		}
 
+		TestAbstractControlSWTRenderer(
+			VControl vElement,
+			ViewModelContext viewContext,
+			ReportService reportService,
+			EMFFormsDatabinding emfFormsDatabinding,
+			EMFFormsLabelProvider emfFormsLabelProvider,
+			VTViewTemplateProvider vtViewTemplateProvider, SWTValidationUiService validationUiService) {
+			super(
+				vElement,
+				viewContext,
+				reportService,
+				emfFormsDatabinding,
+				emfFormsLabelProvider,
+				vtViewTemplateProvider, validationUiService);
+		}
+
 		@Override
 		public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
 			final SWTGridDescription simpleGrid = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
diff --git a/tests/org.eclipse.emfforms.swt.core.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.emfforms.swt.core.tests/META-INF/MANIFEST.MF
index 3de5f21..af7cb0c 100644
--- a/tests/org.eclipse.emfforms.swt.core.tests/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.emfforms.swt.core.tests/META-INF/MANIFEST.MF
@@ -5,6 +5,7 @@
 Bundle-Version: 1.23.0.qualifier
 Fragment-Host: org.eclipse.emfforms.swt.core;bundle-version="[1.23.0,1.24.0)"
 Export-Package: org.eclipse.emfforms.internal.swt.core;version="1.23.0";x-internal:=true,
+ org.eclipse.emfforms.internal.swt.core.ui;version="1.23.0";x-internal:=true,
  org.eclipse.emfforms.spi.swt.core;version="1.23.0",
  org.eclipse.emfforms.swt.core.tests;version="1.23.0";x-internal:=true
 Require-Bundle: org.junit;bundle-version="[4.11.0,5.0.0)",
diff --git a/tests/org.eclipse.emfforms.swt.core.tests/pom.xml b/tests/org.eclipse.emfforms.swt.core.tests/pom.xml
index eee2e7c..bdf9532 100644
--- a/tests/org.eclipse.emfforms.swt.core.tests/pom.xml
+++ b/tests/org.eclipse.emfforms.swt.core.tests/pom.xml
@@ -22,11 +22,38 @@
 				<artifactId>tycho-surefire-plugin</artifactId>
 				<version>${tycho-version}</version>
 				<configuration>
+					<includes>
+						<include>**/*_PTest.java</include>
+						<include>**/*_ITest.java</include>
+						<!-- Execute SWT-Related tests with tycho surefire -->
+						<include>**/SWTValidationUiServiceImpl_Test.java</include>
+					</includes>
 					<useUIHarness>false</useUIHarness>
 					<useUIThread>false</useUIThread>
 				</configuration>
 			</plugin>
 			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>${mav-surefire-version}</version>
+				<executions>
+					<execution>
+						<id>test</id>
+						<phase>test</phase>
+						<configuration>
+							<testClassesDirectory>${project.build.outputDirectory}</testClassesDirectory>
+							<excludes>
+								<!-- Executed with tycho surefire -->
+								<exclude>**/SWTValidationUiServiceImpl_Test.java</exclude>
+							</excludes>
+						</configuration>
+						<goals>
+							<goal>test</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
 				<groupId>org.eclipse.tycho</groupId>
 				<artifactId>target-platform-configuration</artifactId>
 				<configuration>
diff --git a/tests/org.eclipse.emfforms.swt.core.tests/src/org/eclipse/emfforms/internal/swt/core/ui/SWTValidationUiServiceImpl_Test.java b/tests/org.eclipse.emfforms.swt.core.tests/src/org/eclipse/emfforms/internal/swt/core/ui/SWTValidationUiServiceImpl_Test.java
new file mode 100644
index 0000000..a8f087f
--- /dev/null
+++ b/tests/org.eclipse.emfforms.swt.core.tests/src/org/eclipse/emfforms/internal/swt/core/ui/SWTValidationUiServiceImpl_Test.java
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Lucas Koehler - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emfforms.internal.swt.core.ui;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
+import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
+import org.eclipse.emf.ecp.view.spi.model.VElement;
+import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
+import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link SWTValidationUiServiceImpl}.
+ *
+ * @author Lucas Koehler
+ *
+ */
+public class SWTValidationUiServiceImpl_Test {
+
+	private SWTValidationHelper validationHelper;
+	private SWTValidationUiServiceImpl fixture;
+	private VElement vElement;
+	private ViewModelContext viewContext;
+
+	@Before
+	public void setUp() {
+		validationHelper = mock(SWTValidationHelper.class);
+		fixture = new SWTValidationUiServiceImpl(validationHelper);
+		vElement = mock(VElement.class);
+		viewContext = mock(ViewModelContext.class);
+	}
+
+	/**
+	 * Test that getValidationIcon(VElement, ViewModelContext) correctly delegates to getValidationIcon(Diagnostic,
+	 * VElement, ViewModelContext) and that the latter correctly delegates to SWTValidationHelper.
+	 */
+	@Test
+	public void getValidationIcon() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
+		final Diagnostic ok = mock(Diagnostic.class);
+		when(ok.getSeverity()).thenReturn(Diagnostic.OK);
+		final Diagnostic error = mock(Diagnostic.class);
+		when(error.getSeverity()).thenReturn(Diagnostic.ERROR);
+		vDiagnostic.getDiagnostics().add(ok);
+		vDiagnostic.getDiagnostics().add(error);
+		when(vElement.getDiagnostic()).thenReturn(vDiagnostic);
+		final Image expected = new Image(Display.getDefault(), 1, 1);
+		when(validationHelper.getValidationIcon(Diagnostic.ERROR, vElement, viewContext)).thenReturn(expected);
+
+		final Image result = spiedFixture.getValidationIcon(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationIcon(error, vElement, viewContext);
+	}
+
+	/** If the VElement doesn't have a VDiagnostic, it should be assumed that there is no validation error. */
+	@Test
+	public void getValidationIcon_nullVDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		when(vElement.getDiagnostic()).thenReturn(null);
+		final Image expected = new Image(Display.getDefault(), 1, 1);
+		when(validationHelper.getValidationIcon(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Image result = spiedFixture.getValidationIcon(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationIcon(Diagnostic.OK_INSTANCE, vElement, viewContext);
+	}
+
+	/**
+	 * If the VElement's VDiagnostic doesn't have any Diagnostics, it should be assumed that there is no validation
+	 * error.
+	 */
+	@Test
+	public void getValidationIcon_emptyVDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
+		when(vElement.getDiagnostic()).thenReturn(vDiagnostic);
+		final Image expected = new Image(Display.getDefault(), 1, 1);
+		when(validationHelper.getValidationIcon(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Image result = spiedFixture.getValidationIcon(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationIcon(Diagnostic.OK_INSTANCE, vElement, viewContext);
+	}
+
+	/**
+	 * If the given Diagnostic is null, it should be assumed that there is no validation error.
+	 */
+	@Test
+	public void getValidationIcon_nullDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final Image expected = new Image(Display.getDefault(), 1, 1);
+		when(validationHelper.getValidationIcon(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Image result = spiedFixture.getValidationIcon(null, vElement, viewContext);
+
+		assertSame(expected, result);
+	}
+
+	/** The service should cache icons by severity. */
+	@Test
+	public void getValidationIcon_caching() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final Image expected = new Image(Display.getDefault(), 1, 1);
+		when(validationHelper.getValidationIcon(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+		final Diagnostic ok1 = mock(Diagnostic.class);
+		when(ok1.getSeverity()).thenReturn(Diagnostic.OK);
+		final Diagnostic ok2 = mock(Diagnostic.class);
+		when(ok2.getSeverity()).thenReturn(Diagnostic.OK);
+
+		final Image result1 = spiedFixture.getValidationIcon(ok1, vElement, viewContext);
+		final Image result2 = spiedFixture.getValidationIcon(ok2, vElement, viewContext);
+
+		assertSame(expected, result1);
+		assertSame(expected, result2);
+		// if caching is used, the validation helper only needs to be called once
+		verify(validationHelper, times(1)).getValidationIcon(Diagnostic.OK, vElement, viewContext);
+	}
+
+	/**
+	 * Test that getValidationForegroundColor(VElement, ViewModelContext) correctly delegates to
+	 * getValidationForegroundColor(Diagnostic, VElement, ViewModelContext) and that the latter correctly delegates to
+	 * SWTValidationHelper.
+	 */
+	@Test
+	public void getValidationForegroundColor() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
+		final Diagnostic ok = mock(Diagnostic.class);
+		when(ok.getSeverity()).thenReturn(Diagnostic.OK);
+		final Diagnostic error = mock(Diagnostic.class);
+		when(error.getSeverity()).thenReturn(Diagnostic.ERROR);
+		vDiagnostic.getDiagnostics().add(ok);
+		vDiagnostic.getDiagnostics().add(error);
+		when(vElement.getDiagnostic()).thenReturn(vDiagnostic);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationForegroundColor(Diagnostic.ERROR, vElement, viewContext))
+			.thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationForegroundColor(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationForegroundColor(error, vElement, viewContext);
+	}
+
+	/** If the VElement doesn't have a VDiagnostic, it should be assumed that there is no validation error. */
+	@Test
+	public void getValidationForegroundColor_nullVDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		when(vElement.getDiagnostic()).thenReturn(null);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationForegroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationForegroundColor(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationForegroundColor(Diagnostic.OK_INSTANCE, vElement, viewContext);
+	}
+
+	/**
+	 * If the VElement's VDiagnostic doesn't have any Diagnostics, it should be assumed that there is no validation
+	 * error.
+	 */
+	@Test
+	public void getValidationForegroundColor_emptyVDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
+		when(vElement.getDiagnostic()).thenReturn(vDiagnostic);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationForegroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationForegroundColor(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationForegroundColor(Diagnostic.OK_INSTANCE, vElement, viewContext);
+	}
+
+	/**
+	 * If the given Diagnostic is null, it should be assumed that there is no validation error.
+	 */
+	@Test
+	public void getValidationForegroundColor_nullDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationForegroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationForegroundColor(null, vElement, viewContext);
+
+		assertSame(expected, result);
+	}
+
+	/** The service should cache icons by severity. */
+	@Test
+	public void getValidationForegroundColor_caching() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationForegroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+		final Diagnostic ok1 = mock(Diagnostic.class);
+		when(ok1.getSeverity()).thenReturn(Diagnostic.OK);
+		final Diagnostic ok2 = mock(Diagnostic.class);
+		when(ok2.getSeverity()).thenReturn(Diagnostic.OK);
+
+		final Color result1 = spiedFixture.getValidationForegroundColor(ok1, vElement, viewContext);
+		final Color result2 = spiedFixture.getValidationForegroundColor(ok2, vElement, viewContext);
+
+		assertSame(expected, result1);
+		assertSame(expected, result2);
+		// if caching is used, the validation helper only needs to be called once
+		verify(validationHelper, times(1)).getValidationForegroundColor(Diagnostic.OK, vElement, viewContext);
+	}
+
+	/**
+	 * Test that getValidationBackgroundColor(VElement, ViewModelContext) correctly delegates to
+	 * getValidationBackgroundColor(Diagnostic, VElement, ViewModelContext) and that the latter correctly delegates to
+	 * SWTValidationHelper.
+	 */
+	@Test
+	public void getValidationBackgroundColor() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
+		final Diagnostic ok = mock(Diagnostic.class);
+		when(ok.getSeverity()).thenReturn(Diagnostic.OK);
+		final Diagnostic error = mock(Diagnostic.class);
+		when(error.getSeverity()).thenReturn(Diagnostic.ERROR);
+		vDiagnostic.getDiagnostics().add(ok);
+		vDiagnostic.getDiagnostics().add(error);
+		when(vElement.getDiagnostic()).thenReturn(vDiagnostic);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationBackgroundColor(Diagnostic.ERROR, vElement, viewContext))
+			.thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationBackgroundColor(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationBackgroundColor(error, vElement, viewContext);
+	}
+
+	/** If the VElement doesn't have a VDiagnostic, it should be assumed that there is no validation error. */
+	@Test
+	public void getValidationBackgroundColor_nullVDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		when(vElement.getDiagnostic()).thenReturn(null);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationBackgroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationBackgroundColor(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationBackgroundColor(Diagnostic.OK_INSTANCE, vElement, viewContext);
+	}
+
+	/**
+	 * If the VElement's VDiagnostic doesn't have any Diagnostics, it should be assumed that there is no validation
+	 * error.
+	 */
+	@Test
+	public void getValidationBackgroundColor_emptyVDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
+		when(vElement.getDiagnostic()).thenReturn(vDiagnostic);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationBackgroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationBackgroundColor(vElement, viewContext);
+
+		assertSame(expected, result);
+		verify(spiedFixture).getValidationBackgroundColor(Diagnostic.OK_INSTANCE, vElement, viewContext);
+	}
+
+	/**
+	 * If the given Diagnostic is null, it should be assumed that there is no validation error.
+	 */
+	@Test
+	public void getValidationBackgroundColor_nullDiagnostic() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationBackgroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+
+		final Color result = spiedFixture.getValidationBackgroundColor(null, vElement, viewContext);
+
+		assertSame(expected, result);
+	}
+
+	/** The service should cache icons by severity. */
+	@Test
+	public void getValidationBackgroundColor_caching() {
+		// Spy to be able to verify method calls
+		final SWTValidationUiServiceImpl spiedFixture = spy(fixture);
+		final Color expected = new Color(Display.getDefault(), 1, 2, 3);
+		when(validationHelper.getValidationBackgroundColor(Diagnostic.OK, vElement, viewContext)).thenReturn(expected);
+		final Diagnostic ok1 = mock(Diagnostic.class);
+		when(ok1.getSeverity()).thenReturn(Diagnostic.OK);
+		final Diagnostic ok2 = mock(Diagnostic.class);
+		when(ok2.getSeverity()).thenReturn(Diagnostic.OK);
+
+		final Color result1 = spiedFixture.getValidationBackgroundColor(ok1, vElement, viewContext);
+		final Color result2 = spiedFixture.getValidationBackgroundColor(ok2, vElement, viewContext);
+
+		assertSame(expected, result1);
+		assertSame(expected, result2);
+		// if caching is used, the validation helper only needs to be called once
+		verify(validationHelper, times(1)).getValidationBackgroundColor(Diagnostic.OK, vElement, viewContext);
+	}
+}