To solve the problem I needed to create a custom tool part that displays a text box to represent the title of the web part:
public class CustomToolPart : ToolPart
{
...
}
So I don't show the title twice, I hid the original Title property:
public override ToolPart[] GetToolParts()
{
...
WebPartToolPart webPartToolPart = new WebPartToolPart();
webPartToolPart.Hide(WebPartToolPart.Properties.Title);
...
return toolparts;
}
In CreateChildControls for CustomToolPart I accessed my web part by calling:
MyWebPart webPart = (MyWebPart)ParentToolPane.SelectedWebPart;
In my web part, I added a hidden property called "enteredTitle":
public class MyWebPart : WebPart
{
...
/// <summary>
/// Retains a copy of the string a user types into the title property of the toolpart before it gets modified
/// by code in the CreateChildControls() method of this class.
/// </summary>
[WebBrowsable(false)]
[WebPartStorage(Storage.Shared)]
public string enteredTitle { get; set; }
...
}
I then set the entered title to the newly created hidden property in the ApplyChanges() of CustomToolPart:
public class CustomToolPart : ToolPart
{
...
public override void ApplyChanges()
{
if (changed)
{
webPart.enteredTitle = textBox.Text;
}
}
...
}
When CreateChildControls() is called in MyWebPart I can write this.Title = this.enteredTitle to immediately display the updated title in the refreshed page.
This technique makes it easy to have control of the title at any stage the page lifecycle. This is especially useful when translations come into play; for example, when I want to manipulate the Title to display a string from a resource when the user types in a title beginning with the string $Resources:
public class MyWebPart : WebPart
{
...
if (string.IsNullOrEmpty(this.enteredTitle))
this.Title = base.Title;
else if (enteredTitle.StartsWith("$Resources"))
{
try
{
this.Title = Tools.TranslateResourceString(enteredTitle);
if (string.IsNullOrEmpty(this.Title))
this.Title = enteredTitle;
}
catch
{
this.Title = enteredTitle;
}
}
else
this.Title = enteredTitle;
...
}
/// <summary>
/// Translates a string in the form of $Resources:File,Key to grab a value from a resource string
/// stored within SharePoint.
/// </summary>
/// <param name="resourceReferenceString">The string to translate.</param>
/// <returns>Returns the value referenced by the resource string.</returns>
public static string TranslateResourceString(string resourceReferenceString)
{
string s= resourceReferenceString.Substring(11);
string[] args = s.Split(",".ToCharArray(), 2, System.StringSplitOptions.RemoveEmptyEntries);
string newString = (string)HttpContext.GetGlobalResourceObject(args[0], args[1], Thread.CurrentThread.CurrentUICulture);
return newString;
}