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"
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;
}
}
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:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.