The out of box drop down controls provided by TFS requires the drop down elements to be either hardcoded inside the WIT or defined as a global list. This makes the dropdown content definition quite inflexible from list management point of view.
The databound dropdown custom WIT control achieves the easy management of dropdown contents by fetching the data from database data-table. The code snippet below explains the important part of the control.
The custom control is defined as UserControl and implement IWorkItemControl interface. It holds the combobox and listens to SelectedIndexChanged event.
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Controls;
#endregion
namespace TelarixWitCustomControls
{
///
/// This class represents data bound dropdown custom control to be used in side
/// Team foundation server work item definition.
///
public partial class DataboundDropdownList : UserControl, IWorkItemControl
{
private ComboBox m_databoundCombo = new ComboBox();
private bool m_isIndexChange = false;
private bool m_IsUpdatingFromDatasource = false;
///
/// Default constructor
///
public DataboundDropdownList()
{
InitializeComponent();
//Create the combo box with sorted option and add it to control
m_databoundCombo.Dock = DockStyle.Top;
m_databoundCombo.Sorted = true;
m_databoundCombo.SelectedIndexChanged +=
new EventHandler(m_databoundCombo_SelectedIndexChanged);
this.Controls.Add(m_databoundCombo);
}
SelectedIndexChanged event sink is used to set the value of workitem field.
void m_databoundCombo_SelectedIndexChanged(object sender, EventArgs e)
{
if (m_IsUpdatingFromDatasource)
{
//Control is updating its contents from datasource.
//Do nothing to avoid inconsistant state.
return;
}
//Set selected value to work item field
if(m_databoundCombo.SelectedItem != null)
m_workItem.Fields[m_fieldName].Value = m_databoundCombo.SelectedItem.ToString();
m_isIndexChange = true;
}
The function below populates the dropdown values from data source.
///
/// Populate drop down with values from data source
///
void IWorkItemControl.InvalidateDatasource()
{
if (m_isIndexChange)
{
// Do not do anything if datasource is invalidated because of selected index change.
m_isIndexChange = false;
return;
}
if (m_databoundCombo.Items.Count > 0)
return;
m_databoundCombo.Items.Clear();
m_databoundCombo.Text = m_workItem.Fields[m_fieldName].Value.ToString();
AllowedValuesCollection allowedValues = m_workItem.Fields[m_fieldName].AllowedValues;
if (allowedValues.Count != 2)
{
MessageBox.Show("Invalid Parameters defined in WIT xml." +
"Please refer design document for DataboundDropdownList design and configuration.");
return;
}
//Read connection string and sql from WIT definition
string connectionString = allowedValues[1].Replace("/","\\");
string sqlQuery = allowedValues[0];
m_IsUpdatingFromDatasource = true;
//Open SQL Connection
SqlConnection _sqlCon = new SqlConnection(connectionString);
_sqlCon.Open();
SqlDataReader dsSource = null;
try
{
if (_sqlCon.State == ConnectionState.Open)
{
dsSource = SqlHelper.ExecuteReader(_sqlCon, CommandType.Text, sqlQuery);
while (dsSource.Read())
{
m_databoundCombo.Items.Add( dsSource.GetString(0));
}
}
}
catch (Exception exp)
{
MessageBox.Show("Unable to connect to datasource.");
}
finally
{
if(null != dsSource) dsSource.Close();
if(null != _sqlCon) _sqlCon.Close();
m_IsUpdatingFromDatasource = false;
}
}
The parameters to the control are database connection string and SQL query to fetch the dropdown contents as shown in the snippet below:
- <FIELD name="How Found" refname="Telarix.HowFound" type="String">
<HELPTEXT>Maps to 'Source' from eFly.HELPTEXT>
- <SUGGESTEDVALUES>
<LISTITEM value="Server=sssss;UID=yyyyy;PWD=xxxxx;DATABASE=dddddd" />
<LISTITEM value="select SourceName from crete.fly.dbo.tbSource" />
SUGGESTEDVALUES>
FIELD>
Complete code
#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Windows.Forms;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Controls;
#endregion
namespace TelarixWitCustomControls
{
///
/// This class represents data bound dropdown custom control to be used in side
/// Team foundation server work item definition.
///
public partial class DataboundDropdownList : UserControl, IWorkItemControl
{
private ComboBox m_databoundCombo = new ComboBox();
private bool m_isIndexChange = false;
private bool m_IsUpdatingFromDatasource = false;
///
/// Default constructor
///
public DataboundDropdownList()
{
InitializeComponent();
//Create the combo box with sorted option and add it to control
m_databoundCombo.Dock = DockStyle.Top;
m_databoundCombo.Sorted = true;
m_databoundCombo.SelectedIndexChanged +=
new EventHandler(m_databoundCombo_SelectedIndexChanged);
this.Controls.Add(m_databoundCombo);
}
void m_databoundCombo_SelectedIndexChanged(object sender, EventArgs e)
{
if (m_IsUpdatingFromDatasource)
{
//Control is updating its contents from datasource.
//Do nothing to avoid inconsistant state.
return;
}
//Set selected value to work item field
if(m_databoundCombo.SelectedItem != null)
m_workItem.Fields[m_fieldName].Value = m_databoundCombo.SelectedItem.ToString();
m_isIndexChange = true;
}
private void DropdownResized(object sender, EventArgs e)
{
}
#region IWorkItemControl Members
private EventHandlerList m_events;
private EventHandlerList DataSourceEvents
{
get
{
if (m_events == null)
{
m_events = new EventHandlerList();
}
return m_events;
}
}
private static object EventBeforeUpdateDatasource = new object();
event EventHandler IWorkItemControl.BeforeUpdateDatasource
{
add { DataSourceEvents.AddHandler(EventBeforeUpdateDatasource, value); }
remove { DataSourceEvents.RemoveHandler(EventBeforeUpdateDatasource, value); }
}
event EventHandler IWorkItemControl.AfterUpdateDatasource
{
add { DataSourceEvents.AddHandler(EventBeforeUpdateDatasource, value); }
remove { DataSourceEvents.RemoveHandler(EventBeforeUpdateDatasource, value); }
}
void IWorkItemControl.Clear()
{
}
void IWorkItemControl.FlushToDatasource()
{
}
///
/// Populate drop down with values from data source
///
void IWorkItemControl.InvalidateDatasource()
{
if (m_isIndexChange)
{
// Do not do anything if datasource is invalidated because of selected index change.
m_isIndexChange = false;
return;
}
if (m_databoundCombo.Items.Count > 0)
return;
m_databoundCombo.Items.Clear();
m_databoundCombo.Text = m_workItem.Fields[m_fieldName].Value.ToString();
AllowedValuesCollection allowedValues = m_workItem.Fields[m_fieldName].AllowedValues;
if (allowedValues.Count != 2)
{
MessageBox.Show("Invalid Parameters defined in WIT xml." +
"Please refer design document for DataboundDropdownList design and configuration.");
return;
}
//Read connection string and sql from WIT definition
string connectionString = allowedValues[1].Replace("/","\\");
string sqlQuery = allowedValues[0];
m_IsUpdatingFromDatasource = true;
//Open SQL Connection
SqlConnection _sqlCon = new SqlConnection(connectionString);
_sqlCon.Open();
SqlDataReader dsSource = null;
try
{
if (_sqlCon.State == ConnectionState.Open)
{
dsSource = SqlHelper.ExecuteReader(_sqlCon, CommandType.Text, sqlQuery);
while (dsSource.Read())
{
m_databoundCombo.Items.Add( dsSource.GetString(0));
}
}
}
catch (Exception exp)
{
MessageBox.Show("Unable to connect to datasource.");
}
finally
{
if(null != dsSource) dsSource.Close();
if(null != _sqlCon) _sqlCon.Close();
m_IsUpdatingFromDatasource = false;
}
}
private StringDictionary m_properties;
///
/// Get or Set workitem properties
///
StringDictionary IWorkItemControl.Properties
{
get
{
return m_properties;
}
set
{
m_properties = value;
}
}
private bool m_readOnly;
///
/// Get or Set workitem permission.
///
bool IWorkItemControl.ReadOnly
{
get
{
return m_readOnly;
}
set
{
m_readOnly = value;
}
}
void IWorkItemControl.SetSite(IServiceProvider serviceProvider)
{
}
private WorkItem m_workItem;
private WorkItem m_workItemCopy;
///
/// Get or Set workitem in which this control shall be hosted.
///
object IWorkItemControl.WorkItemDatasource
{
get
{
return m_workItem;
}
set
{
m_workItem = (WorkItem) value;
}
}
private string m_fieldName;
///
/// Get or Set workitem field name.
///
string IWorkItemControl.WorkItemFieldName
{
get
{
return m_fieldName;
}
set
{
m_fieldName = value;
}
}
#endregion
}
}
0 comments:
Post a Comment