This chapter shows you how to retrieve all properties of a video capture device using VCD properties.
All properties that are retrieved from the video capture device are displayed in a TreeView. The TreeView was chosen, because it perfectly matches the organization of the properties, since they establish a tree structure. The root of the tree is the VCDPropertyItems collection. The collection contains all VCDPropertyItems that are provided by the current video capture device. Every VCDPropertyItem has one or more VCDPropertyElements, which have one or more VCDPropertyInterfaces that are used to access the property.
The source code for this sample program can be found under samples\C# *\List VCDProperties in your My Documents/IC Imaging Control 3.5 directory.
Create a new project and add IC Imaging Control to the form. Before you run the program, select the video device, input and video format as shown in the First Steps Visual Studio .NET 2010 chapter. Alternatively, run the program without selecting a device. In this case, the program shows the device selection dialog provided by IC Imaging Control.
Add 2 buttons to the form, name them btnSelectDevice and btnShowPage and label them Select Device and Show Property Page. They will be used to show the device settings resp. property page dialog provided by IC Imaging Control. Now add one instance of every user control to the form. Name them btnPush, cboMapStrings, chkSwitch, sldAbsVal and sldRange accordingly. Each of the user controls represents a special type of interface derived from VCDPropertyInterface.
Now add an image list and a tree view to the form. The image list will contain the graphical elements that are used to visualize the tree. Name the tree view Tree. In order to add graphical elements to the image list, click the ... button in the property window of the image list under Images. Now add the following images in the given order: item.bmp, value.bmp, auto.bmp, button.bmp, slider.bmp, switch.bmp and combo.bmp. All these bitmap files can be found in the directory of this sample. Change the Modifiers property of the image list to Friend (or internal if you use C#). Now, the image list can be added to the tree view. To do so, set the property ImageList of the tree view to the name of the image list. This will bind the image list to the tree view.
Add the Click events for the Select Device and Show Property Page buttons and insert the following code:
[C#]
private void btnSelectDevice_Click(object sender, EventArgs e) { // The device settings dialog needs the live mode to be stopped if (icImagingControl1.LiveVideoRunning) { icImagingControl1.LiveStop(); } // Show the device settings dialog icImagingControl1.ShowDeviceSettingsDialog(); // If no device was selected, exit if (!icImagingControl1.DeviceValid) { MessageBox.Show("No device was selected."); this.Close(); return; } UpdateStateAfterDeviceSelect(); } private void UpdateStateAfterDeviceSelect() { // If no device was selected, exit // Print all properties into the debug window ListAllPropertyItems(); // Start live mode icImagingControl1.LiveStart(); // (re-)initialize the tree view BuildVCDPropertiesTree(); }
[C#]
private void btnShowPage_Click(object sender, EventArgs e) { if (icImagingControl1.DeviceValid) { // Show the built-in properties dialog icImagingControl1.ShowPropertyDialog(); } }
Now add a Form_Load event and insert the code below:
[C#]
private void Form1_Load(object sender, EventArgs e) { if( !icImagingControl1.LoadShowSaveDeviceState("lastSelectedDeviceState.xml") ) { MessageBox.Show("No device was selected."); this.Close(); return; } UpdateStateAfterDeviceSelect(); }
As you can see in the code above, the btnSelectDevice_Click and the Form_Load event both call the procedures ListAllPropertyItems and QueryVCDProperties. QueryVCDProperties fills the tree view with the properties of the current video capture device. Additionally, ListAllPropertyItems prints the tree structure of those properties to the debug output window. The debug output shows you the pure organization of the VCDProperties without taking care of other aspects like controls. Let's have a look at the code for the procedure ListAllPropertyItems:
[C#]
private void ListAllPropertyItems() { // Get all property items foreach (TIS.Imaging.VCDPropertyItem PropertyItem in icImagingControl1.VCDPropertyItems) { System.Diagnostics.Debug.WriteLine(PropertyItem.Name); // Get all property elements of the current property item. foreach (TIS.Imaging.VCDPropertyElement PropertyElement in PropertyItem.Elements) { System.Diagnostics.Debug.WriteLine(" Element : " + PropertyElement.Name); // Get all interfaces of the current property element. foreach (TIS.Imaging.VCDPropertyInterface PropertyInterFace in PropertyElement) { System.Diagnostics.Debug.Write(" Interface "); try { // Cast the current interface into the appropriate type to access // the special interface properties. if( PropertyInterFace.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_AbsoluteValue ) { var AbsoluteValue = (TIS.Imaging.VCDAbsoluteValueProperty)PropertyInterFace; System.Diagnostics.Debug.Write("Absolute Value : "); System.Diagnostics.Debug.WriteLine(AbsoluteValue.Value.ToString()); } else if( PropertyInterFace.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_MapStrings ) { var MapString = (TIS.Imaging.VCDMapStringsProperty)PropertyInterFace; System.Diagnostics.Debug.Write("Mapstring : "); System.Diagnostics.Debug.WriteLine(MapString.String); } else if( PropertyInterFace.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Switch ) { var Switch = (TIS.Imaging.VCDSwitchProperty)PropertyInterFace; System.Diagnostics.Debug.Write("Switch : "); System.Diagnostics.Debug.WriteLine(Switch.Switch.ToString()); } else if( PropertyInterFace.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Button ) { var Button = (TIS.Imaging.VCDButtonProperty)PropertyInterFace; System.Diagnostics.Debug.WriteLine("Button"); } else if( PropertyInterFace.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Range ) { var Range = (TIS.Imaging.VCDRangeProperty)PropertyInterFace; System.Diagnostics.Debug.Write("Range : "); System.Diagnostics.Debug.WriteLine(Range.Value.ToString()); } } catch( Exception ex ) { System.Diagnostics.Debug.WriteLine("<error>:" + ex); } } } } }
The three nested For Each loops in the code above reflects the tree structure of the VCDProperties. The first loop covers all available VCDPropertyItems and prints their names to the debug output. For each item, the second loop prints out all available VCDPropertyElements. The third loop prints out all types of interfaces that the element provides. Please note that the interfaces are sorted according to their extend of detail. The most detailed one is the "AbsoluteValue" interface, followed by the "MapStrings" interface. "Switch" and "Button" are detailed interfaces providing simple functionality: toggle On/Off, trigger. The "Range" interface is the most generic one, but does not provide a meaning for the values that represents. For example the "Brightness" range may be from 0 to 255. There is no way to tell, whether 0 or 255 is the maximum brightness value.
Now let's have a look at the BuildVCDPropertiesTree procedure. This procedure fills the tree view with appropriate values. The code for this procedure looks as follows:
[C#]
private void BuildVCDPropertiesTree() { // Erase the complete tree. Tree.Nodes.Clear(); // Fill the tree. TreeNode root = new TreeNode("VCDPropertyItems"); Tree.Nodes.Add(root); BuildVCDPropertiesTreeItems(root, icImagingControl1.VCDPropertyItems); root.ExpandAll(); Tree.SelectedNode = root; }
The procedure above clears the tree view and creates a new root node. The root node is labeled VCDPropertyItems. Then the procedure QueryVCDPropertyItems inserts all available property items. This procedure looks as follows:
[C#]
private void BuildVCDPropertiesTreeItems(TreeNode pp, TIS.Imaging.VCDPropertyItems props) { // Iterate through all VCDPropertyItems and insert them into the tree foreach (TIS.Imaging.VCDPropertyItem item in props) { // Create a new tree node for the item TreeNode newNode = new TreeNode(item.Name, 0, 0); pp.Nodes.Add(newNode); QueryVCDPropertyElements(newNode, item); } }
BuildVCDPropertiesTreeItems iterates through all available property items. For every item, it creates a new node. All of these nodes are direct children of the root node. The name of the VCDPropertyItem (e.g. "Brightness", "Gain" or "Exposure") is assigned to the Text property of the node. Nothing (or null if you use C#) is assigned to the tag property of the node. Then, QueryVCDPropertyElements is called for the new node. QueryVCDPropertyElements looks as follows:
[C#]
private void QueryVCDPropertyElements(TreeNode pp, TIS.Imaging.VCDPropertyItem item) { foreach (TIS.Imaging.VCDPropertyElement elem in item.Elements) { TreeNode newNode = null; if( elem.ElementGUID == TIS.Imaging.VCDGUIDs.VCDElement_Value ) newNode = new TreeNode("VCDElement_Value: '" + elem.Name + "'", 1, 1); else if ( elem.ElementGUID == TIS.Imaging.VCDGUIDs.VCDElement_Auto ) newNode = new TreeNode("VCDElement_Auto: '" + elem.Name + "'", 2, 2); else if ( elem.ElementGUID == TIS.Imaging.VCDGUIDs.VCDElement_OnePush ) newNode = new TreeNode("VCDElement_OnePush: '" + elem.Name + "'", 3, 3); else if ( elem.ElementGUID == TIS.Imaging.VCDGUIDs.VCDElement_WhiteBalanceRed ) newNode = new TreeNode("VCDElement_WhiteBalanceRed: '" + elem.Name + "'", 4, 4); else if ( elem.ElementGUID == TIS.Imaging.VCDGUIDs.VCDElement_WhiteBalanceBlue ) newNode = new TreeNode("VCDElement_WhiteBalanceBlue: '" + elem.Name + "'", 4, 4); else newNode = new TreeNode("Other Element ID: '" + elem.Name + "'", 4, 4); pp.Nodes.Add(newNode); // Insert all interfaces QueryVCDPropertyInterface(newNode, elem); } }
QueryVCDPropertyElements iterates through all available elements. For every element, it creates a new node. All of these nodes are children of the node, which represents the VCDPropertyItem to which these VCDPropertyElements belong. The Select block (or switch block in C#) determines the appropriate graphical tree element for the node and sets its base name, depending on the type of the element. The name of the VCDPropertyElement (e.g. "Value", "Auto" or "Enable") is added to the base name and then assigned to the Text property of the node. Nothing (or null, if you use C#) is assigned to the tag property of the node. Then, QueryVCDPropertyInterface is called for the new node. QueryVCDPropertyInterface looks as follows:
[C#]
private void QueryVCDPropertyInterface(TreeNode pp, TIS.Imaging.VCDPropertyElement elem) { foreach (TIS.Imaging.VCDPropertyInterface itf in elem) { TreeNode newNode; if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_AbsoluteValue ) newNode = new TreeNode("AbsoluteValue", 4, 4); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_MapStrings ) newNode = new TreeNode("MapStrings", 6, 6); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Range ) newNode = new TreeNode("Range", 4, 4); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Switch ) newNode = new TreeNode("Switch", 5, 5); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Button ) newNode = new TreeNode("Button", 3, 3); else { newNode = new TreeNode(itf.InterfaceGUID.ToString(), 4, 4); } // The Tag property holds the interface at the node. newNode.Tag = itf; pp.Nodes.Add(newNode); } }
QueryVCDPropertyInterface iterates through all available interfaces of an element. For every interface, it creates a new node. All of these nodes are children of the node which represents the VCDPropertyElement to which these VCDPropertyInterface belong. The Select block (or switch block in C#) determines the appropriate graphical tree element for the node and sets its name, depending on the type of the element. The name is then assigned to the Text property of the node. The interface path that consists of an itemID, an elementID and an interfaceID is assigned to the tag property of the node. The interface node makes up a leaf of the tree.
Now that the tree is built up, we want to access the properties. To do this, add an AfterSelect event for the tree view and insert the following code:
[C#]
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { // If the Tag property is empty, no leaf node was selected. if (Tree.SelectedNode.Tag == null) { return; } // Hide all controls foreach( var ctrl in _currentControls ) { ctrl.Dispose(); } _currentControls.Clear(); TIS.Imaging.VCDPropertyInterface itf = Tree.SelectedNode.Tag as TIS.Imaging.VCDPropertyInterface; if (itf != null) { itf.Update(); // Show the control group matching the type of the selected interface // and initialize it. if ( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_AbsoluteValue ) ShowAbsoluteValueControl(itf); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_MapStrings) ShowComboBoxControl(itf); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Range) ShowRangeControl(itf); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Switch) ShowSwitchControl(itf); else if( itf.InterfaceGUID == TIS.Imaging.VCDGUIDs.VCDInterface_Button) ShowButtonControl(itf); } }
Now it becomes clear, why the tags of the interface nodes store the interface path. If you click an interface node, the appropriate control should appear on the form, so that you can manipulate the setting of the property. To get the correct interface, the method FindInterface is used. FindInterface needs the interface path (or GUID) as a parameter. The tag of an interface node represents this interface path (or GUID). The Select block (or switch in C#) maps the appropriate user control to the desired interface and calls the respective "Show%%" procedure. These procedures set the "Visible" property of the user control to True, so that it can be used to manipulate the setting of the specified property. The ShowAbsoluteValueControl procedure for example is implemented as follows:
[C#]
private void ShowAbsoluteValueControl(TIS.Imaging.VCDPropertyInterface itf) { var newCtrl = new AbsValSlider((TIS.Imaging.VCDAbsoluteValueProperty)itf); CtrlFrame.Controls.Add(newCtrl); newCtrl.SetBounds(20, 20, 500, 27); CtrlFrame.Text = "Absolute Value"; _currentControls.Add(newCtrl); }
The appropriate procedures for the other user controls are implemented in the same way, except the last line of code of the sample, which is left out.