【问题标题】:Accessing a control inside a ControlTemplate访问 ControlTemplate 中的控件
【发布时间】:2013-10-07 14:54:36
【问题描述】:

这是 xaml:

<Page.Resources>
    <ControlTemplate x:Key="WeddingButtonBigTemplate" TargetType="Button">
        <Grid>
            <Image x:Name="imgNormal" Source="../Images/Married_button2.png"/>
            <TextBlock x:Name="textBlock2" Style="{StaticResource RegularBlueSpecialBoldText}" LineHeight="28" LineStackingStrategy="BlockLineHeight" HorizontalAlignment="Center" Margin="10,30,10,70" TextWrapping="Wrap" TextAlignment="Center" VerticalAlignment="Stretch" >
                <Run FontSize="20" Text="The event of"></Run>
                <Run FontSize="28" Text="{DynamicResource strBride}"></Run>
            </TextBlock>
        </Grid>
    </ControlTemplate>
</Page.Resources>

<Grid HorizontalAlignment="Center" VerticalAlignment="Top" Width="1000">
    <Button x:Name="btnWedding" HorizontalAlignment="Left" Margin="10,20,0,-49" VerticalAlignment="Top" Template="{StaticResource WeddingButtonBigTemplate}" Foreground="#FF2B4072" Width="380" Click="btnClick" />
</Grid>

我正在尝试访问名为 textBlock2 的 TextBlock。
我试图覆盖 OnApplyTemplate 但得到了 null。

我试过了:

Grid gridInTemplate = (Grid)btnWedding.Template.FindName("grid", btnWedding);
var ct0 = btnWedding.Template.FindName("textBlock2", btnWedding);
var ct1 = btnWedding.FindName("textBlock2");
var ct2 = btnWedding.FindResource("textBlock2");

gridInTemplate 为空(示例取自 MSDN)。
当然,ct# 都是空的。

我在这里错过了什么?

【问题讨论】:

  • 我怀疑你的按钮还没有loaded(rendered on UI)。仅当模板应用于按钮时,您的代码才会返回 null。
  • 另外,gridInTemplate 为 null,因为您尚未在 xaml 声明中为您的 Grid 指定 x:Name
  • 我遇到了类似的问题,不得不致电UpdateLayout,以解决@RohitVats 提到的问题。
  • 如果你使用 Expander 可以看看stackoverflow.com/questions/26422811/…

标签: c# wpf c#-4.0 controltemplate


【解决方案1】:

如果您已覆盖 OnApplyTemplate,则不要使用 FindResource() 或 Template.FindName() 或 VisualTreeHelper 的任何 hack。只需使用this.GetTemplateChild("textBlock2");

WPF 中的模板具有独立的名称范围。这是因为模板被重复使用,并且当控件的多个实例各自实例化其模板时,模板中定义的任何名称都不能保持唯一。调用 GetTemplateChild 方法以返回对模板实例化后来自模板的对象的引用。您不能使用 FrameworkElement.FindName 方法从模板中查找项目,因为 FrameworkElement.FindName 的作用范围更广,并且一旦应用 ControlTemplate 类本身与实例化模板之间就没有任何联系。

查看此链接:

http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.gettemplatechild.aspx

如果你的例子是微软的例子,那么我建议你再读一遍。您可能跳过了一些内容。

http://msdn.microsoft.com/en-us/library/bb613586.aspx

总结 - 在编写自定义控件时使用 GetTemplateChild(),例如OnApplyTemplate,其他情况下使用 Template.FindName。

【讨论】:

  • 这对我有帮助。通过覆盖OnApplyTemplate(而不是OnTemplateChange)使其工作。
【解决方案2】:

试试下面的代码。这将返回模板化元素。

this.GetTemplateChild("ControlName");

【讨论】:

    【解决方案3】:

    您的代码是正确的,但可能不在正确的位置...FindName 仅在应用模板后才能工作。通常,当您在自定义控件中覆盖 OnApplyTemplate 时会使用它。由于您没有创建自定义控件,因此可以在按钮的 Loaded 事件中进行。

    【讨论】:

      【解决方案4】:

      您可以使用 VisualTreeHelper 迭代按钮的可视化树以获取任何子级。你可以使用这个基本的泛型函数来获取它

      private static DependencyObject RecursiveVisualChildFinder<T>(DependencyObject rootObject)  
      {  
          var child = VisualTreeHelper.GetChild(rootObject, 0);  
          if (child == null) return null;  
      
          return child.GetType() == typeof (T) ? child : RecursiveVisualChildFinder<T>(child);  
      }
      

      你可以像这样使用它

      TextBlock textblock = RecursiveVisualChildFinder<TextBlock>(btnWedding);
      if(textblock.Name == "textBlock2")
      {// Do your stuff here
      }
      

      【讨论】:

      • 我收到以下异常:Specified index is out of range or child at index is null. Do not call this method if VisualChildrenCount returns zero, indicating that the Visual has no children.
      • 同样的问题,加了赏金
      • var ct0 = btnWedding.Template.FindName("textBlock2", btnWedding); 对我来说很好......你能分享你的代码中你想要获取文本块的确切位置吗?
      • 如果它在 Loaded 事件上,它的工作正常,正如 Thomas Levesque 所说。可能是他试图从 OnApplyTemplate 访问代码,此时控制尚不可用。
      【解决方案5】:

      如果你能得到网格控制,那么试试下面的代码

      TextBlock textBlock2 = (TextBlock)gridInTemplate.Children[1];
      

      【讨论】:

      • 我的错误,网格为空,尽管我从 MSDN 示例中获取了代码。问题已相应更新。
      【解决方案6】:

      “FrameworkElement.FindName(string name)”方法使用布局的名称范围,其中按钮/控件用于解析名称。简而言之,您可以使用它在应用程序布局的网格或堆栈面板中查找子项。但是您不能使用它来查找您在应用程序布局中使用过的控件的子项(因为模板化的子项名称在不同的范围内)

      在您的情况下获得子项的一种方法是继承按钮。由于您不会修改按钮的任何其他属性或行为,因此新按钮将正常工作。实际上,我从未使用过这种访问模板子项的方法,因为我从未需要在控件类的范围之外使用它们。

      public class WeddingButton : Button
      {
          public override void OnApplyTemplate()
          {
              TextBlock textBlock = this.GetTemplateChild("textBlock2") as TextBlock;
              base.OnApplyTemplate();
          }
      }
      
      <Page.Resources>
          <ControlTemplate x:Key="WeddingButtonBigTemplate" TargetType="Button">
              <Grid>
                  <Image x:Name="imgNormal" Source="../Images/Married_button2.png"/>
                  <TextBlock x:Name="textBlock2" Style="{StaticResource RegularBlueSpecialBoldText}" LineHeight="28" LineStackingStrategy="BlockLineHeight" HorizontalAlignment="Center" Margin="10,30,10,70" TextWrapping="Wrap" TextAlignment="Center" VerticalAlignment="Stretch" >
                      <Run FontSize="20" Text="The event of"></Run>
                      <Run FontSize="28" Text="{DynamicResource strBride}"></Run>
                  </TextBlock>
              </Grid>
          </ControlTemplate>
      </Page.Resources>
      
      <Grid HorizontalAlignment="Center" VerticalAlignment="Top" Width="1000">
          <WeddingButton x:Name="btnWedding" HorizontalAlignment="Left" Margin="10,20,0,-49" VerticalAlignment="Top" Template="{StaticResource WeddingButtonBigTemplate}" Foreground="#FF2B4072" Width="380" Click="btnClick" />
      </Grid>
      

      【讨论】:

        【解决方案7】:

        对于可能仍然在这里绊倒的其他人。

        我可以控制默认情况下具有 Visibility="Collapsed" 的屏幕。即使我在窗口构造函数中切换了可见性,它也没有初始化模板。必须在 FindName() 之前调用控件上的 ApplyTemplate() 才能获得结果。

        stationElement.ApplyTemplate();
        var PART_DATA = stationElement.Template.FindName("PART_DATA", stationElement);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-23
          • 1970-01-01
          • 2017-01-09
          • 1970-01-01
          相关资源
          最近更新 更多