Embed Custom GUIs in WPF FlowDocuments
by Eric Bergman-Terrell


Listing One

// Convert UserControls into PlaceholderControls in the xaml markup.
public static string ReplaceControlsWithPlaceholders(string xaml, 
                              TextPointer start, TextPointer end)
{
  string modifiedMarkup = xaml;
  // Get a list of UserControl Name attribute values in the 
  // order in which they appear in the document.
  List<string> userControlNames = GetUserControlNames(start, end);
  // Search for each Name... 
  foreach (string name in userControlNames)
  {
    foreach (string typeName in ControlTypeNames)
    {
      string enhancedUserControl = 
    string.Format(@"<{0} .*?Name=""{1}"".*?</{0}>", typeName, name);
      string placeHolderControl = 
         string.Format(@"<wpfcd:PlaceholderControl 
             Name=""{0}"" Visibility=""Hidden""/>", 
      Regex enhancedUserControlRegex = new Regex(enhancedUserControl);
      // Replace the UserControl with a Placeholder control having 
      // the same Name as the UserControl.
      modifiedMarkup = enhancedUserControlRegex.Replace(
            modifiedMarkup, placeHolderControl);
    }
  }
  return modifiedMarkup;
}
// Convert all PlaceholderControls in the document with
// the UserControls that they represent.
public static void ReplacePlaceholdersWithRealControls(
        FlowDocument document, RichTextBox richTextBox)
{
  int replacements = 0;
  TextPointer current = document.ContentStart;
  // Scan through the entire document...
  while (current.CompareTo(document.ContentEnd) < 0)
  {
    // UserControls will be nested in BlockUIContainer
    // or InlineUIContainer XAML elements.
    BlockUIContainer blockUIContainer = current.Parent as 
    InlineUIContainer inlineUIContainer = current.Parent as 
    // If we found a BlockUIContainer or InlineUIContainer...
    if (blockUIContainer != null || inlineUIContainer != null)
    {
      PlaceholderControl placeHolderControl;
      if (blockUIContainer != null)
      {
        placeHolderControl = 
           blockUIContainer.Child as PlaceholderControl;
      }
      else
      {
        placeHolderControl = inlineUIContainer.Child as 
      }
      // If we found a PlaceholderControl...
      if (placeHolderControl != null)
      {
        // Determine the type of the UserControl that the 
        // PlaceholderControl represents.
        Type controlType = 
      EnhancedUserControlUtils.GetType(placeHolderControl.Name);
        // Create a new UserControl of the proper type.
        IEnhancedUserControl newControl = 
        EnhancedUserControlUtils.ControlFactory(controlType);
        // Change the name of the new control so that it won't
        // overwrite another control's data.
        newControl.Name = 
        EnhancedUserControlUtils.NameFromGuid(Guid.NewGuid());
        // Retrieve the UserControl's state data.
        ICloneable dataToPersist = 
            (ICloneable)EnhancedUserControlUtils.
                       Load(placeHolderControl.Name);
        // Clone the state data so that the new UserControl
        //  won't overwrite another UserControl's data.
        ICloneable newObject = (ICloneable)dataToPersist.Clone();
        // Save the new UserControl's type and state.
        Globals.CompoundDocument.PersitedTypes[newControl.Name] = 
        Globals.CompoundDocument.PersitedValues[newControl.Name] = 
        // Load the new UserControl with its state data.
        ((IEnhancedUserControl)newControl).Load(newObject);
        // Nest the new UserControl inside the BlockUIContainer
        // or InlineUIContainer.
        if (blockUIContainer != null)
        {
          blockUIContainer.Child = (UserControl)newControl;
        }
        else if (inlineUIContainer != null)
        {
          inlineUIContainer.Child = (UserControl)newControl;
        }
        replacements++;
      }
    }
   current=current.GetNextContextPosition(LogicalDirection.Forward);
  }
  ...
}


Listing Two
// Get control names of all UserControls in the specified document 
// region.
public static List<string> GetUserControlNames(TextPointer start, 
                                               TextPointer end)
{
  List<string> userControlNames = new List<string>();
  TextPointer current = start;
  // Scan through the document fragment...
  while (current.CompareTo(end) < 0)
  {
    // If we encounter something that could potentially
    // contain a UserControl...
    if (current.Parent is BlockUIContainer || 
        current.Parent is InlineUIContainer)
    {
      // Get the BlockUIContainer or InlilneUIContainer's  full 
      // XAML markup.
      string containerMarkup = XamlWriter.Save(current.Parent);
      XmlDocument xmlDocument = new XmlDocument();
      xmlDocument.LoadXml(containerMarkup);

      // Extract the Name attribute from the XAML markup.
      XmlAttribute nameAttribute = 
          xmlDocument.DocumentElement.FirstChild.Attributes["Name"];
      string name = null;
      if (nameAttribute != null && 
          !string.IsNullOrEmpty(nameAttribute.Value))
      {
        name = nameAttribute.Value;
      }
      else
      {
        Debug.Assert(false);
      }
      // Store the UserControl's name in the List, avoiding 
      // duplicates.
      if (!userControlNames.Contains(name))
      {
        userControlNames.Add(name);
      }
    }
    current = 
        current.GetNextContextPosition(LogicalDirection.Forward);
  }
  return userControlNames;
}



3


