Most of our ISV Partners will need to provide some custom administration settings to allow their customers to configure the partners' solutions. For example, you may want to store a server name and database name to connect to as part of a workflow and then access that information at the time the workflow runs. As a partner you would want to make it easy for your customers to edit this configuration and do so in a way that is consistent with the rest of the Service Manager product.
The Settings view in the Administration workspace is the "catch all" view for Service Manager administrators to view these kinds of settings. Out of the box, we put things like grooming settings, incident management settings like the Target Time to Resolution settings, and many others in this view.
The question is – "How do I add my own custom settings to this view and provide a user experience for administrators to get/set these settings?"
This is what we are trying to achieve:
This is what the user experience looks like:
When the user double-clicks or clicks Properties:
First we need to create a management pack with our new Admin Setting class in it. If we derive from System.SolutionSettings our item will automatically show up in the Settings view. Further, since we aren't going to be creating multiple instances of this class we should set it to Singleton="true". For this demo, I'm adding a couple of basic properties creatively named Property1 and Property2.
<ClassType ID="Microsoft.Demo.AdminSetting.Example"
Accessibility="Public"
Abstract="false"
Base="AdminItem.Library!System.SolutionSettings"
Hosted="false"
Singleton="true"
Extension="false">
<Property ID="Property1"
Type="string"
AutoIncrement="false"
Key="false"
CaseSensitive="false"
MaxLength="256"
MinLength="0"
Required="false"
/>
<Property ID="Property2"
AutoIncrement="false" Key="false"
</ClassType>
Then we need to define a console task handler so that when the user clicks the Properties link in the task pane our form will come up. Notice how the task is targeted at our singleton class. It will call the class in the referenced assembly which I'll show you in a minute.
<ConsoleTask ID="ConsoleTask.Microsoft.Demo.AdminSetting.Example.Edit"
Enabled="true"
Target="Microsoft.Demo.AdminSetting.Example" RequireOutput="false">
<Assembly>Console!SdkDataAccessAssembly</Assembly>
<Handler>
Microsoft.EnterpriseManagement.UI.SdkDataAccess.ConsoleTaskHandler
</Handler>
<Parameters>
<Argument Name="Assembly">AdminSettingsExample</Argument>
<Argument Name="Type">
Microsoft.Demo.AdminSettings.AdminSettingsExample
</Argument>
</Parameters>
</ConsoleTask>
Then we need to set a couple of categories:
<Category ID="Category.DoubleClickEditAdminSetting"
Target="ConsoleTask.Microsoft.Demo.AdminSetting.Example.Edit" Value="Console!Microsoft.EnterpriseManagement.ServiceManager.UI.Console.DoubleClickTask" />
<Category ID="SCSMMPCategory"
Value="Console!Microsoft.EnterpriseManagement.ServiceManager.ManagementPack">
<ManagementPackName>Microsoft.Demo.AdminSettings</ManagementPackName>
<ManagementPackVersion>7.0.5244.0</ManagementPackVersion>
The first one tells Service Manager that when the singleton class object is selected in the Settings view that the doubleclick task is the one defined in this management pack.
The second one tells Service Manager that this is an MP intended to be used in Service Manager. This is necessary to make sure that the console task shows up in the Service Manager console.
Then of course we add the usaul Language Pack stuff. I won't go over that again, but remember there is more information on localizing management packs if you need it.
That's it for the management pack. Now let's take a look at the form – it's really easy. All I did was add a new WPF User Control to my project and then changed it to derive from wpfwiz:WizardRegularPageBase.
<wpfwiz:WizardRegularPageBase x:Class="Microsoft.Demo.AdminSettings.AdminSettingConfigurationPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Microsoft.Demo.AdminSettings"
xmlns:wpfwiz="clr-namespace:Microsoft.EnterpriseManagement.UI.WpfWizardFramework;assembly=Microsoft.EnterpriseManagement.UI.WpfWizardFramework" Loaded="WizardRegularPageBase_Loaded">
<Grid Name="ConfigurationGrid" Margin="15,25,15,25">
<ScrollViewer Margin="0,50,0,50" Name="scrollViewer" CanContentScroll="True" VerticalScrollBarVisibility="Auto">
<StackPanel Name="stackPanel" Orientation="Vertical">
<Label Height="25" Padding="0" Margin="0,0,0,0" Name="displayamelabel" Content="Property 1:"/>
<TextBox Height="25" Margin="0,-8,0,10" Name="displaynameTextBlock">
<TextBox.Text>
<Binding Path="Property1" Mode="TwoWay" FallbackValue=""/>
</TextBox.Text>
</TextBox>
<Label Height="25" Padding="0" Margin="0,0,0,0" Name="datafilepathLabel" Content="Property 2:"/>
<TextBox Height="25" Margin="0,-8,0,10" Name="domainTextBlock">
<Binding Path="Property2" Mode="TwoWay" FallbackValue=""/>
</StackPanel>
</ScrollViewer>
</Grid>
</wpfwiz:WizardRegularPageBase>
There is a very small amount of code behind that we need to put into the .cs file that is associated with this .xaml file. You'll see that this implementation is very similar to the task handler provided in the CSV Connector example. We are using the same concept of a "Wizard" in property page mode to display the UI to get/set the property values.
public partial class AdminSettingConfigurationPage : WizardRegularPageBase
{
private AdminSettingWizardData adminSettingWizardData = null;
public AdminSettingConfigurationPage(WizardData wizardData)
InitializeComponent();
this.DataContext = wizardData;
this.adminSettingWizardData = this.DataContext as AdminSettingWizardData;
}
private void WizardRegularPageBase_Loaded(object sender, RoutedEventArgs e)
This code simply binds the WizardData object to the form as part of the constructor. Now let's look at the task handler code and associated WizardData class. The only tricky/new part of this is highlighted below in the code comments.
namespace Microsoft.Demo.AdminSettings
public class AdminSettingsExample : ConsoleCommand
public AdminSettingsExample()
public override void ExecuteCommand(IList<NavigationModelNodeBase> nodes, NavigationModelNodeTask task, ICollection<string> parameters)
/*
This GUID is generated automatically when you import the Management Pack with the singleton admin setting class in it.
You can get this GUID by running a query like:
Select BaseManagedEntityID, FullName where FullName like '%<enter your class ID here>%'
where the GUID you want is returned in the BaseManagedEntityID column in the result set
*/
String strSingletonBaseManagedObjectID = "79893B9E-04AC-9D3A-51D8-B085BEE6EA78";
//Get the server name to connect to and connect to the server
String strServerName = Registry.GetValue("HKEY_CURRENT_USER\\Software\\Microsoft\\System Center\\2010\\Service Manager\\Console\\User Settings", "SDKServiceMachine", "localhost").ToString();
EnterpriseManagementGroup emg = new EnterpriseManagementGroup(strServerName);
//Get the Object using the GUID from above - since this is a singleton object we can get it by GUID
EnterpriseManagementObject emoAdminSetting = emg.EntityObjects.GetObject<EnterpriseManagementObject>(new Guid(strSingletonBaseManagedObjectID), ObjectQueryOptions.Default);
//Create a new "wizard" (also used for property dialogs as in this case), set the title bar, create the data, and add the pages
WizardStory wizard = new WizardStory();
wizard.WizardWindowTitle = "Edit Admin Setting";
WizardData data = new AdminSettingWizardData(emoAdminSetting);
wizard.WizardData = data;
wizard.AddLast(new WizardStep("Configuration",
typeof(AdminSettingConfigurationPage), wizard.WizardData));
//Show the property page
PropertySheetDialog wizardWindow = new PropertySheetDialog(wizard);
//Update the view when done so the new values are shown
bool? dialogResult = wizardWindow.ShowDialog();
if (dialogResult.HasValue && dialogResult.Value)
RequestViewRefresh();
class AdminSettingWizardData : WizardData
#region Variables
private String strProperty1 = String.Empty;
private String strProperty2 = String.Empty;
private Guid guidEnterpriseManagementObjectID = Guid.Empty;
public String Property1
get
return this.strProperty1;
set
if (this.strProperty1 != value)
this.strProperty1 = value;
public String Property2
return this.strProperty2;
if (this.strProperty2 != value)
this.strProperty2 = value;
public Guid EnterpriseManagementObjectID
return this.guidEnterpriseManagementObjectID;
if (this.guidEnterpriseManagementObjectID != value)
this.guidEnterpriseManagementObjectID = value;
#endregion
internal AdminSettingWizardData(EnterpriseManagementObject emoAdminSetting)
//Get the server name to connect to and connect
//Get the AdminSettings MP so you can get the Admin Setting class
ManagementPack mpAdminSetting = emg.GetManagementPack("Microsoft.Demo.AdminSettings", null, new Version("1.0.0.0"));
ManagementPackClass classAdminSetting = mpAdminSetting.GetClass("Microsoft.Demo.AdminSetting.Example");
this.Property1 = emoAdminSetting[classAdminSetting, "Property1"].ToString();
this.Property2 = emoAdminSetting[classAdminSetting, "Property2"].ToString();
this.EnterpriseManagementObjectID = emoAdminSetting.Id;
public override void AcceptChanges(WizardMode wizardMode)
//Get the Connector object using the object ID
EnterpriseManagementObject emoAdminSetting = emg.EntityObjects.GetObject<EnterpriseManagementObject>(this.EnterpriseManagementObjectID, ObjectQueryOptions.Default);
//Set the property values to the new values
emoAdminSetting[classAdminSetting, "Property1"].Value = this.Property1;
emoAdminSetting[classAdminSetting, "Property2"].Value = this.Property2;
//Update Connector instance
emoAdminSetting.Commit();
this.WizardResult = WizardResult.Success;
In order to deploy this solution you need to import the management pack .xml file and copy the AdminSettingsExample.dll file to the %ProgramFiles%\Microsoft System Center\Service Manager 2010 directoy on all computers that will be accessing this form. That .dll contains the task handler code and the XAML form.
Now you know how to:
The solution files are here:
http://cid-17faa48294add53f.skydrive.live.com/self.aspx/.Public/AdminSettingsExample.zip
Follow me on Twitter:
http://twitter.com/radtravis
Dear Travis,
I'm creating a wizard consisting of multiple custom forms which is feasible thanks to your above instruction. When navigating from one form to another within the wizard using built-in Next buttons of the wizard, I'm struggling to find the events when these buttons are clicked so that I can validate the data of previous forms to meet business rules
Please help with details on how I can capture the event when Next/Previous button is clicked
Thank you very much. Highly appreciate your help
Nga
@Nga -
I dont know that we have a event handler for the Next button click event. Instead we try to proactively validate the form as the user is filling it out. You can hook various other events on the form and perform a validation. When the form meets your business logic, then you can change .IsNextButtonEnabled = true.
Hope that helps!
Thanks Travis, we found ways around with LoadState and SaveState event for validation
Cheers,
Hi Travis,
I'm trying to do this using the 2010 Authoring Tool... I've been able to create the form but I'm missing the part where I have to create the dll (assembly).
Do I have to install Visual Studio and create a C# project to create the dll file?
I'm actually building an improved version of SLA Management... and I have everything working but this.
Thanks in advance!
German.
@German Minicucci
Hi German - sorry for the delayed response. I'm on week 4 of a 4 week world tour for SCSM now and it's been hard to keep up. It's probably not necessary to write cod to have a settings dialog, but it is an option. Please send me an email at scsmbeta [at] live [dot] com with some more details on what you are trying to do and I'll try to make a recommendation.
How can I call my wizard(same as yours) in the management pack?
But Great Post! Saved me a lot of time. Thanks.