Template.FindName не работает, если я устанавливаю Имя из кода - PullRequest
0 голосов
/ 15 февраля 2019

Как вы можете видеть в приведенном ниже коде, у меня есть потомок ScrollViewer, и я задаю имя MyScrollView в ctor.Но когда я пытаюсь использовать 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>

Ответы [ 2 ]

0 голосов
/ 15 февраля 2019

, но я ищу решение без изменения 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;
    }
}
0 голосов
/ 15 февраля 2019

Конструктор MyScrollViewer не будет вызываться при template создании с XamlReader, поэтому имена , с которыми MyScrollViewer было создано , сохраняются во внутренних словарях.Смотри template.ChildNames.Сначала будет вызван конструктор MyScrollViewer, когда MyTextBox станет видимым, но уже слишком поздно.

Шаблон создается из XAML, и он замечает дочерние имена при разборе, без создания дочерних экземпляров.Позже будут созданы дочерние экземпляры, но шаблон будет содержать старые имена.Поэтому, если вы позвоните Template.FindNames с новыми именами, они не будут найдены.
Попробуйте

var contentHost = Template.FindName("2_T", this);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...