Switch to the design view of MyControl.cs. Replace the existing button1_Click handler function by using the following code.
This code creates a new ToDoItem instance and passes the user control instance as a parameter together with the text that the user entered in the TextBox control. Next, the code adds the item to the ListBox. (The ListBox will call the ToString method of the ToDoItem instance to retrieve the string to display in the ListBox.) Next, the code calls the TrackSelection function, which you will write in a later step. Finally, the code checks for errors.
Switch back to the design view of MyControl.cs to add the code that handles user selection of a new item in the ListBox.
Click the ListBox control. In the Properties window, double-click the SelectedIndexChanged event. Doing this adds a stub for a SelectedIndexChanged handler and assigns it to the event.
Fill in the SelectedIndexChanged handler as follows, and stub in the method it calls.
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
TrackSelection();
}
private void TrackSelection()
{
}
Save your work. You can build your project and look for typos.
Now, fill in the TrackSelection function, which will provide integration with the Properties window. This function is called when the user adds an item to the ListBox or clicks an item in the ListBox.
Now that you have a class that the Properties window can use, you can integrate the Properties window with the tool window. When the user clicks an item in the ListBox in the tool window, the Properties window should be updated accordingly. Similarly, when the user changes a ToDo item in the Properties window, the associated item should be updated.
Put the code for updating the Properties window in the TrackSelection function. Doing this will tie the ToDoItem object to the Properties window; you do not have to write any additional code to modify the ToDoItem when the user changes a value in the Properties window. The Properties window will automatically call the set property accessors to update the values. However, you must finish the UpdateList method that you created when you wrote the code for the ToDoItem class.
Add the following namespace declarations to the top of the MyControl.cs file, after the existing using/imports statements.
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
Implement the TrackSelection function as follows.
private SelectionContainer mySelContainer;
private System.Collections.ArrayList mySelItems;
private IVsWindowFrame frame = null;
private void TrackSelection()
{
if (frame == null)
{
var shell = GetService(typeof(SVsUIShell)) as IVsUIShell;
if (shell != null)
{
var guidPropertyBrowser = new
Guid(ToolWindowGuids.PropertyBrowser);
shell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fForceCreate,
ref guidPropertyBrowser, out frame);
}
}
if (frame != null)
{
frame.Show();
}
if (mySelContainer == null)
{
mySelContainer = new SelectionContainer();
}
mySelItems = new System.Collections.ArrayList();
var selected = listBox1.SelectedItem as ToDoItem;
if (selected != null)
{
mySelItems.Add(selected);
}
mySelContainer.SelectedObjects = mySelItems;
var track = GetService(typeof(STrackSelection))
as ITrackSelection;
if (track != null)
{
track.OnSelectChange(mySelContainer);
}
}
Add the following code just after the closing brace at the end of the TrackSelection function.
protected override object GetService(Type service)
{
object obj = null;
if (_parent != null)
{
obj = _parent.GetVsService(service);
}
if (obj == null)
{
obj = base.GetService(service);
}
return obj;
}
This code calls the GetService function. This function first tries to obtain the service from the parent tool window by calling its GetService function. If that fails, it tries to obtain it from the GetService function of the object. Because the GetService function in the parent tool window is not public, the code calls GetVsService instead. You must add the GetVsService function.
Open MyToolWindow.cs. Add the following code to the end of the class, just before the final two closing braces in the file.
Save MyToolWindow.cs.
The first time the TrackSelection function runs, it calls GetService to obtain an instance of the Visual Studio shell. It then uses that instance to obtain an object for the Properties window. To get the Properties window object, the code starts by using the GUID that represents the Properties window. (The GUIDs for the tool windows are members of the ToolWindowGuids80 class.) The code then calls the FindToolWindow function of the shell, by passing the GUID, to get the Properties window object. Doing this saves it in the frame variable so that when the function is called again, this process of obtaining the Properties window does not have to be repeated.
Next, the method calls the Show method of the frame variable to display the Properties window.
The next code gathers the selected items in the ListBox. The ListBox is not configured to enable multiple selection. To pass the selected item to the Properties window, you must use a container. Therefore, the code gathers the selected item and puts it in an ArrayList, and then puts that ArrayList in a container of type SelectionContainer.
Next, the code calls GetService to obtain an instance of ITrackSelection, which is the Visual Studio object that tracks selected objects in the user interface (UI) and displays their properties. Then the code directly calls the ITrackSelection OnSelectChange event handler, and passes the SelectionContainer that is holding the selected item. The result is that the Properties window displays the properties for the selected item.
When the user changes a ToDoItem object in the Properties window, the Properties window automatically calls the set accessor functions in the ToDoItem object. That updates the object, but you still have to update the ListBox.
In an earlier step, you added code in the set accessor functions to call an UpdateList function in MyControl.cs. Now, add the rest of the UpdateList function code.
Switch back to MyControl.cs.
Implement the UpdateList method as follows.
public void UpdateList(ToDoItem item)
{
var index = listBox1.SelectedIndex;
listBox1.Items.RemoveAt(index);
listBox1.Items.Insert(index, item);
listBox1.SetSelected(index, true);
}
This code determines which item is selected and will correspond to the ToDoItem that is being modified. The code removes the item from the ListBox, and then re-inserts it. Doing this updates the line in the ListBox for the item. Then the code sets the selection back to the same item.
Save your work.