在Silverlight DataTemplate中访问元素

5

虽然SO上已有一些相关问题,但我仍未能找到一个干净的解决方案。

如果我有一个数据模板被多次使用,例如TreeViewItem.HeaderTemplate,我该如何仅更改其中某些TreeViewItem的模板?

例如,假设我的TVI HeaderTemplate拥有一个文本块,并且根据字符串不同,我想让字体加粗。

我希望做到这样:

((TextBlock)myTreeView.Items.ElementAt(0).FindName("myTextBlock")).FontWeight = FontWeights.Bold;

有没有人有解决这个问题的方案?--> 谢谢 Evan

编辑:是否有一种通用的方法来根据名称获取控件,即使它在数据模板中也可以?

LayoutRoot.FindName("myTextBlock"); 如果 myTextBlock 不在数据模板中,则可以使用。如何编写一个 findElementInDataTemplate(string elementName, string parentName) 函数?

Evan的答案不是我要找的,因为我正在开发一个控件。我希望使用我的控件的应用程序开发人员能够更改控件中的任何元素。如果我使用Evan的解决方案,那将需要应用程序开发人员访问控件中的所有模板。这是可能的,但不理想。谢谢!


如果您使用了一个名为“myTextBlock”的控件的 DataTemplate,并且在某个 LayoutRoot.FindName("myTextBlock") 操作中多次使用它,那么您希望该操作返回哪个名为“myTextBlock”的控件? - AnthonyWJones
@AnthonyWJones 好的,编辑后包括了 parentName 参数。 - NickHalden
@AnthonyWJones:通常情况下,两个具有相同名称的控件是不可能的。如果您在模板中保留了两个相同名称的控件,则认为该模板是错误的。 - Shashi
5个回答

4

我做到这一点的方法是,使用控件的Loaded事件,将所有需要的项目存储在类级别的集合变量中。 以此DataTemplate为例。

<DataTemplate>
   ...
   <TextBlock Loaded="TemplateTextBlock_Loaded" />
</DataTemplate>

然后您可以使用Loaded事件来加载一些集合以供以后使用。

private List<TextBlock> templateTextBlocks = new List<TextBlock>();

private void TemplateTextBlock_Loaded(object sender, RoutedEventArgs e)
{
   TextBlock tb = sender as TextBlock;
   if (!this.templateTextBlocks.Contains(tb)) this.templateTextBlocks.Add(tb);
}

当然,如果您要频繁加载和卸载控件,这可能不适合您。

这对我有用,AnthonyWJones的答案在可视化树已经生成时很有用,但是这个也可以在那之前工作。 - hungryMind

2

如果您正在使用数据绑定,是否尝试过使用绑定转换器? 在这种情况下,您可以像这样操作...

FontWeight={Binding Path=TextProperty, Converter={StaticResource BoldConverter}}

而转换器将类似于...
string myTestString = (string)value;
if (myTestString.Contains("Bob"))
    return FontWeights.Bold;
return FontWeights.Normal;

这使得查找特定元素时更加轻松,不必费力地深入研究元素。

非常好的解决方案,感谢您回答我的问题。现在让我们假装我问了我真正想问的问题,看看我的修改。 - NickHalden

1

对于这样的要求,我的第一反应是:你真的确定想这么做吗?我通常会建议开发人员查看已使用的现有控制模式。在这种情况下,似乎需要一个模板化控件。

当然,这并不能提供您所需的灵活性。您似乎追求的是可定制控件的“圣杯”,即希望调整控件的任何细节,而无需复制整个控件的模板。当然,这在声明上实际上是不可能的,如果可以的话,我会担心语法和语义规则。

话虽如此,总有例外。因此,尽管我觉得您不应该这样做,但我会提出一个可能的选项。

这个旧的答案提供了一个Descendents扩展方法,允许您枚举对象树中的控件。给定TreeViewItem的实例,您应该能够找到所需的TextBlock

TextBlock tb = treeViewItem.Descendents()
                 .OfType<TextBlock>()
                 .Where(t => t.Name == "myTextBlock")
                 .FirstOrDefault();

我收到了错误信息:“IEnumerable依赖对象不包含TypeOf的定义”。 - NickHalden
@JGord:请确保你的代码文件顶部已经包含了“using System.Linq”。 - AnthonyWJones
@JGord:糟糕,我的错!应该是“OfType”,而不是“TypeOf”,已做更正。 - AnthonyWJones

0
这是哪个版本的Silverlight? 这篇文章中的“Aug 10 at 18:55”是哪一年的?
在当前的SL4版本中似乎没有它...

0

你也可以尝试这个:

TextBlock txtBlk = grd.FindName("txtBlkName") as TextBlock;

其中,grd代表你的根元素(要查找的元素的父元素)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接