Хорошо, теперь легко увидеть, что здесь происходит. Подсказки были в твоем исходном вопросе, но мне было не очевидно, что ты делал, пока не опубликовал логическое дерево.
Как я и подозревал, ваша проблема вызвана отсутствием логического наследования: в большинстве примеров вы увидите, что в Интернете ContentPresenter будет представлять FrameworkElement, который будет логическим потомком ToolBar, поэтому при маршрутизации событий и FindAncestor будет работать даже тогда, когда визуальное дерево прерывается всплывающим окном.
В вашем случае отсутствует логическое древовидное соединение, потому что содержимое, представляемое ContentPresenter, не является FrameworkElement.
Другими словами, это позволит привязкам и маршрутизации событий работать даже внутри рекламодателя:
<Toolbar Width="15">
<MenuItem .../>
<MenuItem .../>
</Toolbar>
Но это не так:
<Toolbar Width="15">
<my:NonFrameworkElementObject />
<my:NonFrameworkElementObject />
</Toolbar>
Конечно, если ваши элементы являются производными от FrameworkElement, они могут быть элементами управления, и вы можете использовать ControlTemplate вместо DataTemplate. В качестве альтернативы они могут быть ContentPresenters, которые просто представляют свои элементы данных.
Если вы устанавливаете ItemsSource в коде, это легко изменить. Заменить это:
MyItems.ItemsSource = ComputeItems();
с этим:
MyItems.ItemsSource = ComputeItems()
.Select(item => new ContentPresenter { Content = item });
Если вы устанавливаете ItemsSource в XAML, метод, который я обычно использую, заключается в создании присоединенного свойства (например, «DataItemsSource») в моем собственном классе и установке PropertyChangedCallback, так что каждый раз, когда устанавливается DataItemsSource, он выполняет .Select (), показанный выше, для создания ContentPresenters и устанавливает ItemsSource. Вот мясо:
public class MyItemsSourceHelper ...
{
... RegisterAttached("DataItemsSource", ..., new FrameworkPropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var dataSource = GetDataItemsSource(obj);
obj.SetValue(ItemsControl.ItemsSource,
dataSource==null ? null :
dataSource.Select(item => new ContentPresenter { Content = item });
}
}
, который позволит это работать:
<Toolbar Width="15" DataTemplate="..."
my:MyItemsSourceHelper.DataItemsSource="{Binding myItems}" />
, где myItems - это набор не-1027 *, к которым применяется DataTemplate
. (Перечисление элементов в строке также возможно с <Toolbar.DataItemsSource><x:Array ...
)
Также обратите внимание, что этот метод обертывания элементов данных предполагает, что шаблон ваших данных применяется через стили, а не через ItemsControl.ItemTemplate property
. Если вы хотите применить шаблон через ItemsControl.ItemTemplate, вашим ContentPresenters необходимо добавить привязку к их свойству ContentTemplate
, которое использует FindAncestor для поиска шаблона в ItemsControl
. Это делается после «нового ContentPresenter» с использованием «SetBinding».
Надеюсь, это поможет.