Creating a Custom Administration Setting

Introduction


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?”


Starting with the End in Mind


This is what we are trying to achieve:



  • A row in the Settings view that the user can either select and click Properties for in the task pane or can double click on

  • When the user clicks Properties or double clicks a custom form pops up to capture the user input in the Service Manager database

  • The data is stored as properties of an object so that it is easy to access and store

This is what the user experience looks like:



When the user double-clicks or clicks Properties:



Implementation


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


Type=string


AutoIncrement=false
Key=false


CaseSensitive=false


MaxLength=256


MinLength=0


Required=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


Accessibility=Public


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”>


<TextBox.Text>


<Binding Path=”Property2″ Mode=”TwoWay” FallbackValue=””/>


</TextBox.Text>


</TextBox>


</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


{


get


{


return this.strProperty2;


}


set


{


if (this.strProperty2 != value)


{


this.strProperty2 = value;


}


}


}


public Guid EnterpriseManagementObjectID


{


get


{


return this.guidEnterpriseManagementObjectID;


}


set


{


if (this.guidEnterpriseManagementObjectID != value)


{


this.guidEnterpriseManagementObjectID = value;


}


}


}


#endregion


internal AdminSettingWizardData(EnterpriseManagementObject emoAdminSetting)


{


//Get the server name to connect to and connect


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 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 server name to connect to and connect


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 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”);


//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;


}


}


Deploying this Solution


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.


Conclusion


Now you know how to:



  • create your own singleton class,

  • host it in the Administration workspace Settings view,

  • add a double-click task handler,

  • use the wizard framework to display a custom form to the user,

  • create a simple custom form using XAML,

  • display the form to the user using a custom task handler

The solution files are here:


http://cid-17faa48294add53f.skydrive.live.com/self.aspx/.Public/AdminSettingsExample.zip


Follow me on Twitter:


https://twitter.com/radtravis