ListBox с Сеткой как ItemsPanelTemplate производит странные ошибки привязки

C ++ 11

Проблема

Хотя правила в 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::result_type, как известно, является типом - компилятор будет «волшебным образом» смотреть на такого рода зависимые типы, чтобы понять это.

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::f. Таким образом, вы получаете лучший из двух миров: «Отложенный» поиск защищает вас, если вы можете столкнуться с проблемами с зависимыми базовыми классами, а также «немедленный» поиск, который освобождает вас от typename и template.

Неизвестные специализации

В коде D имя typename D::questionable_type не является членом текущего экземпляра. Вместо этого язык обозначает его как члена неизвестной специализации . В частности, это всегда происходит, когда вы делаете DependentTypeName::Foo или DependentTypedName->Foo, и либо зависимый тип , ни текущий экземпляр (в этом случае компилятор может отказаться и сказать «мы будем Посмотрите позже, что Foo), или - текущий экземпляр, и имя не было найдено в нем или его независящих базовых классах, и есть также зависимые базовые классы.

Представьте, что произойдет, если у нас была функция-член h в пределах указанного выше шаблона класса A

void h() {
  typename A::questionable_type x;
}

. В C ++ 03 язык позволил поймать эту ошибку, потому что никогда не может быть действительный способ создания экземпляра A::h (любой аргумент, который вы даете T). В C ++ 11 теперь у пользователя есть дополнительная проверка, чтобы дать больше причин для компиляторов реализовать это правило. Поскольку A не имеет зависимого базовые классы и A объявляют ни одного члена questionable_type, имя A::questionable_type не является ни членом текущего экземпляра , ни членом неизвестной специализации. в этом случае что этот код не может быть достоверно скомпилирован во время создания экземпляра, поэтому язык запрещает имя, в котором спецификатор является текущим экземпляром, не является ни членом неизвестной специализации, ни членом текущего экземпляра (однако это нарушение все еще не является

Примеры и мелочи

Вы можете попробовать это знание на этом ответе и посмотреть, действительно ли эти определения имеют смысл для вас реальный пример (в этом ответе они несколько менее детализированы).

Правила 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.

35
задан akjoshi 15 March 2012 в 06:19
поделиться

5 ответов

Обязательная проблема возникает из стиля по умолчанию для 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 в Вашем пользовательском стиле.

31
ответ дан 27 November 2019 в 06:51
поделиться

Согласно Обзор Шаблонной обработки Данных MSDN, DataTemplates должен использоваться в качестве ItemTemplate, чтобы определить, как данные представлены, в то время как Style использовался бы в качестве ItemContainerStyle для моделирования просто сгенерированного контейнера, такой как ListBoxItem.

Однако кажется, что Вы пытаетесь использовать последнего, чтобы сделать задание первого. Я не могу воссоздать Вашу ситуацию без намного большего количества кода, но я подозреваю, что выполнение связывающий с данными в стиле контейнера могло бросать ключ в принятое визуальное/логичное дерево.

я также не могу не думать что пользовательский макет объектов на основе информационных призывов объекта к созданию пользовательского Panel. Для пользовательского Panel к расположению объекты, чем для объектов, вероятно, лучше разметить себя с выбором Rube Goldberg IValueConverters.

1
ответ дан 27 November 2019 в 06:51
поделиться

Это типичная проблема с ListBoxItem с и другое эфемерное *Item контейнеры. Они создаются асинхронно/на лету, в то время как эти ItemsControl загружается/представляется. Необходимо присоединить к ListBox.ItemContainerGenerator StatusChanged событие и ожидать Состояния для становления ItemsGenerated прежде, чем попытаться получить доступ к ним.

7
ответ дан 27 November 2019 в 06:51
поделиться

Установка OverridesDefaultStyle к True в Вашем ItemContainerStyle также решит эти проблемы.

<Style TargetType="ListBoxItem">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <!-- set the rest of your setters, including Template, here -->
</Style>
22
ответ дан 27 November 2019 в 06:51
поделиться

Если вы хотите полностью заменить шаблон 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 значения, вы можете удалить некоторые из них для повышения производительности.

1
ответ дан 27 November 2019 в 06:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: