ItemsControl: как использовать FindName в ItemsPanelTemplate для доступа к панели

<Style TargetType="{x:Type local:CustomItemsControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <ScrollViewer>
                    <ItemsPresenter x:Name="PART_Presenter"/>
                </ScrollViewer>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel x:Name="PART_StackPanel" IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

Попытка получить доступ к StackPanel для установки событий при изменении дочерних элементов.

[TemplatePartAttribute(Name = "PART_StackPanel", Type = typeof(StackPanel))]
[TemplatePartAttribute(Name = "PART_Presenter", Type = typeof(ItemsPresenter))]
public class CustomItemsControl: ItemsControl
{
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        var presenter = (ItemsPresenter)this.Template.FindName("PART_Presenter", this);
        var stackPanel = (StackPanel)this.ItemsPanel.FindName("PART_StackPanel",this);
    }
}

Получите исключение, когда я пытаюсь найти StackPanel.

InvalidOperationException:

Эта операция действительна только для элементов, к которым применен этот шаблон.

Пожалуйста, сообщите, если есть способ найти TemplatePart внутри ItemsPanelTemplate. И когда я должен знать, когда применяется ItemsPanelTemplate?

3 ответа

Другой вариант - позвонить .ApplyTemplate() на ItemsPresenter, пока еще в ItemControl's OnApplyTemplate метод. Тогда призыв к .FindName преуспеет.

    [TemplatePartAttribute(Name = "PART_StackPanel", Type = typeof(StackPanel))]
    [TemplatePartAttribute(Name = "PART_Presenter", Type = typeof(ItemsPresenter))]
    public class CustomItemsControl : ItemsControl
    {
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            var presenter = (ItemsPresenter)this.Template.FindName("PART_Presenter", this);
            presenter.ApplyTemplate();
            var stackPanel = (StackPanel)this.ItemsPanel.FindName("PART_StackPanel", presenter);
        }
    }

Выяснилось, что событие Loaded было тем, которое нужно ожидать на ItemsPanelTemplate. Я могу найти StackPanel, используя имя TemplatePart. Спасибо Рику за предложение, что StackPanel должен быть найден в Presenter.

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);
        this.Loaded += new Accordion_Loaded;
    }

    void Accordion_Loaded(object sender, RoutedEventArgs e)
    {
        var presenter = (ItemsPresenter)this.Template.FindName("PART_Presenter", this);
        var stackPanel = (StackPanel)this.ItemsPanel.FindName("PART_StackPanel", presenter);
    }

FindName Метод находит только имена в расширенном шаблоне и ItemsPanel расширяется ItemsPresenter, не ItemsControl, В вашей ситуации "PART_StackPanel" всегда будет дитя "PART_Presenter" так что вы можете получить ссылку на это так:

    var stackPanel = (StackPanel)VisualTreeHelper.GetChild(presenter, 0);
Другие вопросы по тегам