如下面的代码所示,我有ScrollViewer的后代,并在ctor中设置了MyScrollView的名称。但是,当我尝试在控件模板中使用MyScrollViewer时,无法通过Template.FindName
找到一个如果我将<local:MyScrollViewer />
更改为<local:MyScrollViewer Name=""PART_ContentHost"" />
,代码将按预期工作,但是我正在寻找解决方案而不更改XAML。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var parserContext = new ParserContext
{
XamlTypeMapper = new XamlTypeMapper(new string[0]),
XmlnsDictionary =
{
{ "", "http://schemas.microsoft.com/winfx/2006/xaml/presentation" },
{ "x", "http://schemas.microsoft.com/winfx/2006/xaml" },
{ "local", "clr-namespace:" + typeof(MyScrollViewer).Namespace + ";assembly=" + typeof(MyScrollViewer).Assembly.FullName}
}
};
var template = (ControlTemplate)XamlReader.Parse(@"
<ControlTemplate TargetType=""TextBox"">
<Border>
<local:MyScrollViewer />
<!--<local:MyScrollViewer Name=""PART_ContentHost""/> -->
</Border>
</ControlTemplate>
", parserContext);
// Name=""PART_ContentHost""
Content = new MyTextBox { Template = template };
}
}
public class MyScrollViewer: ScrollViewer
{
public MyScrollViewer() => Name = "PART_ContentHost";
}
public class MyTextBox: TextBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var contentHost = Template.FindName("PART_ContentHost", this);
if (contentHost == null)
throw new Exception("Can not find PART_ContentHost");
}
}
已更新: 即使我将控件模板放入MainWindow.xaml(并从MainWindow ctor中删除),也无法正常工作。
public MainWindow()
{
InitializeComponent();
}
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.Resources>
<ControlTemplate TargetType="local:MyTextBox" x:Key="TextBoxTemplate">
<local:MyScrollViewer />
</ControlTemplate>
</Grid.Resources>
<local:MyTextBox Template="{StaticResource TextBoxTemplate}" />
</Grid>
</Window>
答案 0 :(得分:2)
使用beforeEach
创建MyScrollViewer
时不会调用template
的构造函数,因此存储了创建XamlReader
的名称 在内部词典中。参见MyScrollViewer
。当template.ChildNames
可见时将首先调用MyScrollViewer
的构造函数,但为时已晚。
从XAML创建的模板,它通过解析来注意到子名称,而不创建子实例。稍后将创建子实例,但模板保留旧名称。因此,如果您使用新名称调用MyTextBox
,将不会找到它们。
尝试
Template.FindNames
答案 1 :(得分:1)
但是我正在寻找不更改XAML的解决方案
在ScrollViewer
的构造函数中设置名称将无效。您必须在模板中进行设置,才能将其注册到模板的名称范围中。
如果您不想在模板中为元素分配Name
,则可以等待其创建,然后在视觉树中找到它而无需使用名称:
public class MyTextBox : TextBox
{
public MyTextBox()
{
Loaded += MyTextBox_Loaded;
}
private void MyTextBox_Loaded(object sender, RoutedEventArgs e)
{
MyScrollViewer sv = FindVisualChild<MyScrollViewer>(this);
//...
}
private static T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;
}
}