C# Applications

!~ Context Help Made Easy
This article introduces a new way of instrumenting your code that enables help authors associate help topics with the application’s visual contexts at any time - even post-compilation – and to do so using the application’s user interface without the involvement of the developer.
.NET 2.0, VS2005, C#, Windows, Arch, Dev, Beginner

**********************************************************************************
!~ Sending and playing microphone audio over network
.NET 2.0, VS2005, C# 2.0, Windows, Dev, Intermediate

**********************************************************************************

Sending and playing microphone audio over network

Download AdvancedWave source files - 2,416.2 KB


Download LumiSoftNet - 1,187.7 KB




Introduction
This example shows you how to receive data from a microphone and stream it over UDP to another computer. The example application can act like a direct phone, if both endpoints listen for data and send microphone data to each other. One would probably suspect that no source code exists for that, but of course it does. I hate those who will do commercial advertising. There is also a second related project what will contain a UDP server that we need to send/receive audio and compress it with g711 codec.Though only UDP is not the best way to transport audio data, RTP is the right way to go. RTP adds quality of service to transported audio, you can see how many packets are lost and can even arrange disordered packets. I will try to add an RTP example soon, so be calm, it's under way. There are some similar example applications, but most of them aren't well commented and missing some parts, so I will try to fill this part.

The package contains:
LumiSoft.Media - Audio related API (Included in example app)
LumiSoft.Net - UDP server, G711 codec
Example Application


Using the code
WaveIn - class provides a simple way to receive audio from a microphone. Actually all what you need to do is:WavIn.Devices - returns all available input devices from where we can get data.
///


/// Application main class.
///

public class Test
{
private WavIn m_pSoundReceiver = null;

///
/// Default constructor.
///

public Test()
{
// G711 needs 8KHZ 16 bit 1 channel audio,
// 400kb buffer gives us 25ms audio frame.
m_pSoundReceiver = new WavIn(WavIn.Devices[0],8000,16,1,400);
m_pSoundReceiver.BufferFull += new BufferFullHandler
(m_pSoundReceiver_BufferFull);
m_pSoundReceiver.Start();
}

///
/// This method is called when recording buffer is full
/// and we need to process it.
///

/// Recorded data.
private void m_pSoundReceiver_BufferFull(byte[] buffer)
{
// Just store audio data or stream it over the network ...
}
}


WaveOut -

class provides methods for playing out streaming data. The only thing you need to do is just call waveoutInstance.Play method.In my opinion, the whole example application has been coded well enough, so dig into the code.

Note: Sound quality depends on network delay jittering, if there will be too big a variance in delays, voice will have holes in it. In addition, UDP packet loss and disordered packets will affect it too.

Links:-

Examples

AdvancedWave.zip

BarCode.zip

DnsQuery_aspx.zip

DnsQuery_c#.zip

DnsQuery_SUN_java.zip

ftp_client_app.zip

imap_client_app.zip

IMAP_sync.zip

pop3_client_app.zip

SIP_Proxy_demo.zip

smtp_client_app.zip

SmtpStress.zip

STUN_Client_app.zip

vCardViewer.zip


License
This article has no explicit

Context Help Made Easy

Introduction
Most application developers have been through it. During the later stages of a project, you find yourself spending time working with an HTML Help author to connect the screens and dialogs of your application to context sensitive help.
This article introduces a way to instrument your code that enables help authors to associate help topics with the application's visual contexts at any time - even post-compilation – and to do so using the application's user interface without the involvement of the developer. There is no need for the help author to manually edit any files or have knowledge of any internals.





Background
Like my earlier articles [^], this one steps you through the code writing process, displaying code snippets for each change. I'm sure that readers will have many valuable comments, criticisms, and suggestions to offer. You are encouraged to do so in the discussion section at the bottom of the article. I will do my best to incorporate your thoughts in the next revision of the article - but even if I'm a lazy bum and don't get around to it, you will be sharing your perceptions and ideas with others in the community.


What is Context?
When you're running an application (as in life), you are always in multiple contexts at any given moment. For example, if the focus is in a particular TextBox control, the context can be all of the following: the TextBox, a TabPage containing the TextBox, the Tab control containing the TabPage, the dialog box containing the TabPage, and the application itself. It is up to the help author to decide which of these contexts should have an associated help topic.
The general approach many applications take when launching Context Sensitive Help is to start with the control with the focus and check to see if it has an associated help topic. If so, the application launches the topic, and if not, it moves outward (i.e., up the parent chain) to the containing control and repeats the process.


Implementing F1 Help
We'll start by implementing a system for displaying an associated topic when the F1 key is pressed, by using a ContextID to topic mapping that will be stored in an XML configuration file. After this is working, we'll get to adding the feature that enables help authors to edit these mappings from inside the application UI. Click on this link to skip to the fun part. :)


The IContextHelp Interface
The first step is to create an Interface (IContextHelp) that you can add to the various controls in your project. This interface identifies controls that are candidates for acting as a context for help, and provides a unique string identifier (ContextID) to identify the context.


public interface IContextHelp
{
string ContextHelpID { get; }
}


Since such controls are typically derived from UserControl, we can simplify things further by creating a UserControlEx that implements the interface. To use this, derive your user controls from UserControlEx instead of UserControl. In the default implementation, the ContextID for the control is the fully qualified class name of the control.


public virtual string ContextHelpID {get{return this.GetType().FullName;}}

Similarly, we create a FormEx that implements IContextHelp in the same way. When you define a form in your application, change it to derive from FormEx instead of Form.
If you want to provide context help for standard Windows or third party controls, you can do so easily by deriving new controls and adding the interface. For example:


// PanelEx is like a Panel, except that it implements IContextHelp
public class PanelEx : Panel, IContextHelp
{
private string m_sContextID;
public string ContextHelpID
{
get
{
if (string.IsNullOrEmpty(m_sContextID))
return this.Name;
return m_sContextID;
}
set { m_sContextID = value; }
}
}


In this case, we use the control's name as the default ID, and permit it to be overridden in case there is a name conflict. In practice, that hasn't been necessary for projects I've worked on.
If you need to provide a context ID for many different standard Windows or third party controls, you should consider implementing an IExtenderProvider as described by Maddy

[^] in his excellent comment[^] to the original version of this article.


Trapping the F1 Key
You can implement a single handler to trap and respond to the F1 key press throughout your application by adding a message filter to your application, and checking for F1 when the WM_KEYDOWN message is received.

static void Main()
{
//add message filter
MessageFilter oFilter = new MessageFilter();
System.Windows.Forms.Application.AddMessageFilter(
(IMessageFilter)oFilter);
// etc.
}
internal class MessageFilter : IMessageFilter
{
#region IMessageFilter Members
bool IMessageFilter.PreFilterMessage(ref Message m)
{
//Use a switch so we can trap other messages in the future.
switch (m.Msg)
{
case 0x100 : // WM_KEYDOWN
if ((int)m.WParam == (int)Keys.F1)
{
HelpUtility.ProcessHelpRequest(Control.FromHandle(m.HWnd));
return true;
}
break;
}
return false;
}
#endregion
}


Handling the F1 Key
Now that we've taken care of the infrastructure, it's time to show how to handle the F1 key. Later, we'll modify this method to test for the Control key, but for now it looks like this:

public static class HelpUtility
{
public static void ProcessHelpRequest(Control ctrContext)
{
ShowContextHelp(ctrContext);
}
}

The ShowContextHelp() method travels up the parent chain, looking for a control that:
implements IContextHelp,
has a non-empty IContextHelp.ContextHelpID, and
has a corresponding entry in the mapping XML file.
If one is found, it launches the help viewer to display it. If not, it launches the default topic.

// Process a request to display help
// for the context specified by ctrContext.
public static void ShowContextHelp(Control ctrContext)
{
Control ctr = ctrContext;

string sHTMLFileName = null;
while (ctr != null)
{
// Get the first control in the parent chain
// with the IContextHelp interface.
IContextHelp help = GetIContextHelpControl(ctr);
// If there isn't one, display the default help for the application.
if (help == null)
break;
// Check to see if it has a ContextHelpID value.
if (help.ContextHelpID != null)
{
// Check to see if the ID has a mapped HTML file name.
sHTMLFileName = LookupHTMLHelpPathFromID(help.ContextHelpID);
if (sHTMLFileName != null && ShowHelp(ctrContext, sHTMLFileName))
return;
}
// Get the parent control and repeat.
ctr = ((Control)help).Parent;
}
// Show the default topic.
ShowHelp(ctrContext, "");
}

The GetIContextHelpControl() method traverses up the parent chain, looking for an IContextHelp control.


// Get the first control in the parent chain
// (including the control passed in)
// that implements IContextHelp.
private static IContextHelp GetIContextHelpControl(Control ctl)
{
while (ctl != null)
{
IContextHelp help = ctl as IContextHelp;
if (help != null)
{
return help;
}
ctl = ctl.Parent;
}
return null;
}

The ShowHelp() method launches HTML help, displaying the topic specified by sHTMLHelp.

// Display the specified help page.
private static bool ShowHelp(Control ctlContext, string sHTMLHelp)
{
try
{
if (string.IsNullOrEmpty(sHTMLHelp))
Help.ShowHelp(ctlContext, HelpUtility.HelpFilePath);
else
Help.ShowHelp(ctlContext, HelpUtility.HelpFilePath,
HelpNavigator.Topic, sHTMLHelp);
}
catch (ArgumentException)
{
// Ideally, we would return false when
// the HTML file isn't found in the CHM file.
// Unfortunately, there doesn't seem to be
// a way to do this without parsing the CHM.
return false;
}
return true;
}
// Define this contstant at the top of the file.
private const string mc_sHELPFILE = "ContextHelpMadeEasy.chm";
// Return the path to the CHM file.
private static string HelpFilePath
{
get
{
return Path.Combine(System.Windows.Forms.Application.StartupPath,
mc_sHELPFILE);
}
}


Reading the Mapping File
To complete F1 help, we need to implement the LookupHTMLHelpPathFromID() function called from ShowContextHelp(). This method examines the ID to topic mappings, and returns the topic filename if a mapping exists. The mappings are read from the XML configuration file on first access, and cached to avoid reading the file every time a user presses F1. We start by defining a StringDictionary to hold the mapping cache. In addition, we define a number of constants which represent the mapping file name, and various XML elements and attribute names.

static private StringDictionary ms_sdContextPaths = null;
private const string mc_sMAPPING_FILE_NAME = "HelpContextMapping.Config";
private const string mc_sIDMAP_ELEMENT_NAME = "IDMap";
private const string mc_sCONTEXTID_ELEMENT_NAME = "ContextID";
private const string mc_sID_ATTRIBUTE_NAME = "ID";
private const string mc_sHTMLPATH_ATTRIBUTE_NAME = "HTMLPath";

The MappingFilePath property returns the path to the mapping file, which is assumed to be in the same directory as the application executable.

private static string MappingFilePath
{
get { return Path.Combine(Application.StartupPath, mc_sMAPPING_FILE_NAME); }
}
The ContextPaths property implements the cache by reading the mapping file only on the first call.
private static StringDictionary ContextPaths
{
get
{
if (ms_sdContextPaths == null)
{
ms_sdContextPaths = ReadMappingFile();
}
return ms_sdContextPaths;
}
}


The ReadMappingFile() method creates a StringDictionary and populates it with information read from the XML help mapping configuration file.

// Read the mapping file to create a list of ID to HTML file mappings.
private static StringDictionary ReadMappingFile()
{
StringDictionary sdMapping = new StringDictionary();
XmlDocument docMapping = new XmlDocument();
if (File.Exists(MappingFilePath) == true)
{
try { docMapping.Load(MappingFilePath); }
catch
{
MessageBox.Show(string.Format("Could not read help mapping file '{0}'.",
MappingFilePath), "Context Help Made Easy", MessageBoxButtons.OK,
MessageBoxIcon.Error);
throw;
}
XmlNodeList nlMappings = docMapping.SelectNodes("//" +
mc_sCONTEXTID_ELEMENT_NAME);
foreach (XmlElement el in nlMappings)
{
string sID = el.GetAttribute(mc_sID_ATTRIBUTE_NAME);
string sPath = el.GetAttribute(mc_sHTMLPATH_ATTRIBUTE_NAME);
if (sID != "" && sPath != "")
sdMapping.Add(sID, sPath);
}
}
return sdMapping;
}


A mapping file looks something like this:

With the cache implemented, the LookupHTMLHelpPathFromID() implementation becomes trivial.


// Given an ID, return the associated HTML Help path
private static string LookupHTMLHelpPathFromID(string sContextID)
{
if (ContextPaths.ContainsKey(sContextID))
return ContextPaths[sContextID];
return null;
}


Finally! We're done with the F1 implementation, and we can move on to the really cool part - instrumenting your code to allow help authors implement context sensitive help without your involvement.


Instrumenting the Code - Empowering the Help Author
We now have almost everything we need to disentangle the work of the help author with the work of the developer. With the code as described, the author could manually modify the XML configuration file to add and modify help mappings – if they knew the ContextIDs for each application contexts which they want to provide help for.
The idea here is that with the infrastructure already developed, we can turn the application itself into a context sensitive editor for the configuration file. We'll modify the code so that when a HTML help author navigates to some screen, he or she can click Ctrl-F1 and get a dialog that displays all the available ContextIDs for that screen. The HTML help author can then add or remove a ContextID to HTML filename associations using the dialog, with the resulting association written to the help mapping configuration file. Immediately after clicking OK, they can press F1 and see the context sensitive help appear.


Trapping Ctrl-F1
We can modify the ProcessHelpRequest() method described above by adding a check for the Control key as follows:


public static void ProcessHelpRequest(Control ctrContext)
{
if (Control.ModifierKeys == Keys.Control)
{
ShowHelpMappingDialog(ctrContext);
return;
}
ShowContextHelp(ctrContext);
}


In practice, you will probably want to put in an additional test to prevent end-users from getting this dialog if they accidentally hit Ctrl-F1. In my implementation, the test for the control key is ANDed with a registry check for a key value that enables this feature. You can, of course, check the application configuration file or anything else that could uniquely identify a help author.


Displaying the Mapping Dialog
Now, if the Control key is pressed when the WM_KEYDOWN message is handled, instead of displaying help for the specified control, we call the ShowHelpMappingDialog() method.

// Traverse the parent control chain looking for controls that implement the
// IContextHelp interface. For each one found, add it to the list of available
// contexts. Include the associated HTML path if it's define
// Finally, show the dialog for the help author to edit the mappings.
public static void ShowHelpMappingDialog(Control ctrContext)
{
IContextHelp help = GetIContextHelpControl(ctrContext);
List alContextPaths = new List();
// Create a list of contexts starting with the current help context
// and moving up the parent chain.
while (help != null)
{
string sContextID = help.ContextHelpID;
if (sContextID != null)
{
string sHTMLHelpPath = LookupHTMLHelpPathFromID(sContextID);
alContextPaths.Add(new ContextIDHTMLPathMap(sContextID, sHTMLHelpPath));
}
help = GetIContextHelpControl(((Control)help).Parent);
}
// Pop up the mapping dialog. If it returns true, this means a change was made
// so we rewrite the XML mapping file with the new information.
if (FHelpMappingDialog.ShowHelpWriterHelper(alContextPaths) == true)
{
foreach (ContextIDHTMLPathMap pathMap in alContextPaths)
{
if (!string.IsNullOrEmpty(pathMap.ContextID))
{
if (!string.IsNullOrEmpty(pathMap.HTMLPath))
{
ContextPaths[pathMap.ContextID] = pathMap.HTMLPath;
}
else
{
if (ContextPaths.ContainsKey(pathMap.ContextID))
ContextPaths.Remove(pathMap.ContextID);
}
}
}
SaveMappingFile(ContextPaths);
}
}
}


The mapping dialog opened by calling FHelpMappingDialog.ShowHelpWriterHelper() is discussed below. It basically acts as an editor for the list of ContextIDHTMLPathMap structures.
The ContextIDHTMLPathMap structure containing two strings and a constructor:

// Utility class for maintaining relationship between context Id and path.
public class ContextIDHTMLPathMap
{
public string ContextID;
public string HTMLPath;
public ContextIDHTMLPathMap(string ID, string Path)
{
ContextID = ID;
HTMLPath = Path;
}
}


Writing the Results
Since we'd like our changes to be written immediately to the configuration file, we implement this as a write-through cache. The SaveMappingFile() method called from ShowHelpMappingDialog() takes care of this:

// Saves the specified StringDictionary that contains ID to Path mappings to the
// XML mapping file.
private static void SaveMappingFile(StringDictionary sdMappings)
{
// Create a new XML document and initialize it with the XML declaration and the
// outer IDMap element.
XmlDocument docMapping = new XmlDocument();
XmlDeclaration xmlDecl = docMapping.CreateXmlDeclaration("1.0", null, null);
docMapping.InsertBefore(xmlDecl, docMapping.DocumentElement);
XmlElement elIDMap = AddChildElementToNode(docMapping, docMapping,
mc_sIDMAP_ELEMENT_NAME);
// Add the defined mappings between contextID and filename.
foreach (DictionaryEntry de in sdMappings)
{
XmlElement elMapping = AddChildElementToNode(elIDMap, docMapping,
mc_sCONTEXTID_ELEMENT_NAME);
elMapping.SetAttribute(mc_sID_ATTRIBUTE_NAME, de.Key as string);
elMapping.SetAttribute(mc_sHTMLPATH_ATTRIBUTE_NAME, de.Value as string);
}
try
{
docMapping.Save(MappingFilePath);
}
catch
{
MessageBox.Show(string.Format("Could not write help mapping file '{0}'",
MappingFilePath), "Context Help Made Easy", MessageBoxButtons.OK,
MessageBoxIcon.Error);
throw;
}
}


The AddChildElementToNode() utility function makes the code a bit more readable:


// Small utility method to add XML elements to a parent node.
private static XmlElement AddChildElementToNode(XmlNode node,
XmlDocument doc, string elementName)
{
XmlElement el = doc.CreateElement(elementName);
node.AppendChild(el);
return el;
}

The Mapping Dialog
The mapping dialog consists of a ListView control, and buttons for editing the topic file name, OK, and Cancel. It is an editor of the list of ContextIDHTMLPathMap structures that contain the current control's contexts. The dialog populates the ListView control, permits editing of the HTML File names in it, and writes the results back to the List.
The entry point for the dialog is the static method ShowHelpWriterHelper() that instantiates the form and initializes its data.

// Static entry point to pop up this form.
public static bool
ShowHelpWriterHelper(List contextIDs)
{
FHelpMappingDialog frmHelper = new FHelpMappingDialog();
frmHelper.IDList = contextIDs; // Populate the treelist.
if( frmHelper.lvMapping.Items.Count > 0 )
frmHelper.lvMapping.SelectedIndices.Add(0);
frmHelper.ShowDialog(); // Popup the form.
if (frmHelper.Changed)
{
// For each item in the ListView,
// change the path map to correspond to the UI.
foreach (ListViewItem lvi in frmHelper.lvMapping.Items)
{
ContextIDHTMLPathMap pathMap = (ContextIDHTMLPathMap)lvi.Tag;
pathMap.HTMLPath = (string)lvi.SubItems[0].Text.Trim();
}
}
return frmHelper.Changed;
}

The ListView is populated in the IDList property set method:
// Gets and sets the list of ids. The setter updates the UI.
public List IDList
{
set
{
lvMapping.Items.Clear();
foreach (ContextIDHTMLPathMap pathMap in value)
{
AddMappingNode(pathMap);
}
}
}


// Utility to add a node to the treelist.
private void AddMappingNode(ContextIDHTMLPathMap pathMap)
{
ListViewItem lvi = new ListViewItem(pathMap.HTMLPath);
lvi.SubItems.Add(pathMap.ContextID);
lvi.Tag = pathMap;
lvMapping.Items.Add(lvi);
}


Other methods in the dialog are what you'd probably expect:

// Begin editing the label of the selected
// item when they click this button
private void btnEditTopicFile_Click(object sender, EventArgs e)
{
if( lvMapping.SelectedItems.Count == 1 )
{
ListViewItem lvi = lvMapping.SelectedItems[0];
lvi.BeginEdit();
}
}

// If the item has changed after editing
// the label, flag the dialog as changed.
private void lvMapping_AfterLabelEdit(object sender,
LabelEditEventArgs e)
{
this.Changed = true;
};
}

The details of implementing the dialog can be found in the source attached to this article, but this should give you the basic idea.


Finishing Touches
We now have a functioning help system that can easily be configured post-compilation by a help author. This section describes a few additional features to give you a sense of how you can extend the system.


Handling the "?" Dialog Help Button
When you have a dialog with an associated ContextID (e.g., one derived from FormEx), you may want the user to be able to click on the "?" help button to bring up the HTML help for that context. We'll modify FormEx so that all dialogs have this behavior. Set the following properties in the FormEx designer to get the help button to appear:

HelpButton = true;
MaximizeBox = false;
MinimizeBox = false;


Now we need to override the OnHelpButtonClicked() method to call our help handler and cancel the default behavior of changing the cursor.
protected override void OnHelpButtonClicked(CancelEventArgs e)

{
HelpUtility.ProcessHelpRequest(this);
base.OnHelpButtonClicked(e);
e.Cancel = true;
}


We're done :).


Handling Tab Controls
With a tab (or similar) control, you may want to change the default behavior of the help system. For example, you'll probably want the ContextID returned when a tab is focused to be the ContextID of the control contained on the TabPage. To do this, we just override the ContextID property of the form that contains the tab control.
public override string ContextHelpID
{
get
{
switch (this.tcSettings.SelectedIndex)
{
case 0: return settingsGeneral1.ContextHelpID;
case 1: return settingsConfiguration1.ContextHelpID;
}
return base.ContextHelpID;
}
}


The Source Code and Sample Application
The attached sample application contains the source code for a fully instrumented application illustrating the principles from this article. It also contains a help file "ContextHelpMadeEasy.chm" that you can use to test the application. This file should be located in the same directory as the built executable. The topic file names in ContextHelpMadeEasy.chm are:
Configuration_Settings.htm
Configuration_Settings.htm
Context_Help_Made_Easy_Test_Application.htm
General_Settings.htm
Settings.htm
Topic_A.htm
Topic_B.htm
Topic_C.htm
Topic_D.htm
UI_1.htm
UI_2.htm
UI_1_Left_Side.htm
UI_1_Right_Side.htm
UI_2.htm

To see how it works, run the sample application and navigate to any of the application screens using the File and View menus. Place your cursor in some control and then, while holding down the Control key, click F1. The help author's dialog will appear, which you can use to associate any of these topics with the current context.

Contents

!~ Applications
!~ Attributes
!~ Beginners
!~ Samples Utilities
!~ COM Interop
!~ Date / Time
!~ Delegates and Events
!~ Enumerations
!~ General
!~ Generics
!~ How To
!~ Memory Management
!~ PInvoke
!~ Reflection
!~ Libraries - Reporting
!~ Samples
!~ Utilities
!~ Windows Forms


********************************************************************
Contact Us >>>>> Join Mad Engineerss...!
********************************************************************