Хотя правила в C ++ 03 о том, когда вам нужны typename
и template
, в значительной степени разумны, есть один неприятный недостаток его формулировки
template
struct A {
typedef int result_type;
void f() {
// error, "this" is dependent, "template" keyword needed
this->g();
// OK
g();
// error, "A" is dependent, "typename" keyword needed
A::result_type n1;
// OK
result_type n2;
}
template
void g();
};
. Как можно видеть, нам нужно ключевое слово disamiguation, даже если компилятор мог бы понять, что A::result_type
может быть только int
(и, следовательно, является типом) и this->g
может быть только членом шаблона g
, объявленного позже (даже если A
явно специализирован где-то, что не повлияет на код внутри этого шаблона, поэтому его значение не может быть затронуто более поздней специализацией A
!).
Чтобы улучшить ситуацию, на C ++ 11 язык отслеживает, когда тип относится к охватывающему шаблону. Чтобы знать это, тип должен быть сформирован с использованием определенной формы имени, которая является ее собственным именем (в приведенных выше, A
, A
, ::A
). Известно, что тип, на который ссылается такое имя, является текущим экземпляром . Может быть несколько типов, которые являются текущим экземпляром, если тип, из которого формируется имя, является членом / вложенным классом (тогда A::NestedClass
и A
являются текущими экземплярами).
Исходя из этого понятия, язык говорит, что CurrentInstantiation::Foo
, Foo
и CurrentInstantiationTyped->Foo
(такие как A *a = this; a->Foo
) являются участником текущего экземпляра , если они являются членами класса, который является текущим экземпляром или одним из его независящих базовых классов (просто выполнив поиск имени сразу).
Теперь ключевые слова typename
и template
больше не требуются, если определитель является членом текущего экземпляра. Здесь следует помнить, что A
- все еще имя, зависящее от типа (ведь T
также зависит от типа). Но A
, как известно, является типом - компилятор будет «волшебным образом» смотреть на такого рода зависимые типы, чтобы понять это.
struct B {
typedef int result_type;
};
template
struct C { }; // could be specialized!
template
struct D : B, C {
void f() {
// OK, member of current instantiation!
// A::result_type is not dependent: int
D::result_type r1;
// error, not a member of the current instantiation
D::questionable_type r2;
// OK for now - relying on C to provide it
// But not a member of the current instantiation
typename D::questionable_type r3;
}
};
Это впечатляет, но можем ли мы сделать лучше? Язык даже идет дальше, и требует , чтобы реализация снова смотрела D::result_type
при создании экземпляра D::f
(даже если она нашла свое значение уже во время определения). Когда результат поиска отличается или приводит к двусмысленности, программа плохо сформирована и должна быть дана диагностика. Представьте, что произойдет, если мы определим C
, как это
template<>
struct C {
typedef bool result_type;
typedef int questionable_type;
};
. Компилятор должен улавливать ошибку при создании экземпляра D
. Таким образом, вы получаете лучший из двух миров: «Отложенный» поиск защищает вас, если вы можете столкнуться с проблемами с зависимыми базовыми классами, а также «немедленный» поиск, который освобождает вас от typename
и template
.
В коде D
имя typename D::questionable_type
не является членом текущего экземпляра. Вместо этого язык обозначает его как члена неизвестной специализации . В частности, это всегда происходит, когда вы делаете DependentTypeName::Foo
или DependentTypedName->Foo
, и либо зависимый тип , ни текущий экземпляр (в этом случае компилятор может отказаться и сказать «мы будем Посмотрите позже, что Foo
), или - текущий экземпляр, и имя не было найдено в нем или его независящих базовых классах, и есть также зависимые базовые классы.
Представьте, что произойдет, если у нас была функция-член h
в пределах указанного выше шаблона класса A
void h() {
typename A::questionable_type x;
}
. В C ++ 03 язык позволил поймать эту ошибку, потому что никогда не может быть действительный способ создания экземпляра A
(любой аргумент, который вы даете T
). В C ++ 11 теперь у пользователя есть дополнительная проверка, чтобы дать больше причин для компиляторов реализовать это правило. Поскольку A
не имеет зависимого базовые классы и A
объявляют ни одного члена questionable_type
, имя A
не является ни членом текущего экземпляра , ни членом неизвестной специализации. в этом случае что этот код не может быть достоверно скомпилирован во время создания экземпляра, поэтому язык запрещает имя, в котором спецификатор является текущим экземпляром, не является ни членом неизвестной специализации, ни членом текущего экземпляра (однако это нарушение все еще не является
Вы можете попробовать это знание на этом ответе и посмотреть, действительно ли эти определения имеют смысл для вас реальный пример (в этом ответе они несколько менее детализированы).
Правила C ++ 11 делают неправильный код действующего C ++ 03 (который не был предназначен комитетом C ++, но, вероятно, не будет исправлен)
struct B { void f(); };
struct A : virtual B { void f(); };
template
struct C : virtual B, T {
void g() { this->f(); }
};
int main() {
C c; c.g();
}
Этот действительный код C ++ 03 связывает this->f
с A::f
во время создания экземпляра, и все в порядке. Однако C ++ 11 привязывает его к B::f
и требует двойной проверки при создании экземпляра, проверяя, соответствует ли поиск. Однако при создании экземпляра C::g
применяется правило Dominance Rule , и поиск будет искать A::f
.
Обязательная проблема возникает из стиля по умолчанию для ListBoxItem. По умолчанию при применении стилей к элементам WPF ищет стили по умолчанию и применяет каждое свойство, которое конкретно не установлено в пользовательском стиле от стиля по умолчанию. Обратитесь к это большое сообщение в блоге Ian Griffiths для получения дополнительной информации об этом поведении.
Назад к нашей проблеме. Вот стиль по умолчанию для ListBoxItem:
<Style
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
TargetType="{x:Type ListBoxItem}">
<Style.Resources>
<ResourceDictionary/>
</Style.Resources>
<Setter Property="Panel.Background">
<Setter.Value>
<SolidColorBrush>
#00FFFFFF
</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Control.HorizontalContentAlignment">
<Setter.Value>
<Binding Path="HorizontalContentAlignment" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl, AncestorLevel=1}"/>
</Setter.Value>
</Setter>
<Setter Property="Control.VerticalContentAlignment">
<Setter.Value>
<Binding Path="VerticalContentAlignment" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl, AncestorLevel=1}"/>
</Setter.Value>
</Setter>
<Setter Property="Control.Padding">
<Setter.Value>
<Thickness>
2,0,0,0
</Thickness>
</Setter.Value>
</Setter>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Примечание, что я удалил ControlTemplate для создания его компактным (я использовал StyleSnooper - для получения стиля). Вы видите, что существует привязка с относительным исходным набором предку с типом ItemsControl. Таким образом в Вашем случае ListBoxItems, которые создаются, когда привязка не нашла их ItemsControl. Можно ли предоставить большему количеству информации тем, каков ItemsSource для ListBox?
P.S.: Один способ удалить ошибки состоит в том, чтобы создать новые методы set для HorizontalContentAlignment и VerticalContentAlignment в Вашем пользовательском стиле.
Согласно Обзор Шаблонной обработки Данных MSDN, DataTemplates
должен использоваться в качестве ItemTemplate
, чтобы определить, как данные представлены, в то время как Style
использовался бы в качестве ItemContainerStyle
для моделирования просто сгенерированного контейнера, такой как ListBoxItem
.
Однако кажется, что Вы пытаетесь использовать последнего, чтобы сделать задание первого. Я не могу воссоздать Вашу ситуацию без намного большего количества кода, но я подозреваю, что выполнение связывающий с данными в стиле контейнера могло бросать ключ в принятое визуальное/логичное дерево.
я также не могу не думать что пользовательский макет объектов на основе информационных призывов объекта к созданию пользовательского Panel
. Для пользовательского Panel
к расположению объекты, чем для объектов, вероятно, лучше разметить себя с выбором Rube Goldberg IValueConverters
.
Это типичная проблема с ListBoxItem
с и другое эфемерное *Item
контейнеры. Они создаются асинхронно/на лету, в то время как эти ItemsControl
загружается/представляется. Необходимо присоединить к ListBox.ItemContainerGenerator
StatusChanged
событие и ожидать Состояния для становления ItemsGenerated
прежде, чем попытаться получить доступ к ним.
Установка OverridesDefaultStyle
к True
в Вашем ItemContainerStyle
также решит эти проблемы.
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<!-- set the rest of your setters, including Template, here -->
</Style>
Если вы хотите полностью заменить шаблон ListBoxItem
таким образом, чтобы не было видно выделения (возможно, вам нужен внешний вид ItemsControl
с группировкой / и т.д. поведение ListBox
), то вы можете использовать этот стиль:
<Style TargetType="ListBoxItem">
<Setter Property="Margin" Value="2" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
HorizontalAlignment="Stretch"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Этот шаблон также исключает стандартную оболочку Border
. Если вам это нужно, вы можете заменить шаблон следующим:
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
Если вам не нужны все эти TemplateBinding
значения, вы можете удалить некоторые из них для повышения производительности.