In Service Manager 2010, when you open an Incident (or most any other Work Item) that is resolved or closed, all of the controls on the form are disabled. You can no longer edit the incident, it cannot be changed.
This is great, you don’t want your analysts, or, indeed, anyone else, making changes to such incidents.
However, in Service Manager 2012, the controls on the form are not disabled. Therefore, anyone with access to the incident can change it after it has been resolved or even closed.
Not good!
So, how do you get around this?
It is possible by adding your own custom control to the incident form. In fact, this technique can be used for the other class forms, too.
Here’s how…
In Visual Studio, create a new .NET Framework 3.5 WPF User Control Library:
Name it like this and add the highlighted references:
Check the code for the xaml file, make sure the namespace, etc match:
namespace DisableForm
{
public partial class DisableFormControl : UserControl
{
Right-click "InitializeComponent()" Go To Definition and check the same there:
namespace DisableForm {
public partial class DisableFormControl : System.Windows.Controls.UserControl, System.Windows.Markup.IComponentConnector {
Open the xaml file in the designer and replace all of the xaml with this:
<UserControl x:Class="DisableForm.DisableFormControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Visibility="Collapsed"
Width="Auto" Height="Auto" DataContextChanged="DisableFormControl_DataContextChanged" >
<Grid Height="Auto" Width="Auto">
<TextBox Height="0" Margin="0,0,0,0" Name="textId" VerticalAlignment="Top" Width="0" Text="{Binding Path=$Id$, Mode=OneWay}" Visibility="Collapsed" />
</Grid>
</UserControl>
The textbox is purely to give us a binding to our control to work with.
Now replace all of the xaml.cs code with this:
/*Custom control to disable incident form if status is Resolved or Closed for Service Manager 2012
*
* Rob Ford 2012
*
*/
//Standard for .NET 3.5 WPF control
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
//Additional requirements
using System.ComponentModel;
using System.ComponentModel.Design;
using Microsoft.EnterpriseManagement;
using Microsoft.EnterpriseManagement.Common;
using Microsoft.EnterpriseManagement.Configuration;
using Microsoft.EnterpriseManagement.UI.DataModel;
using Microsoft.EnterpriseManagement.UI.SdkDataAccess;
using Microsoft.EnterpriseManagement.ConsoleFramework;
using Microsoft.EnterpriseManagement.UI.WpfControls;
namespace DisableForm
{
public partial class DisableFormControl : UserControl
{
//Current session
private EnterpriseManagementGroup emg = null;
//Current instance
private IDataItem inst = null;
public DisableFormControl()
{
InitializeComponent();
//Connect to the current console session
this.emg = GetSession();
}
private EnterpriseManagementGroup GetSession()
{
//Get the current console session management group
IServiceContainer container = (IServiceContainer)FrameworkServices.GetService(typeof(IServiceContainer));
Microsoft.EnterpriseManagement.UI.Core.Connection.IManagementGroupSession curSession =
(Microsoft.EnterpriseManagement.UI.Core.Connection.IManagementGroupSession)container.GetService(typeof(Microsoft.EnterpriseManagement.UI.Core.Connection.IManagementGroupSession));
return curSession.ManagementGroup;
}
private void DisableFormControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
//Check binding, we want an IDataItem
if (this.DataContext is IDataItem)
{
//Get the IDataItem
inst = (IDataItem)this.DataContext;
//Check if this is a new incident form
if ((bool)inst["$IsNew$"])
{
//You could add code here to set fom defaults, etc
}
else
{
//Editing existing incident
//Status Guids
Guid gStatus = Guid.NewGuid();
Guid gResolved = new Guid("2b8830b6-59f0-f574-9c2a-f4b4682f1681");
Guid gClosed = new Guid("bd0ae7c4-3315-2eb3-7933-82dfc482dbaf");
//Get Status Guid
if (inst["Status"] != null) gStatus = (Guid)(inst["Status"] as IDataItem)["Id"];
//Check incident status and disable form if needed
if (gStatus == gResolved || gStatus == gClosed) DisableForm();
}
}
}
//Disables all required controls on the incident form to prevent editing
private void DisableForm()
{
try
{
//Attempt to get the parent TabControl of our custom control
//There is a TabControl near the top of the tree, we want the one below that
DependencyObject doParentRoot = GetParentDependancyObject(this, "System.Windows.Controls.TabControl");
//Did we get the TabControl?
if (doParentRoot != null)
{
//Now process each TabItem on the tab control
foreach (DependencyObject doChild in LogicalTreeHelper.GetChildren(doParentRoot))
{
//Is this a TabItem?
if (doChild.GetType().ToString() == "System.Windows.Controls.TabItem")
{
//Process each child on current tab and disable or set readonly
DisableLogicalFormControls(doChild);
}
}
}
//Now, we must go to the top form level so we can disable the OK and Apply buttons
//Passing "" will navigate to top
doParentRoot = GetParentDependancyObject(this, "");
if (doParentRoot != null)
{
//If we got the top parent, disable the buttons
DisableVisualButtons(doParentRoot);
}
}
catch
{
}
}
//Navigates the Visual tree for passed parent and disables buttons, except for Cancel
private void DisableVisualButtons(DependencyObject rootparent)
{
//Get count of children for current parent
int iCount = VisualTreeHelper.GetChildrenCount(rootparent);
//Process each child
for (int i = 0; i < iCount; i++)
{
//Get next child
DependencyObject rootChild = VisualTreeHelper.GetChild(rootparent, i);
try
{
//Do we have a button?
if (rootChild.GetType().ToString() == "System.Windows.Controls.Button")
{
//Disable all buttons except for Cancel
Button b = (Button)rootChild;
if (b.Content.ToString().IndexOf("Cancel") == -1) b.IsEnabled = false;
}
}
catch
{
}
//Disable further children of this child
DisableVisualButtons(rootChild);
}
}
//Navigates the Logical tree for passed parent and disables common controls or sets to readonly for textboxes
private void DisableLogicalFormControls(DependencyObject rootparent)
{
//Navigate the logical tree for the children
foreach (var rootChild in LogicalTreeHelper.GetChildren(rootparent))
{
try
{
if (rootChild is DependencyObject)
{
if (rootChild.GetType().ToString() == "Microsoft.EnterpriseManagement.UI.WpfControls.ListPicker")
{
//Disable all listpickers
ListPicker l = (ListPicker)rootChild;
l.IsEnabled = false;
}
else if (rootChild.GetType().ToString() == "System.Windows.Controls.TextBox")
{
//Set all textboxes readonly
TextBox tb = (TextBox)rootChild;
tb.IsReadOnly = true;
}
else if (rootChild.GetType().ToString() == "System.Windows.Controls.CheckBox")
{
//Disable all checkboxes
CheckBox cb = (CheckBox)rootChild;
cb.IsEnabled = false;
}
else if (rootChild.GetType().ToString() == "System.Windows.Controls.Button")
{
//Disable all buttons
Button b = (Button)rootChild;
b.IsEnabled = false;
}
else if (rootChild.GetType().ToString() == "Microsoft.EnterpriseManagement.UI.WpfControls.SpinControl")
{
//Disable all spin controls
SpinControl sc = (SpinControl)rootChild;
sc.IsEnabled = false;
}
}
}
catch
{
}
//Disable further logical children of this child
if (rootChild is DependencyObject) DisableLogicalFormControls(rootChild as DependencyObject);
}
}
//Returns specified parent object, if found, if name is empty, then navigate to top
private DependencyObject GetParentDependancyObject(DependencyObject child, string name)
{
try
{
//We need the logical tree to get our parent
DependencyObject parent = LogicalTreeHelper.GetParent(child);
DependencyObject lastparent = null;
//Is the parent our specified control?
if (name != "" && parent.GetType().ToString() == name) return parent;
//No, process further
while (parent != null)
{
string s = parent.GetType().ToString();
if (s == name && name != "") return parent;
parent = LogicalTreeHelper.GetParent(parent);
if (parent != null) lastparent = parent;
}
//Return results
if (name != "") return null;
else return lastparent;
}
catch
{
return null;
}
}
}
}
This code contains everything you need. It connects to the current console session and gets the data binding. A check is made for a new (being created) incident or not. If not, the status is checked to see if the incident is resolved or closed here:
//Editing existing incident
//Status Guids
Guid gStatus = Guid.NewGuid();
Guid gResolved = new Guid("2b8830b6-59f0-f574-9c2a-f4b4682f1681");
Guid gClosed = new Guid("bd0ae7c4-3315-2eb3-7933-82dfc482dbaf");
//Get Status Guid
if (inst["Status"] != null) gStatus = (Guid)(inst["Status"] as IDataItem)["Id"];
//Check incident status and disable form if needed
if (gStatus == gResolved || gStatus == gClosed) DisableForm();
DisableForm() is called if the incident status is Resolved or Closed. This then calls several routines that walk the Visual and Logical WPF control trees to obtain the parent TabControl of your custom control. Each tab on this control is then processed and all controls specified and either disabled or set readonly. The controls are specified here:
if (rootChild.GetType().ToString() == "Microsoft.EnterpriseManagement.UI.WpfControls.ListPicker")
{
//Disable all listpickers
ListPicker l = (ListPicker)rootChild;
l.IsEnabled = false;
}
else if (rootChild.GetType().ToString() == "System.Windows.Controls.TextBox")
{
//Set all textboxes readonly
TextBox tb = (TextBox)rootChild;
tb.IsReadOnly = true;
}
else if (rootChild.GetType().ToString() == "System.Windows.Controls.CheckBox")
{
//Disable all checkboxes
CheckBox cb = (CheckBox)rootChild;
cb.IsEnabled = false;
}
else if (rootChild.GetType().ToString() == "System.Windows.Controls.Button")
{
//Disable all buttons
Button b = (Button)rootChild;
b.IsEnabled = false;
}
else if (rootChild.GetType().ToString() == "Microsoft.EnterpriseManagement.UI.WpfControls.SpinControl")
{
//Disable all spin controls
SpinControl sc = (SpinControl)rootChild;
sc.IsEnabled = false;
}
Setting text boxes to read-only allows their text to be copied if required. Also, attachments can still be opened if needed.
A 2nd step then walks to the very top of the tree and sets the Apply and OK buttons on the main form to disabled.
That's it for the DLL.
Note - if you are not using English as your console language, you'll need to tweak the code where the OK and Apply buttons are disabled.
Compile the assembly to create DisableFormControl.dll. Those of you who are familiar with adding controls to to your own custom forms can now add this control onto the incident form. That is all that is required. Opening a non-active incident will set the form controls to disabled.
For the rest of you, I will show you how to do this in part 2...


[...] Further information and basic source (minus additional status guids) can be found on my blog site here: http://scsmnz.net/service-manager-2012-how-to-disable-form-controls-for-a-resolved-or-closed-inciden… [...]
Great post, congratulations.
I am trying set impact field in the form but scsm get a error.
I am trying with:
inst["Impact"] = emg.EntityTypes.GetEnumeration(new Guid(“8f1a713e-53aa-9d8a-31b9-a9540074f305″));
is correct?
Hi,
Try using:
inst["Impact"] = ConsoleContextHelper.Instance.GetEnumeration(new Guid(“8f1a713e-53aa-9d8a-31b9-a9540074f305″));
I have been having trouble with part 2 of this, so I did this step all over again and have some comments:
Target framework needs to be set to .NET framework 4 in order to compile (for me atleast) – mine was set to .NET framework 4 client profile.
Microsoft.EnterpriseManagement.Core.dll is only found on the management server. Wouldn’t it be missing in the console installations?
With solution named DisableForm and project DisableFormControl the DLL is by default named “DisableForm.dll” (as the solution), or am I wrong? May be abit confusing for some in the next part.
I am missing a copy-to-clipboard for the code. If I copy-paste manually line-breaks are no retained. I had to do it from the html source and re-instart “, <, and & in the code.
Otherwise a very nice post!