diff --git a/Src/Common/Controls/XMLViews/XMLViewsTests/TestManyOneBrowse.cs b/Src/Common/Controls/XMLViews/XMLViewsTests/TestManyOneBrowse.cs
index 935d349cdb..536bd1b32b 100644
--- a/Src/Common/Controls/XMLViews/XMLViewsTests/TestManyOneBrowse.cs
+++ b/Src/Common/Controls/XMLViews/XMLViewsTests/TestManyOneBrowse.cs
@@ -449,5 +449,28 @@ public void DisplayDoubleSeqPathObject()
m_mdc, m_sda, m_layouts, out useHvo, collectStructNodes);
Assert.That(useHvo, Is.EqualTo(9)); // the third sense, in which context we display the 4th SD
}
+
+ /// A part-ref node must carry itself as the resulting Caller, so child fragments (e.g. multilingual strings) can resolve attributes like "ws" from it.
+ [Test]
+ public void GetDisplayCommandForColumn_PartRefNode_CarriesItselfAsCaller()
+ {
+ ArrayList list = new ArrayList();
+ XmlViewsUtils.CollectBrowseItems(1, m_columnList[0], list, null, m_mdc, m_sda, m_layouts);
+ IManyOnePathSortItem bvi = list[0] as IManyOnePathSortItem;
+
+ // Hand-built node, the shape XmlVc embeds when a part ref names another part.
+ var doc = new XmlDocument();
+ XmlElement partRefNode = doc.CreateElement("part");
+ partRefNode.SetAttribute("ref", "Msas");
+
+ int hvo;
+ NodeDisplayCommand cmd = XmlViewsUtils.GetDisplayCommandForColumn(bvi, partRefNode, null,
+ m_mdc, m_sda, m_layouts, out hvo, null);
+
+ var childrenCmd = cmd as NodeChildrenDisplayCommand;
+ Assert.That(childrenCmd, Is.Not.Null, "a resolvable part ref must yield a NodeChildrenDisplayCommand");
+ Assert.That(childrenCmd.Caller, Is.SameAs(partRefNode),
+ "the part ref node itself must be carried down as the caller so child fragments can resolve attributes from it");
+ }
}
}
diff --git a/Src/Common/Controls/XMLViews/XmlVc.cs b/Src/Common/Controls/XMLViews/XmlVc.cs
index 885f15157e..7e354b04b1 100644
--- a/Src/Common/Controls/XMLViews/XmlVc.cs
+++ b/Src/Common/Controls/XMLViews/XmlVc.cs
@@ -5074,6 +5074,12 @@ public override int GetHashCode()
///
public class NodeChildrenDisplayCommand : NodeDisplayCommand
{
+ // Optional "part ref" node that called the part whose children we display. When non-null it is
+ // passed down as the caller so that child fragments (e.g. multilingual strings) can resolve
+ // attributes such as "ws" from it. This mirrors XmlVc's behavior where the "part" case is the
+ // one thing that calls ProcessChildren with a non-null caller.
+ readonly XmlNode m_caller;
+
/// ------------------------------------------------------------------------------------
///
/// Initializes a new instance of the class.
@@ -5085,14 +5091,39 @@ public NodeChildrenDisplayCommand(XmlNode node)
{
}
+ /// ------------------------------------------------------------------------------------
+ ///
+ /// Initializes a new instance of the class that
+ /// carries a caller (part ref) node down to the children being displayed.
+ ///
+ /// The node whose children are displayed.
+ /// The calling part ref node (may be null).
+ /// ------------------------------------------------------------------------------------
+ public NodeChildrenDisplayCommand(XmlNode node, XmlNode caller)
+ : base(node)
+ {
+ m_caller = caller;
+ }
+
+ ///
+ /// The calling part ref node, or null if none was supplied.
+ ///
+ public XmlNode Caller
+ {
+ get { return m_caller; }
+ }
+
internal override void PerformDisplay(XmlVc vc, int fragId, int hvo, IVwEnv vwenv)
{
- vc.ProcessChildren(Node, vwenv, hvo);
+ if (m_caller == null)
+ vc.ProcessChildren(Node, vwenv, hvo);
+ else
+ vc.ProcessChildren(Node, vwenv, hvo, m_caller);
}
internal override void DetermineNeededFields(XmlVc vc, int fragId, NeededPropertyInfo info)
{
- DetermineNeededFieldsForChildren(vc, Node, null, info);
+ DetermineNeededFieldsForChildren(vc, Node, m_caller, info);
}
// Make it work sensibly as a hash key
@@ -5108,7 +5139,8 @@ internal override void DetermineNeededFields(XmlVc vc, int fragId, NeededPropert
/// ------------------------------------------------------------------------------------
public override bool Equals(object obj)
{
- return base.Equals(obj) && obj is NodeChildrenDisplayCommand;
+ var other = obj as NodeChildrenDisplayCommand;
+ return other != null && base.Equals(obj) && other.m_caller == m_caller;
}
/// ------------------------------------------------------------------------------------
@@ -5122,7 +5154,7 @@ public override bool Equals(object obj)
/// ------------------------------------------------------------------------------------
public override int GetHashCode()
{
- return base.GetHashCode();
+ return base.GetHashCode() ^ (m_caller == null ? 0 : m_caller.GetHashCode());
}
diff --git a/Src/Common/Controls/XMLViews/XmlViewsUtils.cs b/Src/Common/Controls/XMLViews/XmlViewsUtils.cs
index 1ffcd8a631..4d90f3a02e 100644
--- a/Src/Common/Controls/XMLViews/XmlViewsUtils.cs
+++ b/Src/Common/Controls/XMLViews/XmlViewsUtils.cs
@@ -1134,19 +1134,23 @@ static NodeDisplayCommand GetDisplayCommandForColumn1(IManyOnePathSortItem bvi,
collectOuterStructParts.Add(node);
return GetDisplayCommandForColumn1(bvi, mainChild, cache, mdc, sda, layouts, depth, out hvo, collectOuterStructParts);
}
- // Review JohnT: In XmlVc, "part" is the one thing that calls ProcessChildren with non-null caller.
- // this should make some difference here, but I can't figure what yet, or come up with a test that fails.
- // We may need a "caller" argument to pass this down so it can be used in GetNodeForRelatedObject.
+ // In XmlVc, "part" is the one thing that calls ProcessChildren with a non-null caller (the
+ // part ref node). The off-screen cell-render path must do the same, otherwise children that
+ // read attributes such as "ws" from the caller (e.g. multilingual strings configured via the
+ // part ref) resolve nothing and render blank. So we capture the part ref node and carry it as
+ // the caller into the NodeChildrenDisplayCommand. (See LT- for the "Grammatical Info." blank
+ // column bug in the Avalonia browse table.)
case "part":
{
string layoutName = XmlUtils.GetOptionalAttributeValue(node, "ref");
if (layoutName != null)
{
// It's actually a part ref, in a layout, not a part looked up by one!
- // Get the node it refers to, and make a command to process its children.
+ // Get the node it refers to, and make a command to process its children, passing this
+ // part ref node down as the caller so child fragments can resolve attributes from it.
XmlNode part = XmlVc.GetNodeForPart(hvo, layoutName, false, sda, layouts);
if (part != null)
- return new NodeChildrenDisplayCommand(part); // display this object using the children of the part referenced.
+ return new NodeChildrenDisplayCommand(part, node); // display this object using the children of the part referenced.
else
return new NodeDisplayCommand(node); // no matching part, do default.
}