Creating a Widget for Operations Manager Dashboard - Walkthrough #1 - Custom UI Control
Introduction
This walkthrough will show you how to build an Operations Manager 2012 (OM12) Widget that can be used in a dashboard created in the Operations Console. In this walk through you will learn the following:
- Select data to be retrieved from the OM12 server.
- Design and build a Silverlight/WPF User Control that will display the selected data.
- Integrate the User Control into a OM12 Management Pack that can be imported and display the data.
This a relatively long process with many steps in this process. The following is a brief outline to give you a feel for the work involved in each of the sections.
1. Select the Data
There are many queries available in OM12. These queries provide access to the data stored in the OM12 database. Each query has parameters that require input and data that it returns. When you have gathered the requirements for your widget, you need to review the queries available and select which set of queries will fit your needs.
2. Design and Build the Silverlight/WPF User Control
Once you have decided on the query that will be used and know the data that will be received, you can design your User Control in Silverlight. OM12 will deliver the data to your control but has no knowledge of, or interest in, what is done with the data. We will build a simple test application that will feed test data to the control to verify its operation. This test application will be useful both to test changes to the control and to help isolate problems when debugging.
3. Create a Management Pack to Display the User Control
The next step is to integrate the User Control into a Management Pack. At this point the User Control will be configured as a single component contained inside of a Dashboard component and will not be placed in a widget. This allows us to verify the successful integration of the User Control into OM12. Once the control is operating properly, we will take the additional steps to place it inside of a Widget component. We will not send data to the User Control at this point. We will however install the Dashboard component containing the User Control into OM12, and will see it displayed properly.
4. Bind OM12 Data to the User Control
All of the remaining steps involve additions and changes to the xml of the Management Pack. At this point the Dashboard component contains a single component: our UserControl. Now a new component is added to the dashboard: the query that we selected. When these two are bound, data will be passed to the control and results will be displayed.
5. Convert User Control Host from a Dashboard to a Widget
At this point we have a User Control that has been successfully integrated into OM12 as a Management Pack component. This component has been placed inside of a Dashboard component that appears at the root level of the Monitoring Tree. We now make changes to the Dashboard component so that it becomes a Widget. As a widget, the component will no longer appear in the Monitoring tree, but the user will instead be able to place it inside of other dashboards.
6. Add Configuration and Personalization Pages to the Widget
One of the important features of the widget is that it can be customized. A wizard is used to allow the user to make selections that affect the operation of the components in the widget. We will add both a configuration page and a personalization page that will be used by the wizard.
Prerequisites
This walkthrough requires the following components installed on your workstation:
- Visual Studio 2010
- Microsoft Silverlight 4 Tools for Visual Studio 2010
- Operations Manager Authoring Extensions for VS 2010
You can download a set of sample projects for this walkthrough from the TechNet Gallery at Creating a Widget for Operations Manager Dashboard Samples. A separate project is provided for each exercise in the walkthrough representing the intended configuration of the solution at the completion of that exercise. This assists the reader in comparing the results in their own project with the intended results. |
1 Select the Data to Retrieve
There are various data queries available in System Center 2012 Operations Manager (OM12) defined in the Microsoft.SystemCenter.Visualization.Library management pack. The Query that will be used in this example is GetAlertsForEntitiesQuery which returns all alerts in the management group. Detail on the specifics of this query is beyond the scope of this walkthrough. Only a brief explanation of the parameters passed and the data returned is required for this sample.
1.1 Parameters
Only one parameter of GetAlertsForEntitiesQuery will be used in this example. The AlertProperties parameter identifies the set of properties that will be returned for each alert in the result set. Its properties are defined in the AlertType schema type which is part of Microsoft.SystemCenter.Visualization.OperationalDataTypes in Microsoft.SystemCenter.Visualization.Library. In this example the properties Name, Severity, and Priority are requested.
1.2 Result Set
The result set returned is a collection of objects of the type IDataObject. Each element in the collection represents an alert. The property values can be accessed using:
instance[properyName].ToString()
2 Design and Build the Silverlight/WPF UserControl
Both a Silverlight and a WPF version of the AlertSummaryControl will be created in this solution. The Silverlight control, which is used by the web console, will be created first. The WPF version, which is used by the Operations Console, will then be created and will share the xaml and code behind from the Silverlight project.
2.1 Create Control to Display the Query Result Set
The properties we selected to be returned for each alert are Name, Severity, and Priority. Below is a screen shot of the control that will be created to display this data. It groups the alerts by the Severity property and displays all three properties for the group of alerts in the selected severity group. The severity group is represented by a colored circle that contains the count of alerts in the group. When the circle is clicked the data grid displays the set of alerts with that severity level.
2.1.1 Create a new Silverlight Class Library project
This procedure creates a new project and solution in Visual Studio to hold the controls and management pack.
- Start Visual Studio and select File | New | Project.
- Expand Visual C# and select Silverlight.
- In the right pane select Silverlight Class Library.
- Name the project AlertSummarySilverlight and click OK.
- Delete the Class1.cs file that was automatically added to the project.
- Change the namespace for the project:
- Right-click the project title in the Solution Explorer and select Properties.
- Change the Default namespace to AlertSummaryControl.
- Create the user control:
- Right-click the project title and select Add | New Item.
- Select Silverlight User Control.
- Name the project AlertControl.xaml and click Add.
2.1.2 Add a class to hold the Query Result Set
This procedure creates a class that is used to hold the result set that is received and then bound to elements in the control.
Right-click the AlertSummarySilverlight project and select Add | New Item.
With Visual C# selected, in the right pane select Class.
Name the class AlertData.cs and click Add.
The AlertData class must inherit from the INotifyPropertyChanged class.
Add the following directives:
1.using System.ComponentModel; 2.using System.Collections.Generic;
Change the declaration of the AlertData class as shown below:
1.public class AlertData : INotifyPropertyChanged
We need to create a class to represent each individual alert. This class will be called AlertItem and will have each of the alert properties that we require. Insert the follow code in the AlertSummaryControl namespace above the declaration of the AlertData class:
1.public class AlertItem 2. { 3. public string Id { get; set; } 4. public string Name { get; set; } 5. public string Severity { get; set; } 6. public string Priority { get; set; } 7. }
We then need to add a List of AlertItem to the AlertData class along with other properties that the AlertData class will require. Add the following code to the AlertData class:
01.private string _CriticalAlertCount; 02.private string _WarningAlertCount; 03. 04.private List<AlertItem> _CriticalAlerts; 05.private List<AlertItem> _WarningAlerts; 06. 07.public string CriticalAlertCount 08.{ 09. get { return _CriticalAlertCount; } 10. 11. set { 12. if (_CriticalAlertCount != value) { 13. _CriticalAlertCount = value; 14. OnPropertyChanged("CriticalAlertCount"); 15. } 16. } 17.} 18. 19.public string WarningAlertCount 20.{ 21. get { return _WarningAlertCount; } 22. 23. set { 24. if (_WarningAlertCount != value) { 25. _WarningAlertCount = value; 26. OnPropertyChanged("WarningAlertCount"); 27. } 28. } 29.} 30. 31.public List<AlertItem> CriticalAlerts 32.{ 33. get { 34. return this._CriticalAlerts; 35. } 36. 37. set { 38. if (_CriticalAlerts != value) { 39. _CriticalAlerts = value; 40. this.OnPropertyChanged("CriticalAlerts"); 41. } 42. } 43.} 44. 45.public List<AlertItem> WarningAlerts 46.{ 47. get {return this._WarningAlerts;} 48. 49. set { 50. if (_WarningAlerts != value) { 51. _WarningAlerts = value; 52. this.OnPropertyChanged("WarningAlerts"); 53. } 54. } 55.} 56. 57.public event PropertyChangedEventHandler PropertyChanged; 58. 59.protected void OnPropertyChanged(string propertyName) 60.{ 61. if (PropertyChanged != null) { 62. PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 63. } 64.}
2.1.3 Modify XAML of the UserControl to include the AlertItem class.
The XAML of the AlertControl user control can now be modified to create the user interface. You must first compile the project to complete the creation of the AlertData class.
Compile the solution by selecting Build | Build Solution.
In the AlertSummarySilverlight project, replace the contents of the AlertControl.xaml file with the following:
<UserControl x:Class="AlertSummaryControl.AlertControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:data="clr-namespace:AlertSummaryControl" HorizontalAlignment="Left" VerticalAlignment="Top" Background="White" d:DesignHeight="300" d:DesignWidth="600"> <UserControl.Resources> <data:AlertData x:Key="alertDataInstance"></data:AlertData> </UserControl.Resources> <StackPanel x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource alertDataInstance}}" Orientation="Vertical" Margin="20,20,20,20" Width="560"> <StackPanel x:Name="spButtons" Orientation="Horizontal"> <StackPanel Orientation="Vertical" Name="CriticalPanel"> <Grid Height="75" Width="75" Margin="0,0,10,0"> <Border Background="Red" CornerRadius="40"> <Button Content="{Binding Path=CriticalAlertCount}" Click="btCritical_Click" BorderThickness="0" Foreground="Black" BorderBrush="Transparent" FontSize="16" Width="49" Height="22" > <Button.Template> <ControlTemplate TargetType="Button"> <Border Background="Red"> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" /> </Border> </ControlTemplate> </Button.Template> </Button> </Border> </Grid> <TextBlock x:Name="tbCriticalLabel" FontWeight="Normal" HorizontalAlignment="Center" FontSize="12">Critical</TextBlock> </StackPanel> <StackPanel Orientation="Vertical" Name="WarningPanel"> <Grid Height="75" Width="75"> <Border Background="Yellow" CornerRadius="40"> <Button Content="{Binding Path=WarningAlertCount}" Click="btWarning_Click" BorderThickness="0" Foreground="Black" BorderBrush="Transparent" FontSize="16" Width="58" Height="22" > <Button.Template> <ControlTemplate TargetType="Button"> <Border Background="Yellow"> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" /> </Border> </ControlTemplate> </Button.Template> </Button> </Border> </Grid> <TextBlock x:Name="tbWarningLabel" HorizontalAlignment="Center" FontWeight="Normal" FontSize="12">Warning</TextBlock> </StackPanel> </StackPanel> <TextBlock x:Name="tbMessage" Height="20"></TextBlock> <Canvas x:Name="cvGrids"> </Canvas> </StackPanel> </UserControl>
Notable lines in this code include:
- Line 7: xmlns:data="clr-namespace:AlertSummaryControl" attaches the prefix data to the namespace of the control, AlertSummaryControl.
- Line 14: <data:AlertData x:Key="alertDataInstance"> defines a static variable of the type AlertData which is defined in the AlertSummaryControl namespace. Identifying this variable in the xaml allows data binding to be declared in the xaml.
- Line 18: DataContext="{Binding Source={StaticResource alertDataInstance}}" sets the DataContent for the LayoutRoot control to the static variable defined on line 11.
- Line 26 & 51: Content="{Binding Path=CriticalAlertCount}" binds the Content of the button to the CriticalAlertCount property in the static variable defined on line 11.
2.1.4 Add Code Behind for the AlertControl class
We now need to include the code behind the AlertControl user control to set properties on the controls and bind to our dataset.
Add reference to System.ComponentModel.Composition and System.Windows.Controls.Data assemblies.
- Right-click References in the Solution Explorer and select Add Reference.
- In the .NET tab, locate and select the assembly.
- Click OK.
Expand AlertControl.xaml.
Double-click AlertControl.xaml.cs to open it.
Add the following directives:
Add the following attributes above the AlertControl class declaration:
[Export] [PartCreationPolicy(CreationPolicy.NonShared)]
The AlertControl class must inherit from INotifyPropertyChanged. Change the declaration of the AlertControl to the following:
public partial class AlertControl : UserControl, INotifyPropertyChanged
Replace the existing contents of the AlertControl class with the following:
public AlertData alertData; private DataGrid criticalGrid; private DataGrid warningGrid; public AlertControl() { InitializeComponent(); this.alertData = (AlertData)this.Resources["alertDataInstance"]; criticalGrid = new DataGrid(); criticalGrid.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; criticalGrid.IsReadOnly = true; criticalGrid.AutoGenerateColumns = true; criticalGrid.Height = 200.0; criticalGrid.Visibility = System.Windows.Visibility.Visible; criticalGrid.ItemsSource = this.alertData.CriticalAlerts; Binding criticalBinding = new Binding("CriticalAlerts"); criticalGrid.SetBinding(DataGrid.ItemsSourceProperty, criticalBinding); cvGrids.Children.Add(criticalGrid); warningGrid = new DataGrid(); warningGrid.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; warningGrid.IsReadOnly = true; warningGrid.AutoGenerateColumns = true; warningGrid.Height = 200.0; warningGrid.Visibility = System.Windows.Visibility.Collapsed; warningGrid.ItemsSource = this.alertData.WarningAlerts; Binding warningBinding = new Binding("WarningAlerts"); warningGrid.SetBinding(DataGrid.ItemsSourceProperty, warningBinding); cvGrids.Children.Add(warningGrid); this.tbCriticalLabel.FontWeight = FontWeights.Bold; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private void btCritical_Click(object sender, RoutedEventArgs e) { this.criticalGrid.Visibility = System.Windows.Visibility.Visible; this.warningGrid.Visibility = System.Windows.Visibility.Collapsed; this.tbCriticalLabel.FontWeight = FontWeights.Bold; this.tbWarningLabel.FontWeight = FontWeights.Normal; this.criticalGrid.Focus(); } private void btWarning_Click(object sender, RoutedEventArgs e) { this.warningGrid.Visibility = System.Windows.Visibility.Visible; this.criticalGrid.Visibility = System.Windows.Visibility.Collapsed; this.tbWarningLabel.FontWeight = FontWeights.Bold; this.tbCriticalLabel.FontWeight = FontWeights.Normal; this.warningGrid.Focus(); }
Notable lines in this code include:
- Line 9: this.alertData = (AlertData)this.Resources["alertDataInstance"] ties the local variable alertData in the .cs files to the static instance defined in the xaml file on line 11.
- Lines 11 – 31: create instances of the dataGrids used to display the Alert data. Only one dataGrid will be visible at a time.
- Lines 37 – 45: provide the PropertyChangedEventHandler, which is required because the class inherits from INotifyPropertyChanged.
- Lines 47 – 63: provide the code that handles the click event on the two buttons. The label below the button is bolded and the appropriate dataGrid is made visible.
The AlertControl user control will produce the following UI control.
This UI contains two buttons, each inside of a colored circle. The Content of each of these buttons is bound to a data item that contains the count of Alerts found for the related severity level.
Below the buttons is a Canvas, named cvGrids, which will be used to contain the data grids. The data grids are not defined in the xaml because Silverlight and WPF use different dataGrid controls, and they are designated differently in the xaml. The data grids are created in the code behind.
2.2 Build a test application to test the User Control
With the Silverlight control built that receives and displays Alert data, we will now build a simple test application that will host the control and provide test data. In the next section, we will build project that allows the Silverlight control to be hosted in the Management Console and Web Console and receive real data.
2.2.1 Create a Silverlight Application project
- Right-click the solution and select Add | New Project.
- Expand Visual C# and select Silverlight.
- In the right pane select Silverlight Application.
- Name the project SilverlightApp and click OK.
- In the New Silverlight Application dialog box, uncheck Host the Silverlight application in a new Web site and click OK.
- Add a reference to the Silverlight Class Library project that we just created.
- In the SilverlightApp project, right-click References and select Add Reference.
- Select the Projects tab.
- Select AlertSummarySilverlight and click OK.
- Place an instance of the AlertControl on the MainPage of the application.
Expand MainPage.xaml.
Double-click MainPage.xaml.cs to open it.
In the MainPage() method, after InitializeComponent(), insert the following code:
AlertSummaryControl.AlertControl alertControl = new AlertSummaryControl.AlertControl(); LayoutRoot.Children.Add(alertControl);
2.2.2 Test the Silverlight Control
You can now compile and test the Silverlight control to verify that the previous steps were performed correctly.
- Right-click the solution and select Properties.
- Change Single startup project to SilverlightApp and click OK.
- Select Debug | Start Debugging.
Internet Explorer should open with the Silverlight control displayed. It will show the two round buttons but will not have any content in the button or in the datagrid below.
2.2.3 Create Test Application to Pass Dummy Data to the Silverlight Control
The next step is to populate the control with dummy data to further validate its operations.
- Place a Test button on the page that will be used to fire the code that generates test data.
In the SilverlightApp project, open MainPage.xaml.
Replace the <Grid> element with the following code:
<Canvas x:Name="LayoutRoot"> <Button Content="Test" Name="button1" Height="23" Width="75" HorizontalAlignment="Left" VerticalAlignment="Top" Click="button1_Click" /> </Canvas>
- Place a Test button on the page that will be used to fire the code that generates test data.
In MainPage.xaml.cs, replace the code for the MainPage class with the following:
public partial class MainPage : UserControl { private AlertSummaryControl.AlertControl alertControl; public MainPage() { InitializeComponent(); alertControl = new AlertSummaryControl.AlertControl(); LayoutRoot.Children.Add(alertControl); } private void button1_Click(object sender, RoutedEventArgs e) { List<AlertSummaryControl.AlertItem> criticalAlerts = new List<AlertSummaryControl.AlertItem>(); for (int i = 1; i <= 5; i++) { AlertSummaryControl.AlertItem criticalAlert = new AlertSummaryControl.AlertItem(); criticalAlert.Id = "Id #" + i.ToString(); criticalAlert.Name = "Name"; criticalAlert.Priority = "Priority"; criticalAlert.Severity = "Critical"; criticalAlerts.Add(criticalAlert); } alertControl.alertData.CriticalAlerts = criticalAlerts; alertControl.alertData.CriticalAlertCount = criticalAlerts.Count.ToString(); List<AlertSummaryControl.AlertItem> warningAlerts = new List<AlertSummaryControl.AlertItem>(); for (int i = 1; i <= 10; i++) { AlertSummaryControl.AlertItem warningAlert = new AlertSummaryControl.AlertItem(); warningAlert.Id = "Id" + i.ToString(); warningAlert.Name = "Name"; warningAlert.Priority = "Priority"; warningAlert.Severity = "Warning"; warningAlerts.Add(warningAlert); } alertControl.alertData.WarningAlerts = warningAlerts; alertControl.alertData.WarningAlertCount = warningAlerts.Count.ToString(); } }
Notable lines in this code include:
- Lines 9-10: Creates an instance of the AlertSummaryControl control and places it in the root control on the page.
- Lines 15-26 & 33-41: Generates a List<AlertItem> using sample data.
- Lines 27-28 & 42-43: Assigns the generated data to the public properties exposed in the AlertSummaryHost Widget control. These properties are defined in AlertData.cs . These assignments will trigger the OnPropertyChanged event which will cause the UI elements to be updated.
- Test the application.
2.3 Create a WPF User Control linked to the Silverlight XAML
The WPF version of the AlertSummaryControl control is required for the Operations Console and will share the xaml and code behind from both the Silverlight Control project and the Silverlight Application Project.
2.3.1 Create a new WPF User Control Library project.
- Right-click the solution and select Add | New Project.
- Expand Visual C# and select Windows.
- In the right pane select WPF User Control Library.
- Name the project AlertSummaryWPF and click OK.
- Delete the UserControl1.xaml file that was added to the project automatically.
- Right-click the project title in the Solution Explorer and select Properties.
- Change the Default namespace to AlertSummaryControl.
- Change the namespace for the project.
- Add a link to the control in the Silverlight project:
- Right-click the project title and select Add | Existing Item.
- Change the file type dropdown to XAML Files (*.xaml;*.xoml).
- Navigate to the AlertControl.xaml file in the AlertSummarySilverlight project.
- Click the dropdown on the Add button and select Add as Link.
- Add a link to the AlertData class in the Silverlight project:
- Right-click the project title and select Add | Existing Item.
- Change the file type dropdown to Visual C# Files.
- Navigate to the AlertData.cs file in the AlertSummarySilverlight project.
- Click the dropdown on the Add button and select Add as Link.
- Add reference to System.ComponentModel.Composition:
- Right-click References in the Solution Explorer and select Add Reference.
- In the .NET tab, locate and select the assembly.
- Click OK.
2.3.2 Create a new WPF Application project
- Right-click the solution and select Add | New Project.
- Expand Visual C# and select Windows.
- In the right pane select WPF Application.
- Name the project WPFApp and click OK.
- Delete the UserControl1.xaml file that was added to the project automatically.
- Change the namespace for the project so that it can easily link to the page in the Silverlight application:
- Open App.xaml.cs.
- Change the namespace WPFApp to SilverlightApp.
- Hover over SilverlightApp and click the icon that appears.
- Select the option Rename WPFApp to SilverlightApp. This will rename the namespace throughout the project.
- Add a link to MainPage in the Silverlight project:
- Right-click the project title and select Add | Existing Item.
- Change the file type dropdown to XAML Files.
- Navigate to the MainPage.xaml file in the SilverlightApp project.
- Click the dropdown on the Add button and select Add as Link.
- Open the App.xaml file.
- Change the StartupUri from MainWindow.xaml to MainPage.xaml.
- Add a reference to the WPF user control library project.
- In the SilverlightApp project, right-click References and select Add Reference.
- Select the Projects tab.
- Select AlertSummaryWPF and click OK.
- Test the application.
- Right-click the solution and select Properties.
- Change Single startup project to SilverlightApp and click OK.
- Select Debug | Start Debugging. The window will open.
- Click the Test button.
- Verify that the output is similar to the test with SilverlightApp.
3 Create a Management Pack to Display the UserControl
In the same way that the test applications in the previous section provided a host environment and data for the AlertSummaryControl, a Management Pack provides the means for the AlertSummaryControl to be hosted in the Management Console (WPF control) and Web Console (Silverlight control). Each console will instantiate the appropriate version of the control and feed it data via exposed properties.
3.2 Create the Management Pack project
We start by creating and configuring the management pack project:
- Create the management pack project:
- Right click on the solution and select Add | New Project.
- Expand Management Pack and select Operations Manager.
- In the right pane, select Operations Manager 2012 Add-on Management Pack.
- Name the project AlertSummaryPack and click OK.
- Configure the management pack to be sealed:
- Obtain a key file using the procedure in How to Seal a Management Pack File.
- Right click on the AlertSummaryPack project title and select Properties.
- Select the Build tab.
- Select checkbox titled “Generate sealed and signed management pack”
- Specify the path to the Key File.
- Add a Management Pack fragment:
- Right click on AlertSummaryPack project in Solution Explorer and select Add | New Item.
- Select Empty Management Pack Fragment.
- Name the fragment AlertSummary.mpx and click Add.
3.3 Add the UserControl assemblies to the Management Pack
The ManagementPack project is used to deliver the WPF/Silverlight controls to Operations Manager. When the project is successfully compiled the result is an .mpb file, which can be directly imported into the Operations Manager console. The mpb file contains one or more assemblies, one for each version of the controls in addition to the Management Pack xml file. The management pack xml defines the controls so that the assemblies can be integrated into the console.
The assembly file for each control is identified in the <Resources> element of the management pack file. This element brings the binaries for the controls into the Management Pack.
3.3.1 Add the assemblies
In AlertSummary.mpx add the following xml segment as a child of the root element ManagementPackFragment:
01.<Resources> 02. <Assembly 03. ID="SilverlightAssembly" 04. QualifiedName="AlertSummarySilverlight, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 05. FileName="AlertSummarySilverlight.dll" 06. Accessibility="Public" 07. HasNullStream="false" /> 08. <Assembly 09. ID="WPFAssembly" 10. QualifiedName="AlertSummaryWPF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 11. FileName="AlertSummaryWPF.dll" 12. Accessibility="Public" 13. HasNullStream="false" /> 14.</Resources>
Notable lines in this code include:
- Lines 3 & 9: provide unique identifiers for each of the <Assembly> elements. These IDs will be used to tie the assemblies to other elements in the management pack.
- Lines 4 & 10 provide the name of each assembly. This value can be taken from the Properties page for the control’s project. It is titled Assembly Name.
- Lines 5 and 11 provide the name of the file for the assemblies. This is typically the Assembly Name followed by .dll.
3.4 Identify the Rendering Type for Each Assembly
To make sure that the right assembly is distributed to the right console, we have to tag each assembly with a category. This tells OM12 where to send this assembly; if it’s tagged as WPF, it will be sent to all Window Consoles. If it’s tagged as Silverlight, it will be sent to all Web Console servers.
In AlertSummary.mpx add the following xml segment as a child of the root element ManagementPackFragment, placed before the <Resources> element.
01.<Categories> 02. <Category 03. ID="SilverlightCategory" 04. Target="SilverlightAssembly" 05. Value="Visualization!Microsoft.SystemCenter.Visualization.SilverlightComponentAssembly" /> 06. <Category 07. ID="WPFCategory" 08. Target="WPFAssembly" 09. Value="Visualization!Microsoft.SystemCenter.Visualization.WpfComponentAssembly" /> 10.</Categories>
Note: You may need to add a reference to Microsoft.SystemCenter.Visualization.Library with the alias Visualization to your management pack project.
3.5 Declare User Controls as Unit Components
Now that the assemblies are part of our Management Pack, we can expose the UI Controls contained in them to the OM12 system so that they can be rendered and bound to data. It is important to understand the concept of components in OM12. In the Management Pack, components are defined under the Presentation/ComponentTypes and Presentation/ComponentImplementations nodes.
- A component can be a base element that has a concrete implementation in the UI, such as a label or a textbox. These simple UI elements are provided in a System Center library.
- A component can also be code based. In this case, the component is a distinct unit that contains multiple UI controls, such as a WPF or a Silverlight user control.
- A component can be a composite of other components.
3.5.1 Component Type
The Component Type can be considered the declaration of the interface of a component . The Component Type contains the following attributes:
- ID: unique identification that is used in the ManagementPack to associate other elements with the Component Type.
- Accessibility: Public or Internal. Specifies where the Component Type can be used by other management packs.
The ComponentType also contains child nodes to define Properties. The Property node defines the data expected by the component. Values are bound to properties when the component is used.
A Property has the following attributes:
- Name: The identifier of the property. This must be unique within the context of the component type
- Type – The data type of the property.
- BindingDirection – This is an enumeration (In, Out, Both) that defines the direction of the property binding. This controls whether the property supports get, set, or both.
Note that a Property node is not added to the ComponentType node for the AlertSummaryControl component at this point in the sample. A Property will be added in a later section.
In AlertSummary.mpx add the following xml segment as a child of the root element ManagementPackFragment, placed before the <Resources> and after the <Categories> element.
1.<Presentation> 2. <ComponentTypes> 3. <ComponentType ID="AlertSummaryControl" Accessibility="Public" /> 4. <ComponentType ID="AlertSummaryHost" Accessibility="Public" /> 5. </ComponentTypes> 6.</Presentation>
Note that there are two components being defined that should not be confused: AlertSummaryControl and AlertSummaryHost. AlertSummaryControl is an OM12 component that contains the Silverlight/WPF UserControl . AlertSummaryHost is an OM12 component that contains the AlertSummaryControl component. AlertSummaryHost will be initially configured as a Dashboard component that will appear in the Monitoring Tree root. Later in this sample it will be reconfigured as a Widget which will allow the user to decide where it will appear.
3.5.2 Component Implementation
A Component Implementation refers to a defined Component Type and defines the implementation of that Component Type.
The attributes for the component implementation element are:
- ID: A unique identifier that is used to refer to this component implementation.
- TypeID: The implementation is related to the ComponentType with this ID.
A Component Implementation can be either:
- Unit – code based implementation (WPF/Silverlight).
- Composite – composed of other component types..
3.5.2.1 Component Implementation for AlertSummaryControl
In AlertSummary.mpx add the following xml segment as a child of the <Presentation> element, placed after the <ComponentTypes> element:
01.<ComponentImplementations> 02. <ComponentImplementation 03. TypeId="AlertSummaryControl" 04. ID="AlertSummaryControlImplementation" 05. Accessibility="Public" 06. Platform="All"> 07. <Unit> 08. <ContractFactory>AlertSummaryControl.AlertControl</ContractFactory> 09. </Unit> 10. </ComponentImplementation> 11.</ComponentImplementations>
Notable lines in this code include:
- Line 3: identifies AlertSummaryControl as the ComponentType associated with this implementation.
- Line 6: indicates that this implementation covers both WPF and Silverlight.
- Line 7: indicates that this is a code based implementation, or a Unit.
- Line 8: The ContractFactory references an object found in an assembly that has been registered. In this sample, the assemblies are registered in the <Resources> element. The object is identified using [namespace].[object]
3.5.2.2 Component Implementation for Alert Summary Control
A Composite component is going to be used to place the AlertSummaryControl component directly into the Monitoring Tree. When a Composite component is put into the Monitoring Tree, it is simply a Dashboard. This will allow us to test the AlertSummaryControl before doing all the remaining steps to convert this component into a true widget.
In AlertSummary.mpx add the following xml segment as a child of the <ComponentImplementations> element, placed before the element for AlertSummaryControl implementation:
01.<ComponentImplementation 02. TypeId="AlertSummaryHost" 03. ID="AlertSummaryImplementation" 04. Accessibility="Public" 05. Platform="All"> 06. <Composite> 07. <Component TypeId="Visualization!Microsoft.SystemCenter.Visualization.ComponentContainer"> 08. <Binding PropertyId="Visual"> 09. <Component TypeId="AlertSummaryControl"> 10. </Component> 11. </Binding> 12. </Component> 13. </Composite> 14.</ComponentImplementation>
Notable lines in this code include:
- Line 2: identifies AlertSummaryHost as the ComponentType associated with this implementation.
- Line 5: indicates that this implementation covers both WPF and Silverlight.
- Line 6: designates that this components is a composition of multiple components. At this point the composition only contains a single component, AlertSummaryControl, but in subsequent steps an additional component will be added that will provide data.
- Lines 7 – 13: define a visual component that will appear in this composite component. This is the AlertSummaryControl.
Insert the following code below the <ComponentTypes> node. This inserts the dashboard into the root folder of the Operations Console:
1.<ComponentReferences> 2. <ComponentReference 3. ID="AlertSummaryReference" 4. Accessibility="Public" 5. TypeID="AlertSummaryHost" 6. Parent="SC!Microsoft.SystemCenter.Monitoring.ViewFolder.Root" /> 7.</ComponentReferences>
3.6 Configure the Management Pack Project
The Management Pack now defines the AlertSummaryControl component for both WPF and Silverlight. It can now be installed and the control will appear, however there will be no data.
- Set the AlertSummaryPack project as the Startup project for the solution:
- Right-click the AlertSummaryPack project in Solution Explorer and select Set as Startup Project.
- Specify the management group to install the management pack into:
- Right-click the AlertSummaryPack project in Solution Explorer and select Properties.
- Select the Management Group tab.
- Click Add.
- Enter a server name or IP address of a management server in the management group.
- If the management group requires different credentials, then select Log in as a different user and specify the credentials.
- Click Add Server.
- Specify the deployment details:
- Select the Deployment tab.
- Under Start Action, select Deploy and start Windows console.
Note: You also need to include the two assemblies (WPF and Silverlight .dll files) in the project. You can do that by just dragging the files from Explorer to the management pack project in Visual Studio and set the Build Action to "Embedded Resource". Alternatively, if you have the WPF and Silverlight control projects in the same solution as your management pack project, you can also add those projects as references in the management pack project and set Package to Bundle to True in the reference properties.
3.7 Install the Management Pack
To install the AlertWidgetSummary management pack from Visual Studio, press F5 or select Debug | Start debugging. The management pack will be installed and the component will appear in the Monitoring Tree. It will have the title AlertSummaryPack!AlertSummaryHost. Click on the title to see the component displayed on the right. It should appear as the image below.
4 Bind OM12 Data to the User Control
In this section, we will bind actual alert data to the AlertSummaryControl component as opposed to using test data that we used in the previous section.
The query used in this example is titled GetAlertsForEntitiesQuery which is defined in Microsoft.SystemCenter.Visualization.Library. Data is returned from this query using the following process:
- You define a variable in your management pack to receive the result set.
- At startup the variable is populated with the result set. No additional updates are made to the variable.
- When there is new data, the existing result set is updated and a NotifyCollectionChangedEventHandler event is generated.
To subscribe to this query the following approach must be taken:
- In the management pack, the Items property of the AlertSummaryControl component is bound to a variable you declare.
- When the update to the variable occurs, the AlertSummaryControl code stores the result set and adds a handler for the NotifyCollectionChangedEventHandler event.
- The NotifyCollectionChangedEventHandler handler updates the local data structure, which will trigger the update of the UI controls in the AlertSummaryControl component.
4.1 Add a Property to AlertData class to Receive SCOM Data
A main data type in SCOM is IDataObject. In this sample we will be using a query titled GetAlertsForEntitiesQuery, which returns an IEnumerable collection of IDataObject.
Expand the AlertSummarySilverlight project.
Add a reference to Microsoft.EnterpriseManagement.CompositionEngine and Microsoft.EnterpriseManagement.Presentation.Core assemblies. These can typically be found in the Console folder of the Operations Manager installation. Attention: the assemblies from the RTM/CU1 installation will not work! You need to download the sample project files and use the included assemblies.
- Right-click References in the Solution Explorer and select Add Reference.
- Select the Browse tab.
- Navigate to the assembly and then click OK.
Add the same references to the AlertSummaryWPF project.
Add the following directives to AlertData.cs:
using Microsoft.EnterpriseManagement.CompositionEngine; using Presentation = Microsoft.EnterpriseManagement.Presentation.DataAccess;
Insert the following code into the AlertData class in AlertData.cs:
private IEnumerable<Presentation.IDataObject> _items; public IEnumerable<Presentation.IDataObject> Items { get { return this._items; } set { if (value != null) { _items = value; this.OnPropertyChanged("Items"); } } }
Notable lines in this code include:
- Line 1: creates a local data structure that will be used to receive data from OM12.
- Lines 3-18: makes Items a public property. This property will be used in the management pack to bind data to the AlertSummaryControl component.
4.2 Add Methods to AlertControl to Process OM12 Data
The AlertControl will have a public property titled Items that will be referenced in the management pack.
Add the following directives to AlertControl.xaml.cs:
using Microsoft.EnterpriseManagement.CompositionEngine; using Presentation = Microsoft.EnterpriseManagement.Presentation.DataAccess; using Microsoft.EnterpriseManagement.Presentation.DataAccess;
Add the following code to AlertControl class in AlertSummaryControl.xaml.cs:
public IEnumerable<Presentation.IDataObject> Items { get { return this.alertData.Items; } set { if (value != null) { this.alertData.Items = value; Presentation.DynamicObservableDictionaryDataObjectCollection collection = value as Presentation.DynamicObservableDictionaryDataObjectCollection; if (collection != null) { collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged); } } } } public delegate void ProcessAlerts_Delegate(); void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { this.Dispatcher.BeginInvoke(new ProcessAlerts_Delegate(ProcessAlerts), null); } private void ProcessAlerts() { List<AlertItem> criticalAlerts = new List<AlertItem>(); List<AlertItem> warningAlerts = new List<AlertItem>(); lock (this.alertData.Items) { foreach (var alert in this.alertData.Items.ToArray()) { try { DynamicObservableDictionaryDataObject dataObject = (DynamicObservableDictionaryDataObject)alert; AlertItem newItem = new AlertItem() { Id = dataObject["Id"].ToString(), Name = dataObject["Name"].ToString(), Severity = dataObject["Severity"].ToString(), Priority = dataObject["Priority"].ToString() }; switch (dataObject["Severity"].ToString()) { case "Critical": criticalAlerts.Add(newItem); break; case "Warning": warningAlerts.Add(newItem); break; } } catch (Exception ex) { } } } this.alertData.CriticalAlertCount = criticalAlerts.Count.ToString(); this.alertData.CriticalAlerts = criticalAlerts; this.alertData.WarningAlertCount = warningAlerts.Count.ToString(); this.alertData.WarningAlerts = warningAlerts; }
Notable lines in this code include:
- Line 2: declares Items as a public property. It is a collection of IDataObject.
- Lines 9-23 are the Set code. It stores the address of the result set locally and adds an event handler for the NotifyCollectionChangedEventHandler. This event will be fired when there are changes to the result set.
- Lines 26 – 32 make use of the Dispatcher to allow the event handler to process the result set changes on the main thread. These events will be received on a background thread so the UI updates that will occur as a result of the result set changes so only take place in the main thread.
- Lines 34 – 78 update the local data stored in the component when notification is received that the query result set has changed. UI controls in this component are bound to the local data, and as a result, will be updated when the local data changes.
4.3 Add AlertSummaryControl’s Public Property to its ComponentType
The AlertSummaryControl now has a public property titled Items that can receive the query result set. This needs to be added to the Component Type and Implementation in the management pack. It must also be identified in the component placed in the AlertSummaryHost composite component. We will use a new variable to bind the public property and will define this variable in a later step.
In AlertSummary.mpx find the Presentation.ComponentTypes.ComponentType element for the AlertSummaryControl and add the following node:
<ComponentTypes> <ComponentType ID="AlertSummaryControl" Accessibility="Public"> <Property Name="Items" Type="xsd://Microsoft.SystemCenter.Visualization.Library!Microsoft.SystemCenter.Visualization.DataProvider/BaseManagedEntity[]" BindingDirection="In"/> </ComponentType>
In AlertSummary.mpx, find the Presentation.ComponentTypes.ComponentImplementation element for the AlertSummaryControl and add the following node directly after the <ContractFactory> node:
<Property Name="Items" Direction="In"> <Reference>$Property/Items$</Reference> </Property>
Add the following code in AlertSummary.mpx inside of <ComponentImplementation> for AlertSummaryHost inside of the <Component TypeId="AlertSummaryControl"> node:
<Binding PropertyId="Items"> <Reference>$Variable/MyVar$</Reference> </Binding>
4.4 Add Query to the Management Pack
The composite component current defined in the management pack, with ID="AlertSummaryImplementation", only contains an instance of the AlertSummaryControl. An additional component needs to be added that will contain the Query. In addition, a variable must be added that will be used both to receive the result set from the Query and be bound to the public property on the AlertSummaryHost Widget.
In AlertSummary.mpx, find the ComponentImplementation element for the AlertSummaryHost and add the following nodes under the Composite node:
<Variable Id="MyVar" Type="xsd://Microsoft.SystemCenter.Visualization.Library!Microsoft.SystemCenter.Visualization.DataProvider/BaseManagedEntity[]" />
Add the following under the Composite.Component node:
<Binding PropertyId="Objects"> <Component TypeId="Visualization!GetAlertsForEntitiesQuery"> <Binding PropertyId="Output"> <Reference>$Variable/MyVar$</Reference> </Binding> <Binding PropertyId="AlertProperties"> <ComplexValueCollection Type="xsd://Microsoft.SystemCenter.Visualization.Library!Microsoft.SystemCenter.Visualization.DataSourceTypes/ValueDefinition[]"> <ComplexValue Type="xsd://Microsoft.SystemCenter.Visualization.Library!Microsoft.SystemCenter.Visualization.DataSourceTypes/ValueDefinition"> <Binding PropertyId="OutputPropertyName"> <SimpleValue Type="xsd://string" Value="Name"/> </Binding> <Binding PropertyId="XPath"> <SimpleValue Type="xsd://string" Value="$Object/Property[Name='Name']$"/> </Binding> <Binding PropertyId="DisplayName"> <SimpleValue Type="xsd://string" Value="$NONE$"/> </Binding> </ComplexValue> <ComplexValue Type="xsd://Microsoft.SystemCenter.Visualization.Library!Microsoft.SystemCenter.Visualization.DataSourceTypes/ValueDefinition"> <Binding PropertyId="OutputPropertyName"> <SimpleValue Type="xsd://string" Value="Severity"/> </Binding> <Binding PropertyId="XPath"> <SimpleValue Type="xsd://string" Value="$Object/Property[Name='Severity']$"/> </Binding> <Binding PropertyId="DisplayName"> <SimpleValue Type="xsd://string" Value="$NONE$"/> </Binding> </ComplexValue> <ComplexValue Type="xsd://Microsoft.SystemCenter.Visualization.Library!Microsoft.SystemCenter.Visualization.DataSourceTypes/ValueDefinition"> <Binding PropertyId="OutputPropertyName"> <SimpleValue Type="xsd://string" Value="Priority"/> </Binding> <Binding PropertyId="XPath"> <SimpleValue Type="xsd://string" Value="$Object/Property[Name='Priority']$"/> </Binding> <Binding PropertyId="DisplayName"> <SimpleValue Type="xsd://string" Value="$NONE$"/> </Binding> </ComplexValue> </ComplexValueCollection> </Binding> </Component> </Binding>
Notable lines in this code include:
- Line 4: specifies that the result set from the query should be placed in the MyVar variable.
- Line 6: starts the node that provides the AlertProperties parameter for the query.
- Line 7: identifies that parameter as a ComplexValueCollection of the type ValueDefinition[].
- Lines 10 – 48 provide three values for the collection: Name, Severity, and Priority. These are the data items requested for the data set.
4.5 Add Internal Components to the Management Pack
Several Component Types and Component Implementations are required by GetAlertsForEntitiesQuery. Since they are marked as Internal in the Microsoft.SystemCenter.Visualization.Configuration.Library management pack, they can not be used by other management packs. For our management pack to use them, we need to copy the XML for their definition.
- For each of the following Component Types, copy their entire definition into AlertSummary.mpx in the <ComponentType> section.
- ChunkingAlertDetailsQuery
- ChunkingQuery
- CommonChunkingDetailsQuery
- ComponentCollection
- CommonDetailQueryInterface
- CommonQueryInterface
- GetAlertDetails
- GetAlertsForEntitiesDataSource
- GetAlertsForEntitiesQuery
- For each of the following Component References, copy their entire definition into AlertSummary.mpx in the <ComponentReference> section.
- ChunkingAlertDetailsQueryImplementation
- ChunkingQueryImplementation
- ComponentCollectionImplementation
- GetAlertsForEntitiesDataSourceImplementation
- GetAlertsForEntitiesQueryImplementation
4.6 Install and Test Management Pack
To install the AlertWidgetSummary management pack from Visual Studio, press F5 or select Debug | Start debugging. The management pack will be installed and the component will appear in the Monitoring Tree. It will have the title AlertSummaryPack!AlertSummaryHost. Click on the title to see the component displayed on the right. It should appear similar the image below with live alert data from your management group.
Open the Web Console to view the Silverlight control:
5 Convert UserControl Host from a Dashboard to a Widget
At this point our UserControl has been successfully integrated into the OM12 as a component in a Management Pack. It is contained in a composite component that is configured as a Dashboard. This composite component appears in the root of the Monitoring tree. We will now reconfigure this composite component so that it performs as a Widget.
Note that whether the AlertSummaryControl component appears in OM12 as a dashboard or as a widget does not impact the configuration of AlertSummaryControl component itself. What is affected is the AlertSummaryHost composite component that hosts the AlertSummaryControl component.
5.1 Create a folder to hold the widget
When the user goes to select a widget, the AlertSummaryHost composite component that we are about to reconfigure will appear among the other widgets that have been installed on the server. To make our AlertSummaryHost widget easy to find we will create a new folder and the reconfigure the control to be contained in it.
In AlertSummary.mpx add the following xml segment as a child of the <Presentation> node:
<Folders> <Folder ID="WalkthroughsFolder" Accessibility="Public" ParentFolder="Config!Microsoft.SystemCenter.Visualization.WidgetViewTemplateRoot"/> </Folders>
Notable lines in this code include:
- Line 3: sets the identity of the folder to WalkthroughsFolder. The folder will be referenced in the Management Pack by this ID.
- Line 5: identifies where this folder will appear. The WidgetViewTemplate folder is used in the Widget Selection Wizard.
In AlertSummary.mpx change the Parent value for the ComponentReference with the TypeID AlertSummaryHost from SC!Microsoft.SystemCenter.Monitoring.ViewFolder.Root to WalkthroughsFolder.
Add display strings for the folder and widget by adding the following xml to AlertSummary.mpx after the <Presentation> section and before the <Resources> section:
<LanguagePacks> <LanguagePack ID="ENU" IsDefault="true"> <DisplayStrings> <DisplayString ElementID="WalkthroughsFolder"> <Name>Walkthroughs Folder</Name> </DisplayString> <DisplayString ElementID="AlertSummaryHost"> <Name>Alert Summary Widget</Name> </DisplayString> </DisplayStrings> </LanguagePack> </LanguagePacks>
5.2 Declare Schema type
One of the important features of the widget is that it can be customized. The data representation of the user selections is stored by OM12 and passed to the component. A SchemaType is defined for the AlertSummaryHost that will detail the data values used to store selections made by the user.
At this point the SchemaType will contain no data. In the next section we will add a configuration page and personalization page to the AlertSummaryHost widget. At that time we will come back and add data elements to the SchemaType.
In AlertSummary.mpx add the following xml segment at the top of the <ManagementPackFragment> node: above the <Categories> section:
<TypeDefinitions> <SchemaTypes> <SchemaType ID="AlertSummarySchema" Accessibility="Internal"> <xsd:complexType name="Configuration"> <xsd:sequence minOccurs="1" maxOccurs="1" > <xsd:element name="notUsed1" type="xsd:boolean" minOccurs="1" maxOccurs="1" /> </xsd:sequence> </xsd:complexType> <xsd:complexType name="Personalization"> <xsd:sequence minOccurs="1" maxOccurs="1" > <xsd:element name="notUsed2" type="xsd:boolean" minOccurs="1" maxOccurs="1" /> </xsd:sequence> </xsd:complexType> </SchemaType> </SchemaTypes> </TypeDefinitions>
5.3 Add Component Behavior
Components that perform as a widget must have a ComponentBehaviors element. One of the purposes of this element is to tie the Configuration and Personalization pages to sections in the SchemaType defined for the component.
In AlertSummary.mpx add the following xml segment after the <ComponentImplementations> node:
<ComponentBehaviors> <ComponentBehavior ID="AlertSummaryComponentBehavior" Accessibility="Public" BehaviorTypeId="Visualization!Microsoft.SystemCenter.Visualization.PersonalizeBehavior" ComponentTypeId="AlertSummaryHost"> <Bindings> <Binding PropertyId="EnableImplicitChanges"> <SimpleValue Type="xsd://boolean" Value="False"/> </Binding> <Binding PropertyId="ConfigurationDataType"> <SimpleValue Type="xsd://string" Value="xsd://AlertSummaryPack!AlertSummarySchema/Configuration"/> </Binding> <Binding PropertyId="PersonalizationDataType"> <SimpleValue Type="xsd://string" Value="xsd://AlertSummaryPack!AlertSummarySchema/Personalization"/> </Binding> </Bindings> </ComponentBehavior> </ComponentBehaviors>
5.4 Add Widget Customization Pages
We will put the widget customization pages into a new Management Pack Fragment file. This code could go in AlertSummary.mpx, but using multpiple files helps the overall organization of the project. At this point in the sample we are not going to add these Customization pages. We will only put in a place holder. This will allow the widget to be installed and run, but without allowing the user to make any changes to the operation of the widget.
Add a new Management Pack fragment:
- Right click on AlertSummaryPack project in Solution Explorer and select Add | New Item.
- Select Empty Management Pack Fragment.
- Name the fragment ConfigPages.mpx and click Add.
Replace the code in the new fragment file with the following:
<ManagementPackFragment xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Presentation> <ComponentImplementations> <ComponentImplementation ID="AlertSummaryConfigPages" TypeId="Config!Microsoft.SystemCenter.Visualization.CustomConfigurationPages" Target="component://AlertSummaryPack!AlertSummaryHost" Accessibility="Public" Platform="All"> <Composite> <Component TypeId="Config!Microsoft.SystemCenter.Visualization.CustomPages"> </Component> </Composite> </ComponentImplementation> </ComponentImplementations> </Presentation> </ManagementPackFragment>
5.5 Install and Test Management Pack
To install the AlertWidgetSummary management pack from Visual Studio, press F5 or select Debug | Start debugging.
Open the Operations Console and create a new Dashboard.
Click to add a widget, and you should see a dialog box similar to the following:
6 Add Configuration and Personalization Pages to the Widget
A widget can have a Configuration page and a Personalization page. These pages are used to configure how the widget performs. Settings made on the Configuration page affect all users. Settings made on the Personalization page affect only the user that makes the selections.
In this exercise the Configuration page will be used to set the maximum rows that will be displayed for each of the severity types. The Personalization page will be used to set which severity types are displayed.
Silverlight UserControls will be used to create the Configuration page and the Personalization page.
6.1 Create User Controls for the Configuration Page
The Configuration UserControl will have two public properties:
- public int MaxCritical
- public int MaxWarning
Below is a screen shot of the Configuration User Control:
6.1.1 Create Silverlight User Control
In the AlertSummarySilverlight project add a new Silverlight UserControl:
- Right-click the project title and select Add | New Item.
- Select Silverlight User Control.
- Name the project Configuration.xaml and click Add.
Replace the existing code in Configuration.xaml with the following:
<UserControl x:Class="AlertSummaryControl.Configuration" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" HorizontalAlignment="Left" VerticalAlignment="Top" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" Height="199" Width="550" Loaded="UserControl_Loaded"> <Grid x:Name="LayoutRoot" Background="White" Height="132" Width="489"> <TextBlock Text="Enter Max Count of Alerts To Display Per Severity Level." Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="21,16,0,0" FontSize="13" FontWeight="Bold"/> <TextBlock Text="Maximum Critical Alerts" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="21,48,0,0" Width="151" /> <TextBox Height="23" Width="32" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="202,48,0,0" Name="tbMaxCriticalAlerts" TextChanged="tbMaxCriticalAlerts_TextChanged" /> <TextBlock Text="Maximum Warning Alerts" Height="23" Width="151" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="21,80,0,0"/> <TextBox Height="23" Width="32" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="202,80,0,0" Name="tbMaxWarningAlerts" TextChanged="tbMaxWarningAlerts_TextChanged" /> </Grid> </UserControl>
Notable lines in this code include:
- Lines 27 & 38: identify the UI elements for the values that the user can change.
- Lines 32 & 43: identify the event handlers that will fire when the values change.
Add the following directives to Configuration.xaml.cs.
In Configuration.xaml.cs replace the existing code for the AlertSummaryControl namespace with the following:
[Export] [PartCreationPolicy(CreationPolicy.NonShared)] public partial class Configuration : UserControl, INotifyPropertyChanged { private int _maxCritical; private int _maxWarning; public int MaxCritical { get { return _maxCritical; } set { this._maxCritical = value; this.tbMaxCriticalAlerts.Text = value.ToString(); OnPropertyChanged("MaxCritical"); } } public int MaxWarning { get { return _maxWarning; } set { this._maxWarning = value; this.tbMaxWarningAlerts.Text = value.ToString(); OnPropertyChanged("MaxWarning"); } } public Configuration() { InitializeComponent(); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private void tbMaxCriticalAlerts_TextChanged(object sender, TextChangedEventArgs e) { int value; if (int.TryParse(this.tbMaxCriticalAlerts.Text, out value)) { this.MaxCritical = value; } } private void tbMaxWarningAlerts_TextChanged(object sender, TextChangedEventArgs e) { int value; if (int.TryParse(this.tbMaxWarningAlerts.Text, out value)) { this.MaxWarning = value; } } public void UserControl_Loaded(object sender, RoutedEventArgs e) { } }
Notable lines in this code include:
- Lines 8 & 23 are the data items that are affected by the user input.
- Lines 18 & 33 update the UI when the set is fired. This will happen when OM12 passes the currently stored values to the configuration page.
- Lines 19 & 34 fire the OnPropertyChanged event when the values change. This will pass the values entered by the user to OM12.
- Lines 53 & 62 are fired when any characters are entered by the user. This eliminates the need for a Submit button.
6.1.2 Create WPF User Control
The WPF version of the Configuration UserControl will share the xaml and code behind from the Silverlight version.
- Right-click the AlertSummaryWPF project title and select Add | Existing Item.
- Change the file type dropdown to XAML Files (*.xaml;*.xoml).
- Navigate to the Configure**.xaml** file in the AlertSummarySilverlight project.
- Click the dropdown on the Add button and select Add as Link.
6.2 Create User Controls for the Personalization Page
The Personalization UserControl will have two public properties:
- public int CriticalEnabled
- public int WarningEnabled
Below is a screen shot of the Personalization User Control:
6.2.1 Create Silverlight User Control
In the AlertSummarySilverlight project add a new Silverlight UserControl:
- Right-click the project title and select Add | New Item.
- Select Silverlight User Control.
- Name the project Personalization.xaml and click Add.
Replace the existing code in Personalization.xaml with the following:
<UserControl x:Class="AlertSummaryControl.Personalization" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" HorizontalAlignment="Left" VerticalAlignment="Top" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" Loaded="UserControl_Loaded" d:DesignHeight="142" d:DesignWidth="337"> <Grid x:Name="LayoutRoot" Background="White" Height="132" Width="327"> <TextBlock Text="Select Severity Types to be Displayed" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="21,16,0,0" FontSize="13" FontWeight="Bold" /> <CheckBox Name="cbCritical" Margin="21,48,145,61" Checked="cbCritical_Checked" Unchecked="cbCritical_Checked" /> <TextBlock Text="Critical Alerts" Height="23" Width="151" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="41,48,0,0" /> <CheckBox Name="cbWarning" Margin="21,80,145,29" Checked="cbWarning_Checked" Unchecked="cbWarning_Checked" /> <TextBlock Text="Warning Alerts" Height="23" Width="151" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="41,80,0,0" /> </Grid> </UserControl>
Notable lines in this code include:
- Lines 20 & 30 identify the UI elements for the values that the user can change.
- Lines 22-23 & Lines 32-33 fire the event when the checkbox value changes.
Add the following directives to Personalization.xaml.cs:
using System.ComponentModel.Composition; using System.ComponentModel;
In Personalization..xaml.cs replace the existing code for the AlertSummaryControl namespace with the following:
[Export] [PartCreationPolicy(CreationPolicy.NonShared)] public partial class Personalization : UserControl, INotifyPropertyChanged { private bool _criticalEnabled; private bool _warningEnabled; public bool CriticalEnabled { get { return _criticalEnabled; } set { this._criticalEnabled = value; this.cbCritical.IsChecked = value; OnPropertyChanged("CriticalEnabled"); } } public bool WarningEnabled { get { return _warningEnabled; } set { this._warningEnabled = value; this.cbWarning.IsChecked = value; OnPropertyChanged("WarningEnabled"); } } public Personalization() { InitializeComponent(); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private void UserControl_Loaded(object sender, RoutedEventArgs e) { } private void cbCritical_Checked(object sender, RoutedEventArgs e) { this.CriticalEnabled = this.cbCritical.IsChecked.GetValueOrDefault(); } private void cbWarning_Checked(object sender, RoutedEventArgs e) { this.WarningEnabled = this.cbWarning.IsChecked.GetValueOrDefault(); } }
Notable lines in this code include:
- Lines 8 & 23 are the data items that are affected by the user input.
- Lines 18 & 33 update the UI when the set is fired. This will happen when OM12 passes the currently stored values to the configuration page.
- Lines 19 & 34 fire the OnPropertyChanged event when the values change. This will pass the values entered by the user to OM12.
- Lines 60 & 65 are fired when any characters are entered by the user. This eliminates the need for a Submit button.
6.2.2 Create WPF User Control
The WPF version of the Personlization UserControl will share the xaml and code behind from the Silverlight version.
- Right-click the AlertSummaryWPF project title and select Add | Existing Item.
- Change the file type dropdown to XAML Files (*.xaml;*.xoml).
- Navigate to the Personlization**.xaml** file in the AlertSummarySilverlight project.
- Click the dropdown on the Add button and select Add as Link.
6.3 Update AlertSummary UserControl to Handle Configuration Parameters
In order for the user selections in the Configuration and Personalization pages to have an effect on the behavior of the User Control, the code behind the User Control must provide public properties for each of the parameters and must take action when values are received for these parameters. The following is a list of the configuration and personalization parameters:
- public int MaxCritical
- public int MaxWarning
- public bool CriticalEnabled
- public bool WarningEnabled
The following code provides a public property for each of the configuration and personalization parameters. Insert the code into the AlertControl class in AlertControl.xaml.cs:
private int _maxCritical = 10; private int _maxWarning = 10; public int MaxCritical { get { return _maxCritical; } set { if (_maxCritical != value) { this._maxCritical = value; this.collection_CollectionChanged(null, null); } } } public int MaxWarning { get { return _maxWarning; } set { if (_maxWarning != value) { this._maxWarning = value; this.collection_CollectionChanged(null, null); } } } public bool CriticalEnabled { get { return this.CriticalPanel.Visibility == System.Windows.Visibility.Visible; } set { this.CriticalPanel.Visibility = value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; } } public bool WarningEnabled { get { return this.WarningPanel.Visibility == System.Windows.Visibility.Visible; } set { this.WarningPanel.Visibility = value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; } }
Notable lines in this code include:
- Lines 4, 21, 38, and 51 declare the public properties that will be controlled by the configuration and personalization pages.
- Lines 47 & 60 handle the changes to the CriticalEnabled and WarningEnabled properties. The visibility property of the associated panels is set according to the value of the boolean property.
The following code processes the set of alerts received taking into account the MaxCritical and MaxWarning parameters. Replace the code in the ProcessAlerts method with the following:
private void ProcessAlerts() { if (this.Items == null || this.alertData == null || this.alertData.Items == null) { return; } List<AlertItem> criticalAlerts = new List<AlertItem>(); List<AlertItem> warningAlerts = new List<AlertItem>(); int criticalCount = 0; int warningCount = 0; lock (this.alertData.Items) { foreach (var alert in this.alertData.Items.ToArray()) { try { DynamicObservableDictionaryDataObject dataObject = (DynamicObservableDictionaryDataObject)alert; AlertItem newItem = new AlertItem() { Id = dataObject["Id"].ToString(), Name = dataObject["Name"].ToString(), Severity = dataObject["Severity"].ToString(), Priority = dataObject["Priority"].ToString() }; switch (dataObject["Severity"].ToString()) { case "Critical": criticalCount++; if (criticalCount <= _maxCritical) { criticalAlerts.Add(newItem); } break; case "Warning": warningCount++; if (warningCount <= _maxWarning) { warningAlerts.Add(newItem); } break; } } catch (Exception ex) { } } } this.alertData.CriticalAlertCount = criticalAlerts.Count.ToString(); this.alertData.CriticalAlerts = criticalAlerts; this.alertData.WarningAlertCount = warningAlerts.Count.ToString(); this.alertData.WarningAlerts = warningAlerts; }
Notable lines in this code include:
- Lines 35 & 43 limit the number of members in the List<> based on the value of the associated parameter.
6.4 Update Management Pack with Configuration and Personlization Parameters
The purpose of the Configuration and Personalization pages is to allow the user to control the behavior of the widget by inputting values for defined parameters. These parameter values will be passed between OM12, the Configuration/Personalization pages, and the widget. It is important to understand how these parameters are defined in the Management Pack. The following is an outline that identifies each location in the Management Pack that will contain a reference to these parameters. The parameter name used in each location is identified by the use of a suffix. The entry in bold shows the parameter named used in the location.
SchemaType
The <SchemaType> is an xml schema. It is used to define the set of parameters used by the configuration and personalization pages to customize the widget. OM12 will use this schema when storing the parameter values selected by the user. The schema is tied to the widget via the <ComponentBehavior> element.<SchemaType ID="AlertSummarySchema" Accessibility="Internal"> <xsd:complexType name="Configuration"> <xsd:sequence minOccurs="1" maxOccurs="1" > <xsd:element name="MaxCritical_widget" type="xsd:int" minOccurs="1" maxOccurs="1" />
Property definitions for the AlertSummaryControl
The AlertSummaryControl component is created by its <ComponentType> element. This component exposes the WPF UserControl. The public properties of the userControl are identified in the **AlertSummaryControl ** component with <Property> elements under the <ComponentType> node.<ComponentTypes> <ComponentType ID="AlertSummaryControl" Accessibility="Public"> <Property Name="MaxCritical_uc" Type="xsd://integer" BindingDirection="In" />
Binding of AlertSummaryControl Properties to the WPF UserControl
In item above the public properties of the WPF UserControl are identified. In this item these public properties are bound to a corresponding property in the AlertSummaryControl component. The ComponentImplementation for the AlertSummaryControl identifies it as a <Unit>
Component, which means that it contains an external assembly. Each property that was defined in the ComponentType for AlertSummaryControl must be bound to a public property in the class contained in the external assembly.<ComponentImplementation TypeId="AlertSummaryControl" <Unit> <ContractFactory>AlertSummaryControl.AlertControl</ContractFactory> <Property Name="MaxCritical" Direction="In"> <Reference>$Property/MaxCritical_uc$</Reference>
MaxCritical is the public property in the WPF UserControl, $Property/MaxCritical_uc$ is the property in the AlertSummaryControl.
Property definitions in the ComponentType for AlertSummaryHost
The AlertSummaryHost component is the actual “widget”. This component must define properties that correspond to the properties in the AlertSummaryControl component and the properties defined in the SchemaType.<ComponentType ID="AlertSummaryHost" Accessibility="Public"> <Property Name="MaxCritical_widget" Type="xsd://integer" BindingDirection="In" />
Binding of AlertSummaryControl Properties to the AlertSummaryHost Properties
The properties in the AlertSummaryControl component must be bound to the corresponding property in the AlertSummaryHost component<Binding PropertyId="MaxCritical_uc"> <Reference>$Property/MaxCritical_widget$</Reference>
The example above binds the property in the WPF UserControl component, MaxCritical_uc, to the property in the host component, MaxCritical_widget.
Property definition in the ComponentType for the Configuration UserControl
The ConfigurationPage component defines the WPF UserControl that provides the UI for the configuration page. The public properties of this UserControl are identified with <Property> elements under the <ComponentType> node for this component
The ComponentImplementation for the WPF UserControl used on the Configuration Page identifies it as a <Unit> Component, which means that it contains an external assembly. Each property defined in the ComponentType must be bound to a public property in the class contained in the external assembly.<ComponentType ID="ConfigurationPage" Accessibility="Public"> <Property Name="MaxCritical_cPage" Type="xsd://int" BindingDirection="In" />
MaxCritical is the public property in the WPF Configuration UserControl, $Property/MaxCritical_cPage$ is the property in the ConfigurationPage component.
Binding of Configuration Page Properties to the WPF Configuration UserControl
The ComponentImplementation for the WPF UserControl used on the Configuration Page identifies it as a <Unit> Component, which means that it contains an external assembly. Each property defined in the ComponentType must be bound to a public property in the class contained in the external assembly.<ComponentImplementation TypeId="ConfigurationPage"> <Unit> <ContractFactory>AlertSummaryControl.Configuration</ContractFactory> <Property Name="MaxCritical" Direction="Both"> <Reference>$Property/MaxCritical_cPage$</Reference>
MaxCritical is the public property in the WPF Configuration UserControl, $Property/MaxCritical_cPage$ is the property in the ConfigurationPage component.
Binding of Configuration UserControl property to the Configuration Wizard component
The configurationPage component appears inside of the Configuration Wizard component. The parameters handled on the configurationPage must be bound to the schema data that is managed by OM12.<Component TypeId="ConfigurationPage"> <Binding PropertyId="MaxCritical_cPage"> <Reference>$Property/Data/MaxCritical_widget$</Reference>
6.4.1 Create ComponentTypes for the Configuration and Personalization UserControls
Enter the following segment in ConfigPages.mpx, inside of the <Presentation> node:
<ComponentTypes> <ComponentType ID="ConfigurationPage" Accessibility="Public"> <Property Name="MaxCritical_cPage" Type="xsd://int" BindingDirection="In" /> <Property Name="MaxWarning_cPage" Type="xsd://int" BindingDirection="In" /> </ComponentType> <ComponentType ID="PersonalizationPage" Accessibility="Public"> <Property Name="CriticalEnabled_pPage" Type="xsd://boolean" BindingDirection="In" /> <Property Name="WarningEnabled_pPage" Type="xsd://boolean" BindingDirection="In" /> </ComponentType> </ComponentTypes>
6.4.2 Update the SchemaType with UserControl Properties
Update the <SchemaTypes> node in AlertSummary.mpx as follows:
<SchemaTypes> <SchemaType ID="AlertSummarySchema" Accessibility="Internal"> <xsd:complexType name="Configuration"> <xsd:sequence minOccurs="1" maxOccurs="1" > <xsd:element name="MaxCritical_widget" type="xsd:int" minOccurs="1" maxOccurs="1" /> <xsd:element name="MaxWarning_widget" type="xsd:int" minOccurs="1" maxOccurs="1" /> </xsd:sequence> </xsd:complexType> <xsd:complexType name="Personalization"> <xsd:sequence minOccurs="1" maxOccurs="1" > <xsd:element name="CriticalEnabled_widget" type="xsd:boolean" minOccurs="1" maxOccurs="1" /> <xsd:element name="WarningEnabled_widget" type="xsd:boolean" minOccurs="1" maxOccurs="1" /> </xsd:sequence> </xsd:complexType> </SchemaType> </SchemaTypes>
6.4.3 Update the AlertSummaryControl Component Type for Parameters
The AlertSummaryControl now has public properties for the parameters set by the user on the Configuration and Personalization Pages.
In AlertSummary.mpx, in the <ComponentType ID="AlertSummaryControl"> node, add the following code:
<Property Name="MaxCritical_uc" Type="xsd://integer" BindingDirection="In" /> <Property Name="MaxWarning_uc" Type="xsd://integer" BindingDirection="In" /> <Property Name="WarningEnabled_uc" Type="xsd://boolean" BindingDirection="In" /> <Property Name="CriticalEnabled_uc" Type="xsd://boolean" BindingDirection="In" />
In AlertSummary.mpx, in the <ComponentType ID="AlertSummaryHost"> node, add the following code:
<Property Name="MaxCritical_widget" Type="xsd://integer" BindingDirection="In" /> <Property Name="MaxWarning_widget" Type="xsd://integer" BindingDirection="In" /> <Property Name="CriticalEnabled_widget" Type="xsd://boolean" BindingDirection="In" /> <Property Name="WarningEnabled_widget" Type="xsd://boolean" BindingDirection="In" />
6.4.4 Update the AlertSummaryControl Component Implementation for Parameters
In AlertSummary.mpx, in the <ComponentImplementation TypeId="AlertSummaryControl"> node, add the following code:
<Property Name="MaxCritical" Direction="In"> <Reference>$Property/MaxCritical_uc$</Reference> </Property> <Property Name="MaxWarning" Direction="In"> <Reference>$Property/MaxWarning_uc$</Reference> </Property> <Property Name="CriticalEnabled" Direction="In"> <Reference>$Property/CriticalEnabled_uc$</Reference> </Property> <Property Name="WarningEnabled" Direction="In"> <Reference>$Property/WarningEnabled_uc$</Reference> </Property>
6.4.5 Update the AlertSummaryHost Composite Component for Parameters
In AlertSummary.mpx, in the <ComponentImplementation TypeId="AlertSummaryHost"> node, add the following code:
<Binding PropertyId="Items"> <Reference>$Variable/MyVar$</Reference> </Binding> <Binding PropertyId="CriticalEnabled_uc"> <Reference>$Property/CriticalEnabled_widget$</Reference> </Binding> <Binding PropertyId="WarningEnabled_uc"> <Reference>$Property/WarningEnabled_widget$</Reference> </Binding> <Binding PropertyId="MaxCritical_uc"> <Reference>$Property/MaxCritical_widget$</Reference> </Binding> <Binding PropertyId="MaxWarning_uc"> <Reference>$Property/MaxWarning_widget$</Reference> </Binding>
6.4.6 Create Unit Components for the Configuration and Personalization User Controls
In ConfigPages.mpx, in the <ComponentImplementations> node, add the following code:
<ComponentImplementation TypeId="ConfigurationPage" ID="ConfigurationPageImplementation" Accessibility="Public" Platform="All"> <Unit> <ContractFactory>AlertSummaryControl.Configuration</ContractFactory> <Property Name="MaxCritical" Direction="Both"> <Reference>$Property/MaxCritical_cPage$</Reference> </Property> <Property Name="MaxWarning" Direction="Both"> <Reference>$Property/MaxWarning_cPage$</Reference> </Property> </Unit> </ComponentImplementation> <ComponentImplementation TypeId="PersonalizationPage" ID="PersonalizationPageImplementation" Accessibility="Public" Platform="All"> <Unit> <ContractFactory>AlertSummaryControl.Personalization</ContractFactory> <Property Name="CriticalEnabled" Direction="Both"> <Reference>$Property/CriticalEnabled_pPage$</Reference> </Property> <Property Name="WarningEnabled" Direction="Both"> <Reference>$Property/WarningEnabled_pPage$</Reference> </Property> </Unit> </ComponentImplementation>
6.4.7 Create a Composite Component to Implement the Configuration Page
In ConfigPages.mpx replace the ComponentImplemenation node with the ID AlertSummaryConfigPages with the following code:
<ComponentImplementation ID="AlertSummaryConfigPages" TypeId="Config!Microsoft.SystemCenter.Visualization.CustomConfigurationPages" Target="component://AlertSummaryPack!AlertSummaryHost" Accessibility="Public" Platform="All"> <Composite> <Component TypeId="Config!Microsoft.SystemCenter.Visualization.CustomPages"> <Binding PropertyId="Pages"> <Component TypeId="Visualization!Microsoft.SystemCenter.Visualization.WizardRegularPage"> <Binding PropertyId="StepId"> <SimpleValue Type="xsd://string" Value="Max Alerts Displayed"/> </Binding> <Binding PropertyId="Title"> <SimpleValue Type="xsd://string" Value="Max Alerts Displayed"/> </Binding> <Binding PropertyId="IsValid"> <SimpleValue Type="xsd://boolean" Value="True"/> </Binding> <Binding PropertyId="FinishButtonText"> <SimpleValue Type="xsd://string" Value="Finish"/> </Binding> <Binding PropertyId="Content"> <Component TypeId="ConfigurationPage"> <Binding PropertyId="MaxCritical_cPage"> <Reference>$Property/Data/MaxCritical_widget$</Reference> </Binding> <Binding PropertyId="MaxWarning_cPage"> <Reference>$Property/Data/MaxWarning_widget$</Reference> </Binding> </Component> </Binding> </Component> </Binding> </Component> </Composite> </ComponentImplementation>
6.4.8 Create a Composite Component to Implement the Personalization Page
In ConfigPages.mpx add the following code inside of the ComponentImplemenations node:
<ComponentImplementation ID="AlertSummaryWidgetPersonalizationPages" TypeId=" Config!Microsoft.SystemCenter.Visualization.CustomPersonalizationPages" Target="component://AlertSummaryPackRMS!AlertSummaryHost" Accessibility="Public" Platform="All"> <Composite> <Component TypeId=" Config!Microsoft.SystemCenter.Visualization.CustomPages"> <Binding PropertyId="Pages"> <Component TypeId="Visualization!Microsoft.SystemCenter.Visualization.WizardRegularPage"> <Binding PropertyId="StepId"> <SimpleValue Type="xsd://string" Value="Alert Severities"/> </Binding> <Binding PropertyId="Title"> <SimpleValue Type="xsd://string" Value="Alert Severities"/> </Binding> <Binding PropertyId="IsValid"> <SimpleValue Type="xsd://boolean" Value="True"/> </Binding> <Binding PropertyId="FinishButtonText"> <SimpleValue Type="xsd://string" Value="Finish"/> </Binding> <Binding PropertyId="Content"> <Component TypeId="PersonalizationPage"> <Binding PropertyId="CriticalEnabled_pPage"> <Reference>$Property/Data/CriticalEnabled_widget$</Reference> </Binding> <Binding PropertyId="WarningEnabled_pPage"> <Reference>$Property/Data/WarningEnabled_widget$</Reference> </Binding> </Component> </Binding> </Component> </Binding> </Component> </Composite> </ComponentImplementation>