To transfer an active audio conversation, you must obtain the AVModality instance on the conversation you will transfer and a Contact instance representing the person receiving the transferred call. Your application must be able to handle all of the outcomes of the transfer operation. A transfer operation can result in the acceptance of the transfer by a target user or rejection by the same user. When a transfer is rejected, your application is responsible for reacting to the rejection by retrieving the audio/video modality from a hold state or terminating the call. You handle a series of events on the conversation audio/video modality to monitor the progress of the transfer.

The state and the availability of specific actions such as hold, retrieve, transfer, connect, and disconnect must be monitored after a transfer operation is initiated. While a transfer is pending, you cannot retrieve, connect, or disconnect a call. If the transfer is accepted, you are disconnected from the call with no further action on the part of your application. If the transfer is rejected, the retrieve, and disconnect actions are available. The conversation state is set to active.

Tip Tip

When forwarding or transferring a conversation using the audio/video modality, the instant messaging modality is connected to the target participant automatically. When a call is transferred to a public switch telephone network (PSTN) telephone, video and IM are disconnected and cannot be re-connected.

The following table lists modality actions and their availability while a conversation transfer is pending.

Modality Action

Availability

Hold

True

Retrieve

False

Transfer

False

The following table lists modality actions and their availability while a conversation transfer has been rejected.

Modality Action

Availability

Hold

False

Retrieve

True

Transfer

True

Transferring an Audio Conversation

  1. Get the LyncClient instance. Verify that the client is signed in to the server. For information about signing in to Microsoft Lync Server 2010, see Walkthrough: Sign In to Lync .

  2. Get a connected instance of Conversation

    The conversation direction can be incoming or outgoing. For information about starting an audio conversation, see Walkthrough: Start an Audio Conversation .

  3. Register for the StateChanged event on the Conversation instance.

  4. Get the AVModalityinstance from the collection of modalities on the Conversation instance.

    Read the Modalities property. Use the ModalityTypes . AudioVideoenumerator as an index to specify which modality to get.

  5. Register for the ModalityStateChanged and ActionAvailabilityChanged events on the conversation AVModality instance.

  6. Get a Contact instance that resolves to the user you target for the transfer.

    Tip Tip

    If the target user is not a contact in one of the groups exposed by Groups , you can obtain the contact by searching for the contact using the Microsoft Lync 2010 API search feature, or you can call into GetContactByuri(string) if you have the URI of the target user.

    For information about searching for a contact, see Walkthrough: Search For a Contact .

  7. To start the transfer operation, call into the BeginTransfer or BeginTransfer method of the AVModality instance. Pass the target contact as the first argument of the method call. If you are transferring a call to a public switched telephone network (PSTN) phone, voicemail or other type of contact endpoint, call the overload of BeginTransferand pass the ContactEndpoint representing the transfer target.

  8. Catch the series of ActionAvailabilityChanged events raised by the audio/video modality.

    As the availability of hold, retrieve, and transfer change as the transfer operation progresses, your event callback for these events is invoked. If the transfer is rejected, the retrieve, disconnect, and transfer options are made available. If the transfer is accepted, your event callback for these events is not invoked.

    Tip Tip

    You should use these events to trigger the update of call action button controls you place on your UI. For example, when the Hold action is not available, disable a hold button on your form.

  9. Catch the series of StateChanged events raised by the conversation.

    After completing the transfer operation, your event callback for these events is invoked. If the transfer is accepted, your event callback is invoked and the state of the conversation is ConversationState . Terminated. If the transfer is rejected, your event callback is invoked and the state of the conversation is ConversationState . Active.

    Important note Important

    The conversation state changes several times while the transfer operation is in progress. You receive the event indicating the conversation is active before the transfer is accepted or rejected, and again if the transfer is rejected. Each time you handle the event with this conversation state, you must evaluate the availability of the retrieve action on the audio/video modality (step 8). If the conversation becomes active and the retrieve action is available, the transfer is rejected.

Handling Events

Audio/Video Modality ActionAvailabilityChanged Event

  • Check the Action and IsAvailable properties. If the action is ModalityAction . Retrieve, . Disconnect, . RemoteTransfer, . ConsultativeTransfer, LocalTransfer, or Forwardthen you get the IsAvailable property. If true, then the transfer was rejected. In this case, you will also receive a conversation state changed event indicating the conversation is now active.

Conversation StateChanged Event

  • Read the NewState property. The resulting enumerator indicates the state of the transfer operation.

    If the state is ConversationState . Terminatedthen the transfer is accepted and you should un-register for conversation and modality events and dispose of the conversation. If the state is . Activeand the audio/video modality can be retrieved, disconnected, transferred, or forwarded then the transfer operation was rejected.

Examples

Class Field Declarations

The following declarations are added to your custom class. You initiate _AVModalitywith the AVModality obtained from the Modalities property of the active conversation.

  Copy imageCopy Code
		private AVModality _AVModality;
		private LyncClient _LyncClient;

		/// <summary>
		/// Updates a form control based on action.
		/// </summary>
		/// <param name="action">UIDelegateAction. The action
to perform on a control.</param>
		/// <param name="formControl">object. The form
control to act on.</param>
		/// <param name="updateValue">updateValue. The value
to set on the control property.</param>
		delegate void UpdateFormControlDelegate(UIDelegateAction
action, object formControl, object updateValue);

	enum UIDelegateAction
	{ 
		SetButtonEnableState = 0,
		SetAllButtonsEnableState = 1,
		SetButtonText = 2,
		SetLabelText = 3,
		CloseForm = 4,
		ShutdownForm = 5
}

Transfer a Call

The following example uses an active Conversation instance where the audio/video modality is connected to a remote user.

Caution note Caution

The example only illustrates the walkthrough tasks need to transfer a call. For an example of starting an audio conversation, see Walkthrough: Start an Audio Conversation .

C#  Copy imageCopy Code
		/// <summary>
		/// Performs a "blind transfer" on the active conversation
		/// </summary>
		/// <param name="targetURI">string. Uri string
resolving to a contact or a telephone such as a mobile
phone.</param>
		private void TransferConversation(string targetURI)
		{
			try
			{
				Contact TargetContact =
_LyncClient.ContactManager.GetContactByUri(targetURI);
				if (TargetContact != null)
				{
					List<string> _context = new
List<string>();
					Object[] asyncState = {
ModalityState.Transferring, _context, _AVModality };
					_AVModality.BeginTransfer(TargetContact,
TransferOptions.None, TransferModalityCallback, asyncState);
			}
		}
			catch (ArgumentException) { 
				MessageBox.Show("Entered Uri is not valid " +
targetURI); 
		}
			catch (ItemNotFoundException) { 
				MessageBox.Show("Entered Uri could not be resolved
to a Contact " + targetURI); 
		}

	}

Transfer Operation Callback

The following example is called asynchronously on the Lync thread when the transfer operation completes.

  Copy imageCopy Code
		/// <summary>
		/// Called on the LyncClient worker thread when a call
transfer operation completes.
		/// </summary>
		/// <param name="ar">IAsyncResult. The state of the
asynchronous operation.</param>
		private void TransferModalityCallback(IAsyncResult ar)
		{
			if (ar.IsCompleted == true)
			{
				_ConsultTransferTargetConversation = null;
				Object[] _asyncState = (Object[])ar.AsyncState;
				ModalityState _targetState =
(ModalityState)_asyncState[0];
				IList<string> _contextProperties =
(List<string>)_asyncState[1];
				((AVModality)_asyncState[2]).EndTransfer(out
_targetState, out _contextProperties, ar);

				if (_targetState == ModalityState.Disconnected)
				{
					this.Invoke(
						new
UpdateFormControlDelegate(UpdateFormControl),
						new object[]
{UIDelegateAction.SetAllButtonsEnableState, 
						null, 
						false });
			}
		}
	}

Modality.ActionAvailabilityChanged Event

Handle the event to discover ability to hold, retrieve, and transfer. Event is raised when your ability to do any of these actions changes. When transferring, actions change. When transfer is rejected, actions change.

C#  Copy imageCopy Code
		/// <summary>
		/// The availability of an action on the audio/video
modality has changed. This event triggers the enable state of
		/// MainForm action buttons.
		/// </summary>
		/// <param name="sender">object. the AVModality
instance whose action availability has changed.</param>
		/// <param
name="e">ModalityActionAvailabilityChangedEventArgs. Event state
data providing the action and availability that
changed.</param>
		void _AVModality_ActionAvailabilityChanged(object sender,
ModalityActionAvailabilityChangedEventArgs e)
		{
			string HoldButtonText = string.Empty;
			switch (e.Action)
			{
				case ModalityAction.Hold:
					if (e.IsAvailable == true)
					{
						HoldButtonText = "Hold Conversation";
				}
					if (e.IsAvailable == false)
					{
						HoldButtonText = "Pick up Conversation";
				}
					break;
				case ModalityAction.LocalTransfer:
					this.Invoke(
						new
UpdateFormControlDelegate(UpdateFormControl),
						new object[]
{UIDelegateAction.SetButtonEnableState, 
							Transfer_Button, 
							e.IsAvailable });
					break;
		}

			if (HoldButtonText.Length > 0)
			{
				this.Invoke(
					new
UpdateFormControlDelegate(UpdateFormControl),
					new object[] {UIDelegateAction.SetButtonText, 
						Hold_Button, 
						HoldButtonText });
		}
	}

ConversationStateChanged Event

Use ConversationState.Inactivewhen a call is placed on hold after a call transfer. Use ConversationState.Activewhen transfer is rejected. After the transfer is rejected, the call must be retrieved.

  Copy imageCopy Code
		/// <summary>
		/// Handles event raised when the state of an active
conversation has changed. 
		/// </summary>
		/// <param name="source">Conversation. The active
conversation that raised the state change event.</param>
		/// <param
name="data">ConversationStateChangedEventArgs. Event data
containing state change data</param>
		void Conversation_ConversationChangedEvent(object source,
ConversationStateChangedEventArgs data)
		{
			UpdateFormControlDelegate del = new
UpdateFormControlDelegate(UpdateFormControl);
			if (data.NewState == ConversationState.Inactive)
			{
				MessageBox.Show("Transfer is in progress.",
"Conversation State Changed");
		}
			if (data.NewState == ConversationState.Active)
			{
				if
(((Conversation)source).Modalities[ModalityTypes.AudioVideo].CanInvoke(ModalityAction.Retrieve)
||
((Conversation)source).Modalities[ModalityTypes.AudioVideo].CanInvoke(ModalityAction.Transfer))
				{
					MessageBox.Show("Transfer has been rejected.",
"Conversation State Changed");
			}
		}
			if (data.NewState == ConversationState.Terminated)
			{
				MessageBox.Show("Conversation state is terminated",
"Conversation State Changed");
				// Update form status label text with current state
of conversation.
		}
	}

Windows Form Control Update Helper

The following example is called using the delegate declared as a class field. This example is invoked on the Lync thread and the event data is marshaled to the UI thread by the Invokecall.

  Copy imageCopy Code
		/// <summary>
		/// Updates a form control based on action.
		/// </summary>
		/// <param name="action">UIDelegateAction. The action
to perform on a control.</param>
		/// <param name="formControl">object. The form
control to act on.</param>
		/// <param name="updateValue">updateValue. The value
to set on the control property.</param>
		private void UpdateFormControl(UIDelegateAction action,
object formControl, object updateValue)
		{
			switch (action)
			{ 
				case UIDelegateAction.CloseForm:
					System.Windows.Forms.Form formToClose =
(System.Windows.Forms.Form)formControl;
					formToClose.Close();
					break;
				case UIDelegateAction.SetButtonEnableState:
					System.Windows.Forms.Button buttonToToggle =
(System.Windows.Forms.Button)formControl;
					buttonToToggle.Enabled = (Boolean)updateValue;
					break;
				case UIDelegateAction.SetAllButtonsEnableState:
					Park_Button.Enabled = (Boolean)updateValue;
					Hold_Button.Enabled = (Boolean)updateValue;
					ConsultTransfer_Button.Enabled =
(Boolean)updateValue;
					Transfer_Button.Enabled = (Boolean)updateValue;
					CompleteConsult_Button.Enabled =
(Boolean)updateValue;
					break;
				case UIDelegateAction.SetButtonText:
					System.Windows.Forms.Button buttonToUpdate =
(System.Windows.Forms.Button)formControl;
					buttonToUpdate.Text = (string)updateValue;
					break;
				case UIDelegateAction.SetLabelText:
					System.Windows.Forms.Label labelToUpdate =
(System.Windows.Forms.Label)formControl;
					labelToUpdate.Text = (string)updateValue;
					break;
				case UIDelegateAction.ShutdownForm:
					this._ClientModel.Dispose();
					this.Close();
					break;
		}
	}

See Also