Pages

Wednesday, March 25, 2009

Microsoft CRM: Embedding Advanced Find Views in Entity Forms (Version 4)

[UPDATE: See Version 5 of the code in this post at http://crmentropy.blogspot.com/2009/04/microsoft-crm-embedding-advanced-find.html]

Thank you Adi Katz! I'll tell you what, it feels good to be thanked by a CRM master for discovering something they overlooked. But that's not the only reason I'm thanking him. He's solved the problem with the automatic refresh of the embedded Advanced Find View grids!

After thorough examination of his new code, I couldn't help but be overcome with a mote of jealousy at its elegance. I've redesigned my code to incorporate the best elements of Adi's solution: namely the automatic refresh hooks, the "Loading View" presentation, and his style adjustments (mine were leaving a space for a scroll bar on the right-hand side of the IFrame--only I didn't know that that's why the space was there until I implemented that part of his code over mine).

I also ran into unexpected "Access Denied" problems regarding the direct interaction that my XMLHTTP object was making to the domain-less IFrame window (and yes, it does not have a domain if it does not have a src/location). Apparently this problem would only rear its ugly head on some machines and not others. On top of that, the problem fixed itself on one machine for no apparent reason, while still manifesting on others.

This prompted me to rewrite the code that pushes content into the IFrame. It should be noted that I suspect "Access Denied" problems people have reported with Adi's code similar to this may fall into the same boat. But his code differs from mine in that he constructs a form into the IFrame, which changes the IFrame's src/location and consequently its domain. This domain should be identical to the domain of the parent window, so XSS limitations shouldn't apply. But, not actually having used his code or produced the error, I can't say for certain.

Here's Version 4:

/// Summary: 
/// Provides a mechanism for replacing the contents of any Iframe on an entity form 
/// with any Advanced Find view. 
/// 
/// Param Description 
/// ---------- ------------------- 
/// iFrameId The id established for the target Iframe 
/// entityName The name of the entity to be found by the Advanced Find 
/// fetchXml FetchXML describing the query for the entity 
/// layoutXml LayoutXML describing the display of the entity 
/// sortCol The schema name of the entity attribute used for primary sorting 
/// sortDescend "true" if sorting the sortCol by descending values, or "false" if ascending 
/// defaultAdvFindViewId The GUID of an Advanced Find View for the entity; may that of a saved view 
/// entityTypeId (Optional) The Object Type ID for the entity. Setting this causes the system 
/// to overwrite the functionality of the "New" button to establish related records

function EmbedAdvancedFindView (iFrameId, entityName, fetchXml, layoutXml, sortCol, sortDescend, defaultAdvFindViewId, entityTypeId) { 
  // Initialize our important variables 
  var httpObj = new ActiveXObject("Msxml2.XMLHTTP"); 
  var url = SERVER_URL + "/AdvancedFind/fetchData.aspx"; 
  var iFrame = document.getElementById(iFrameId); 
  var win = iFrame.contentWindow; 
  var doc = iFrame.contentWindow.document; 
  
  // Provide a global function within the parent scope to avoid XSS limitations 
  // in updating the iFrame with the results from our HTTP request 
  PushResponseContents = function (iFrame, httpObj, entityTypeId) { 
    var win = iFrame.contentWindow; 
    var doc = iFrame.contentWindow.document; 
    var m_iFrameShowModalDialogFunc = null; 
    var m_windowAutoFunc = null; 
    
    // Write the contents of the response to the Iframe 
    doc.open(); 
    doc.write(httpObj.responseText); 
    doc.close(); 
    
    // Set some style elements of the Advanced Find window 
    // to mesh cleanly with the parent record's form 
    doc.body.style.padding = "0px"; 
    doc.body.scroll="no"; 
    
    // Should we overwrite the functionality of the "New" button? 
    if ((typeof(entityTypeId) != "undefined") && (entityTypeId != null)) { 
      var buttonId = "_MBopenObj" + entityTypeId; 
      var newButton = doc.getElementById(buttonId); 
      
      eval("newButton.action = 'locAddRelatedToNonForm(" + entityTypeId + ", " + crmForm.ObjectTypeCode + ", \"" + crmForm.ObjectId + "\",\"\");'");
    } 
    
    // Swap the showModalDialog function of the iFrame 
    if (m_iFrameShowModalDialogFunc == null) { 
      m_iFrameShowModalDialogFunc = win.showModalDialog; 
      win.showModalDialog = OnIframeShowModalDialog; 
    } 
    
    if (m_windowAutoFunc == null) { 
      m_windowAutoFunc = win.auto; win.auto = OnWindowAuto; 
    } 
    
    // Configure the automatic refresh functionality for dialogs
    function OnIframeShowModalDialog(sUrl, vArguments, sFeatures) { 
      m_iFrameShowModalDialogFunc(sUrl, vArguments, sFeatures); 
      doc.all.crmGrid.Refresh(); 
    } 
    
    function OnWindowAuto(otc) { 
      doc.all.crmGrid.Refresh(); 
      m_windowAutoFunc(otc); 
    } 
  } 
  
  // Without a null src, switching tabs in the form reloads the src 
  iFrame.src = null; 
  
  // Preload the iFrame with some HTML that presents a Loading image 
  var loadingHtml = "" 
    + "<table height='100%' width='100%' style='cursor:wait'>" 
    + " <tr>" 
    + " <td valign='middle' align='center'>" 
    + " <img alt='' src='/_imgs/AdvFind/progress.gif' />" 
    + " <div /><i>Loading View...</i>" 
    + " </td>" 
    + " </tr>" 
    + "</table>"; 
  
  doc.open(); 
  doc.write(loadingHtml); 
  doc.close(); 
  
  // Compile the FetchXML, LayoutXML, sortCol, sortDescend, defaultAdvFindViewId, and viewId into 
  // a list of params to be submitted to the Advanced Find form 
  var params = "FetchXML=" 
    + fetchXml 
    + "&LayoutXML=" 
    + layoutXml 
    + "&EntityName=" 
    + entityName 
    + "&DefaultAdvFindViewId=" 
    + defaultAdvFindViewId 
    + "&ViewType=1039" // According to Michael Hohne over at Stunnware, this is static 
    + "&SortCol=" 
    + sortCol 
    + "&SortDescend=" 
    + sortDescend; 
  
  // Establish an async connection to the Advanced Find form 
  httpObj.open("POST", url, true); 
  
  // Send the proper header information along with the request 
  httpObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
  httpObj.setRequestHeader("Content-length", params.length); 
  
  // Function to write the contents of the http response into the iFrame 
  httpObj.onreadystatechange = function () { 
    if (httpObj.readyState == 4 && httpObj.status == 200) { 
      parent.PushResponseContents(iFrame, httpObj, entityTypeId); 
    } 
  } 
  
  // Set it, and forget it! 
  httpObj.send(params); 
}

Once again, I'm really grateful to Adi for the enlightenment and code that his solution has provided me with. As I said previously, though I developed my solution primarily in accordance to the recommendations of Michael Höhne's post on the subject without the benefit of exposure to Adi's code, his solution has helped me identify where I could improve my own development since Version 1.

We do things differently within our respective solutions, so I would welcome anybody to weigh our solutions against each other and flesh out the pros and cons to either. As far as I'm concerned, I'm happy with what I have, and I have it for the job I need right now. If anybody else can make use of it, that's a bonus. I'm not trying to steal Adi's thunder, or (much) of his implementation. But I'm naturally biased when it comes to the distinctions between our methods, and find my own efforts more desirable, and I hope I've given him an acceptable amount of credit for the things I've taken from his code.

Friday, March 13, 2009

Microsoft CRM: Look Up Site Address button

One of the simpler hacks I've ever accomplished in CRM, is the ability to provide a button on an Entity form that allows you to populate address fields on that same Entity with the addresses from Sites. Most of this functionality comes from the Look Up Customer Address code by Michael Höhne over at Stunnware--which, if you haven't guessed by now, is like my CRM idol.

Here's the code:

var url = '/' + ORG_UNIQUE_NAME + '/_controls/lookup/lookupsingle.aspx?class=Site&objecttypes=4009&browse=0&bindingcolumns=address1_line1%2caddress1_line2%2caddress1_line3%2caddress1_city%2caddress1_stateorprovince%2caddress1_postalcode%2caddress1_country&ShowNewButton=0&ShowPropButton=1';

var selectedAddress = window.showModalDialog(url, null, 'dialogWidth:400px;dialogHeight:400px;resizable:yes'); 

if (selectedAddress != null) { 
  var addressFields = selectedAddress.items[0].values; 
  
  for (var index in addressFields) { 
    if (addressFields[index] && addressFields[index].name) { 
      var control = document.getElementById('<Prefix Value>' + addressFields[index].name.replace(/address1_/,'')); 
      
      if (control) { 
        control.DataValue = addressFields[index].value; 
      } 
    } 
  } 
}

This code works best when all of the address fields on your form use a prefix on the name of the fields originating in the Site entity. For example, the target custom_addressline1 field from a custom entity matches the field address1_line1 on the Site entity, making "custom_address" the value for <Prefix Value>. Otherwise, you'll end up mapping each of the fields in the script.

I take the liberty of stripping the "address1_" portion of each source field name from the Site. As you can see, there's an interesting feature of lookupsingle.aspx: the variable bindingcolumns can be used to set a series of invisible fields to return in the values array. I take the above code, and drop it into the ISV.config XML file under a button element for the entity's menu, and voila! The button now generates a nice, little lookup window populated with all my Sites. Selecting one automatically populates address fields on my form. It's possible that you could extend this functionality to System User records as well, and many other record types. I just haven't done so myself.

Thursday, March 12, 2009

Microsoft CRM: Embedding Advanced Find Views in Entity Forms (Version 3)

[UPDATE: See Version 4 of the code in this post at http://crmentropy.blogspot.com/2009/03/microsoft-crm-embedding-advanced-find_25.html]

So, I ran into a little problem using the New button override on an embedded AF view on a Contract Line entity. Apparently top.locAddObjTo() does not function properly on this particular record type. So, I modified the code to use the more appropriate locAddRelatedToNonForm() function. This function takes one more parameter, which I haven't identified, but apparently it's not required or is often set to a blank string. Best of all, this function works from the Iframe, and is the function used by the ordinary Associated View for custom entities. As of yet, I've only tested it with embedded AFs that display custom entities, but it appears to work from any "parent" entity form.

Version 3:

/// Summary: 
/// Provides a mechanism for replacing the contents of any Iframe on an entity form 
/// with any Advanced Find view. 
/// 
/// Param Description 
/// ---------- ------------------- 
/// iFrameId The id established for the target Iframe 
/// entityName The name of the entity to be found by the Advanced Find 
/// fetchXml FetchXML describing the query for the entity 
/// layoutXml LayoutXML describing the display of the entity 
/// sortCol The schema name of the entity attribute used for primary sorting 
/// sortDescend "true" if sorting the sortCol by descending values, or "false" if ascending 
/// defaultAdvFindViewId The GUID of an Advanced Find View for the entity; may that of a saved view 
/// entityTypeId (Optional) The Object Type ID for the entity. Setting this causes the system 
/// to overwrite the functionality of the "New" button to establish related records

function EmbedAdvancedFindView (iFrameId, entityName, fetchXml, layoutXml, sortCol, sortDescend, defaultAdvFindViewId, entityTypeId) { 
  // Initialize our important objects 
  var iFrame = document.getElementById(iFrameId); 
  var httpObj = new ActiveXObject("Msxml2.XMLHTTP"); 
  var url = "/AdvancedFind/fetchData.aspx"; 
  
  // Compile the FetchXML, LayoutXML, sortCol, sortDescend, defaultAdvFindViewId, and viewId into 
  // a list of params to be submitted to the Advanced Find form 
  var params = "FetchXML=" 
    + fetchXml 
    + "&LayoutXML=" 
    + layoutXml 
    + "&EntityName=" 
    + entityName 
    + "&DefaultAdvFindViewId=" 
    + defaultAdvFindViewId 
    + "&ViewType=1039" // According to Michael Hohne over at Stunnware, this is static 
    + "&SortCol=" 
    + sortCol 
    + "&SortDescend=" 
    + sortDescend; 
  
  // Establish an async connection to the Advanced Find form 
  httpObj.open("POST", url, true); 
  
  // Send the proper header information along with the request 
  httpObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
  httpObj.setRequestHeader("Content-length", params.length); 
  
  // Function to write the contents of the http response into the iFrame 
  httpObj.onreadystatechange = function () { 
    if (httpObj.readyState == 4 && httpObj.status == 200) { 
      // Shorthand to the document of the Iframe 
      var doc = iFrame.contentWindow.document; 
      
      // Without a null src, switching tabs in the form reloads the src 
      iFrame.src = null; 
      
      // Write the contents of the response to the Iframe 
      doc.open(); 
      doc.write(httpObj.responseText); 
      doc.close(); 
      
      // Set some style elements of the Advanced Find window 
      // to mesh cleanly with the parent record's form 
      doc.body.style.padding = "0px"; 
      doc.body.style.backgroundColor = "#eaf3ff"; 
      
      // Should we overwrite the functionality of the "New" button? 
      if ((typeof(entityTypeId) != "undefined") && (entityTypeId != null)) { 
        var buttonId = "_MBopenObj" + entityTypeId; 
        var newButton = doc.getElementById(buttonId); 
        
        eval("newButton.action = 'locAddRelatedToNonForm(" + entityTypeId + ", " + crmForm.ObjectTypeCode + ", \"" + crmForm.ObjectId + "\",\"\");'");
      } 
    } 
  } 
  
  // Set it, and forget it! 
  httpObj.send(params); 
}

Tuesday, March 10, 2009

Microsoft CRM: Embedding Advanced Find Views in Entity Forms (Version 2)

[UPDATE: See Version 3 of the code in this post at http://crmentropy.blogspot.com/2009/03/microsoft-crm-embedding-advanced-find_12.html]

So, I discovered a way to hack the functionality of the New button on the Advanced Find grid to establish new records that are related to the record housing the view. So without further ado, I give to you, the updated version of my EmbedAdvancedFindView() function.

Version 2:

/// Summary: 
/// Provides a mechanism for replacing the contents of any Iframe on an entity form 
/// with any Advanced Find view. 
/// 
/// Param Description 
/// ---------- ------------------- 
/// iFrameId The id established for the target Iframe 
/// entityName The name of the entity to be found by the Advanced Find 
/// fetchXml FetchXML describing the query for the entity 
/// layoutXml LayoutXML describing the display of the entity 
/// sortCol The schema name of the entity attribute used for primary sorting 
/// sortDescend "true" if sorting the sortCol by descending values, or "false" if ascending
/// defaultAdvFindViewId The GUID of an Advanced Find View for the entity; may that of a saved view 
/// entityTypeId (Optional) The Object Type ID for the entity. Setting this causes the system 
/// to overwrite the functionality of the "New" button to establish related records

function EmbedAdvancedFindView (iFrameId, entityName, fetchXml, layoutXml, sortCol, sortDescend, defaultAdvFindViewId, entityTypeId) { 
  // Initialize our important objects 
  var iFrame = document.getElementById(iFrameId); 
  var httpObj = new ActiveXObject("Msxml2.XMLHTTP"); 
  var url = "/AdvancedFind/fetchData.aspx"; 
  
  // Compile the FetchXML, LayoutXML, sortCol, sortDescend, defaultAdvFindViewId, and viewId into 
  // a list of params to be submitted to the Advanced Find form 
  var params = "FetchXML=" 
    + fetchXml 
    + "&LayoutXML=" 
    + layoutXml 
    + "&EntityName=" 
    + entityName 
    + "&DefaultAdvFindViewId=" 
    + defaultAdvFindViewId 
    + "&ViewType=1039" // According to Michael Hohne over at Stunnware, this is static 
    + "&SortCol=" 
    + sortCol 
    + "&SortDescend=" 
    + sortDescend; 
  
  // Establish an async connection to the Advanced Find form 
  httpObj.open("POST", url, true); 
  
  // Send the proper header information along with the request 
  httpObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
  httpObj.setRequestHeader("Content-length", params.length); 
  
  // Function to write the contents of the http response into the iFrame 
  httpObj.onreadystatechange = function () { 
    if (httpObj.readyState == 4 && httpObj.status == 200) { 
      // Shorthand to the document of the Iframe 
      var doc = iFrame.contentWindow.document; 
      
      // Without a null src, switching tabs in the form reloads the src 
      iFrame.src = null; 
      
      // Write the contents of the response to the Iframe 
      doc.open(); 
      doc.write(httpObj.responseText); 
      doc.close(); 
      
      // Set some style elements of the Advanced Find window 
      // to mesh cleanly with the parent record's form 
      doc.body.style.padding = "0px"; 
      doc.body.style.backgroundColor = "#eaf3ff"; 
      
      // Should we overwrite the functionality of the "New" button? 
      if ((typeof(entityTypeId) != "undefined") && (entityTypeId != null)) { 
        var buttonId = "_MBopenObj" + entityTypeId; 
        var newButton = doc.getElementById(buttonId); 
        
        eval("newButton.action = 'top.locAddObjTo(" + entityTypeId + ", " + crmForm.ObjectTypeCode + ", \"" + crmForm.ObjectId + "\");'"); 
      } 
    } 
  } 
  
  // Set it, and forget it! 
  httpObj.send(params); 
}

You'll notice that there's an additional parameter over the last version. entityTypeId should set as a string representation of the entity type code assigned to the record described by entityName. The code then overwrites the functionality of the New button with a call to top.locAddObjTo(). This function, I've found, takes 3 parameters in the following order: the entity type of the target record, the entity type of the related record, and the GUID of the related record. The caveat to this hacking is that upon completion of the new record, the grid does not automatically refresh to display the new record.

This may be a limitation to using the code for your purposes, so take it with a grain of salt. As for correcting this particular annoyance, there seems to be little option. As hard as I try, I cannot seem to reference the parent window, or grid, from the new record. All the normal javascript pointers (parent, top, opener) don't reference it. This doesn't mean a reference doesn't exist--I just haven't found it. The reverse seems also true: I can't grab a handler to the spawned window from any object/variable within the parent.

In light of this, I had toyed around with the idea of establishing a dual functionality for the New button, so that it would open a new record window and also start an invisible polling process to check the number of records for the view by using SOAP calls to the WSDL API. If the total number of records changed during a particular poll, then this invisible process would call crmGrid.Refresh().

I'm all for hacking, but to me that seems a bit overzealous and ugly. Although it may work, I'm not convinced it's the best approach. So, for the moment I'm leaving the annoyance in and training people to manually refresh the grid after they've established a new, related record. If anyone reading this blog has some insight on how I might better accommodate the automatic grid refresh, please leave a comment.

Friday, March 6, 2009

Microsoft CRM: Embedding Advanced Find Views in Entity Forms

[UPDATE: See Version 2 of the code in this post at http://crmentropy.blogspot.com/2009/03/microsoft-crm-embedding-advanced-find_10.html]
CRM. What a beautiful beast. I regularly describe the product as Microsoft's best. My exposure and experience with it has been considerable over the last 2 years, and I've found a vast number of helpful resources online to assist me in developing solutions for it.
Finally, it appears as if I may have something to offer the community. Something that has relatively little explanation or resource, and for which I've done a great deal of research and hacking to obtain. So, it is with great pleasure that I introduce my method of embedding Advanced Find views into CRM entity forms:
1. Place an Iframe into the CRM form, with the source "about:blank"
2. Copy the following function to the OnLoad() event for the form:
/// Summary:
/// Provides a mechanism for replacing the contents of any Iframe on an entity form
/// with any Advanced Find view.
///
/// Param Description
/// ---------- -------------------
/// iFrameId The id established for the target Iframe
/// entityName The name of the entity to be found by the Advanced Find
/// fetchXml FetchXML describing the query for the entity
/// layoutXml LayoutXML describing the display of the entity
/// sortCol The schema name of the entity attribute used for primary sorting
/// sortDescend "true" if sorting the sortCol by descending values, or "false" if ascending
/// defaultAdvFindViewId The GUID of an Advanced Find View for the entity; may that of a saved view

function EmbedAdvancedFindView (iFrameId, entityName, fetchXml, layoutXml, sortCol, sortDescend, defaultAdvFindViewId) {
// Initialize our important objects
  var iFrame = document.getElementById(iFrameId);
  var httpObj = new ActiveXObject("Msxml2.XMLHTTP");
  var url = "/AdvancedFind/fetchData.aspx";
  
  // Compile the FetchXML, LayoutXML, sortCol, sortDescend, defaultAdvFindViewId, and viewId into
  // a list of params to be submitted to the Advanced Find form
  var params = "FetchXML=" + fetchXml
  + "&LayoutXML=" + layoutXml
  + "&EntityName=" + entityName
  + "&DefaultAdvFindViewId=" + defaultAdvFindViewId
  + "&ViewType=1039" // According to Michael Hohne over at Stunnware, this is static
  + "&SortCol=" + sortCol
  + "&SortDescend=" + sortDescend;
  // Establish an async connection to the Advanced Find form
  
  httpObj.open("POST", url, true);
  
  // Send the proper header information along with the request
  httpObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  httpObj.setRequestHeader("Content-length", params.length);
  
  // Function to write the contents of the http response into the iFrame
  httpObj.onreadystatechange = function () {
    if (httpObj.readyState == 4 && httpObj.status == 200) {
      // Shorthand to the document of the Iframe
      var doc = iFrame.contentWindow.document;
      
      // Without a null src, switching tabs in the form reloads the src
      iFrame.src = null;
      
      // Write the contents of the response to the Iframe
      doc.open();
      doc.write(httpObj.responseText);
      doc.close();
      
      // Set some style elements of the Advanced Find window
      // to mesh cleanly with the parent record's form
      doc.body.style.padding = "0px";
      doc.body.style.backgroundColor = "#eaf3ff";
    }
  }
  
  // Set it, and forget it!
  httpObj.send(params);
}
3. Next, establish values for all of the parameters, call the function, and you're done!
It's a pretty simple process, actually. It makes use of "AJAX" related functionality in IE by way of the "XMLHTTP" object. This object represents a "POST" call to the Advanced Find form given the parameters you establish. When the request is complete, the results are written directly into the Iframe.
For me, this code works beautifully. Compare it to Adi Katz' solution at http://mscrm4ever.blogspot.com/2008/09/display-fetch-in-iframe.html. The main differences between his solution and mine are as follows:
1. He provides a handler a viewer object which provides, among other things, a handy "Refresh" function to update the view when related information changes. For mine, you have to call the function again. Personally, I wrap it in an Update() function for those forms which require it.
2. He uses a hard-coded HTML Form in the Iframe to submit information. Mine uses an ActiveX XMLHTTP object.
3. His displays a handy, "loading" image, while the server interaction is taking place. I'm probably going to duplicate this functionality in mine at some point later.
4. He plugs into the form's event handlers to determine when to load the Advanced Find. If the page is on a different tab, it's not loaded until you view that tab. He does this, I believe, because the form's behavior naturally causes the Iframe to load when the tab containing the Iframe is navigated to--but managed in CRM's internal code. Mine initializes immediately with the record, and since it's asynchronous it shouldn't impact the performance of navigating around the form until the XMLHTTP object returns the results. I did encounter the form's behavior of loading (or, in this case, reloading) Iframes on different tabs, and found it was simply easy to subvert this behavior by nullifying the "src" attribute of the Iframe. Then, the form simply has nothing to load.
I would like to thank Adi for his code. There is hardly a better resource out there for understanding how to interact with the Advanced Find form, outside of Michael Höhne's posting at http://www.stunnware.com/crm2/topic.aspx?id=AdvancedFind1. I credit them both for discovering the protocol for interfacing with Advanced Find. Though, I did only find Adi's code after completing development on mine. (Had I found his first, I probably would have copied it outright, and not made a parallel development.)
So, for an example that uses my code:
// Embed an AF window
fetchXml = ""
  + "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>"
  + " <entity name='account'>"
  + " <attribute name='name'/>"
  + " <attribute name='address1_city'/>"
  + " <attribute name='primarycontactid'/>"
  + " <attribute name='telephone1'/>"
  + " <attribute name='accountid'/>"
  + " <order attribute='name' descending='false'/>"
  + " <filter type='and'>"
  + " <condition attribute='statecode' operator='eq' value='0'/>"
  + " </filter>"
  + " </entity>"
  + "</fetch>";

layoutXml = ""
  + "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>"
  + " <row name='result' id='accountid'>"
  + " <cell name='name' width='300' />"
  + " <cell name='primarycontactid' width='150' />"
  + " <cell name='telephone1' width='100' />"
  + " <cell name='address1_city' width='100' />"
  + " </row>"
  + "</grid>";

EmbedAdvancedFindView("IFRAME_Accounts", "account", fetchXml, layoutXml, "name", "false", "{00000000-0000-0000-00AA-000000666000}");
In this example, I have an Iframe on a form with the name/id "IFRAME_Accounts". No part of the FetchXml is dynamic, so I really don't need to reload the Advanced Find contextually. If I did, then I would simply wrap the above code in an Update() function of some form, and call it whenever relevant fields on the form were changed.
My next task is finding out how to change the New button on the embedded AF view to make a new target record related to the record holding the form. I think top.locAddObjTo() will do the trick, if I can programmatically force that into the action attribute of the menu button.