Обычно это происходит, когда вы связываете ItemsControl.ItemsSource
с string
.ItemsControl
внутренне обращается к копии коллекции, связанной с индексом ItemsSource
, потому что он должен создать контейнер для каждого элемента данных (ItemContainerGenerator
), чтобы представить данные какVisual
object.
Так как string
реализует индексатор, такой как
public char this[int index] { get; }
, он доступен по индексу, как коллекция или массив.
Теперь, когда привязка string
к ItemsControl.ItemsSource
, string
копируется в коллекцию ItemsControl.Items
и передается во внутренний ItemContainerGenerator
, который отвечает за создание визуальногоэлементы, которые в конечном итоге отображаются как визуальное представление данных.Это ItemContainerGenerator
обрабатывает значение string
как коллекцию (поскольку string
реализует IEnumerable
) и обращается к нему по индексу.Благодаря реализованному индексатору string
вернет свои базовые символы, а затем генератор создаст контейнер для каждого.Вот почему string
выглядит разделенным.
Убедитесь, что вы всегда привязываете к коллекции string
, но никогда не к string
напрямую, чтобы избежать такого поведения.
Модель представления, которая предоставляет строковое значение и коллекцию строк для привязки
class ViewModel : INotifyPropertyChanged
{
private string stringValue;
public string StringValue
{
get => this.stringValue;
set
{
this.stringValue= value;
OnPropertyChanged();
}
}
private ObservableCollection<string> stringValues;
public ObservableCollection<string> StringValues
{
get => this.stringValues;
set
{
this.stringValues= value;
OnPropertyChanged();
}
}
}
MainWindow.xaml, где DataContext
- это класс ViewModel
<!-- This ComboBox will display the single characters of the string value (each item is a character)-->
<ComboBox x:Name="comboBox" ItemsSource="{Binding StringValue}" />
<!-- This ComboBox will display the strings of the StringValues collection (each item is a complete string) -->
<ComboBox x:Name="comboBox" ItemsSource="{Binding StringValues}" />
Отображаемые элементы ComboBox
(или ItemsControl
в целом) фактически являются контейнерами.Контейнер представляет собой визуальное представление данных и является сложным, как UserControl
.Контейнер имеет Border
, Background
, Padding
, Margin
и т. Д. Это Visual
, который состоит из других Visuals
(или элементов управления).Один string
не может быть отображен таким образом (со шрифтом, цветом шрифта, фоном и т. Д.).
Поэтому ItemsControl
должен создать визуальный контейнер для каждого объекта данных.
Это делается с помощью ItemsControl.ItemsPanel
, который фактически использует ItemContainerGenerator
для достижения этой цели.Итак, внутренне ComboBox
(или ItemsControl.ItemsPanel
) получает доступ к связанной коллекции ItemsControl.Items
, чтобы создать контейнер следующим образом:
IItemContainerGenerator generator = this.ItemContainerGenerator;
GeneratorPosition position = generator.GeneratorPositionFromIndex(0);
using (generator.StartAt(position, GeneratorDirection.Forward, true))
{
DependencyObject container = generator.GenerateNext();
generator.PrepareItemContainer(container);
}
Как вы можете видеть, генератор обращается к элементам с помощьюиндекс.Под капотом генератор получает доступ к копии ItemsControl.Items
(ItemContainerGenerator.ItemsInternal
), чтобы получить данные, которые должны храниться в контейнере.Это как-то (внутри генератора) выглядит так:
object item = ItemsInternal[position];
Так как string
реализует индексатор, вы можете получить доступ к string
также как к массиву:
var someText = "A String";
char firstCharacter = someText[0]; // References 'A' from "A String"
Так что при поискев приведенном выше коде генератора контейнеров теперь вы можете понять, какое влияние оказывают строки
GeneratorPosition position = generator.GeneratorPositionFromIndex(0);
и
generator.StartAt(position, GeneratorDirection.Forward, true)
на string
, где позиция - фактический индекс элемента:извлекает символ за символом для сопоставления их с контейнером.
Это упрощенное объяснение того, как ItemsControl
обрабатывает исходную коллекцию.