Поделиться через


Обзор модели содержимого элементов управления

Обновлен: Ноябрь 2007

В этом разделе обсуждаются модели содержимого, используемые классами, которые наследуют от класса Control. Модель содержимого определяет типы объектов, которые могут содержаться в элементе управления. В данном разделе термин «элемент управления» обозначает класс, содержащий в своей иерархии класс Control. Четыре модели содержимого, рассматриваемые в этом разделе, определяются следующими четырьмя классами, которые наследуют от класса Control:

  • Класс ContentControl содержит один элемент.

  • Класс HeaderedContentControl содержит заголовок и отдельный элемент.

  • Класс ItemsControl содержит коллекцию элементов.

  • Класс HeaderedItemsControl содержит заголовок и коллекцию элементов.

Эти четыре класса выступают в качестве базовых для большинства элементов управления в приложении WPF. Классы, использующие эти модели содержимого, могут содержать одинаковые типы содержимого и обрабатывать содержимое сходным образом; любой тип объекта, который может быть помещен в класс ContentControl (или класс, наследуемый от ContentControl), может быть помещен в элемент управления, имеющий любую из трех других моделей содержимого. На следующем рисунке показан один элемент управления из каждой модели содержимого, содержащий изображение и текст.

Button, GroupBox, Listbax, TreeViewItem

В этом разделе содержатся следующие подразделы.

  • Предварительные условия
  • ContentControl
  • HeaderedContentControl
  • ItemsControl
  • HeaderedItemsControl
  • Связанные разделы

Предварительные условия

В этом разделе предполагается, что пользователь имеет базовое представление о приложении WPF и знает, как добавлять в него элементы управления. Дополнительные сведения см. в разделах Введение в Windows Presentation Foundation и Общие сведения об элементах управления.

ContentControl

Наиболее простым из четырех моделей содержимого является объект ContentControl, который имеет свойство Content. Свойство Content принадлежит типу Object, поэтому нет ограничений того, что можно поместить в объект ContentControl. Можно использовать Язык XAML (Extensible Application Markup Language) или код для установки свойства Content.

Модель содержимого ContentControl используют следующие элементы управления:

В следующем примере демонстрируется создание четырех элементов управления Button, для которых установлено одно из следующих значений Content:

Bb613556.alert_note(ru-ru,VS.90).gifПримечание.

В примере версии Язык XAML (Extensible Application Markup Language) можно заключить содержимое каждой кнопки в теги <Button.Content>, но это не обязательно. Дополнительные сведения см. в разделе Общие сведения о XAML.

<!--Create a Button with a string as its content.-->
<Button>This is string content of a Button</Button>

<!--Create a Button with a DateTime object as its content.-->
<Button xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
</Button>

<!--Create a Button with a single UIElement as its content.-->
<Button>
  <Rectangle Height="40" Width="40" Fill="Blue"/>
</Button>

<!--Create a Button with a panel that contains multiple objects 
as its content.-->
<Button>
  <StackPanel>
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <TextBlock TextAlignment="Center">Button</TextBlock>
  </StackPanel>
</Button>
' Add a string to a button.
Dim stringContent As New Button()
stringContent.Content = "This is string content of a Button"

' Add a DateTime object to a button.
Dim objectContent As New Button()
Dim dateTime1 As New DateTime(2004, 3, 4, 13, 6, 55)

objectContent.Content = dateTime1

' Add a single UIElement to a button.
Dim uiElementContent As New Button()

Dim rect1 As New Rectangle()
rect1.Width = 40
rect1.Height = 40
rect1.Fill = Brushes.Blue
uiElementContent.Content = rect1

' Add a panel that contains multpile objects to a button.
Dim panelContent As New Button()
Dim stackPanel1 As New StackPanel()
Dim ellipse1 As New Ellipse()
Dim textBlock1 As New TextBlock()

ellipse1.Width = 40
ellipse1.Height = 40
ellipse1.Fill = Brushes.Blue

textBlock1.TextAlignment = TextAlignment.Center
textBlock1.Text = "Button"

stackPanel1.Children.Add(ellipse1)
stackPanel1.Children.Add(textBlock1)

panelContent.Content = stackPanel1
// Create a Button with a string as its content.
Button stringContent = new Button();
stringContent.Content = "This is string content of a Button";

// Create a Button with a DateTime object as its content.
Button objectContent = new Button();
DateTime dateTime1 = new DateTime(2004, 3, 4, 13, 6, 55);

objectContent.Content = dateTime1;

// Create a Button with a single UIElement as its content.
Button uiElementContent = new Button();

Rectangle rect1 = new Rectangle();
rect1.Width = 40;
rect1.Height = 40;
rect1.Fill = Brushes.Blue;
uiElementContent.Content = rect1;

// Create a Button with a panel that contains multiple objects 
// as its content.
Button panelContent = new Button();
StackPanel stackPanel1 = new StackPanel();
Ellipse ellipse1 = new Ellipse();
TextBlock textBlock1 = new TextBlock();

ellipse1.Width = 40;
ellipse1.Height = 40;
ellipse1.Fill = Brushes.Blue;

textBlock1.TextAlignment = TextAlignment.Center;
textBlock1.Text = "Button";

stackPanel1.Children.Add(ellipse1);
stackPanel1.Children.Add(textBlock1);

panelContent.Content = stackPanel1;

На следующем рисунке показаны четыре кнопки, созданные в предыдущем примере.

Четыре кнопки

HeaderedContentControl

Объект HeaderedContentControl наследует свойство Content от объекта ContentControl и определяет свойство Header, принадлежащее типу Object. Свойство Header предоставляет заголовок для элемента управления. Подобно свойству Content объекта ContentControl, свойство Header может принадлежать любому типу. Приложение WPF поставляется с тремя элементами управления, которые наследуют от объекта HeaderedContentControl:

В следующем примере создается объект TabControl (ItemsControl), содержащий два объекта TabItem. Первый объект TabItem содержит форматированное содержимое в свойствах Header и Content: для свойства Header установлен объект StackPanel, который содержит объекты Ellipse и TextBlock, а для свойства Content установлен объект StackPanel, который содержит объекты TextBlock и Label. Для свойства Header второго объекта TabItem установлена строка, а для свойства Content установлен один объект TextBlock.

<TabControl>
  <TabItem>
    <TabItem.Header>
      <StackPanel Orientation="Horizontal">
        <Ellipse Width="10" Height="10" Fill="DarkGray"/>
        <TextBlock>Tab 1</TextBlock>
      </StackPanel>
    </TabItem.Header>
    <StackPanel>
      <TextBlock>Enter some text</TextBlock>
      <TextBox Name="textBox1" Width="50"/>
    </StackPanel>
  </TabItem>
  <TabItem Header="Tab 2">
    <!--Bind TextBlock.Text to the TextBox on the first
    TabItem.-->
    <TextBlock Text="{Binding ElementName=textBox1, Path=Text}"/>
  </TabItem>
</TabControl>

На следующем рисунке показан объект TabControl, созданный в предыдущем примере.

TabControl

ItemsControl

Элементы управления, которые наследуют от объекта ItemsControl, содержат коллекцию объектов. Примером объекта ItemsControl является объект ListBox. Для заполнения объекта ItemsControl можно использовать свойство ItemsSource или Items.

Свойство ItemsSource

Свойство ItemsSource объекта ItemsControl позволяет использовать любой тип, реализующий интерфейс IEnumerable как содержимое объекта ItemsControl. Свойство ItemsSource обычно используется для отображения коллекции данных или привязки объекта ItemsControl к объекту коллекции.

В следующем примере создается класс с именем MyData, являющийся простой коллекцией строк.

Public Class MyData
    Inherits ObservableCollection(Of String)

    Public Sub New()  '

        Add("Item 1")
        Add("Item 2")
        Add("Item 3")

    End Sub 'New
End Class 'MyData
public class MyData : ObservableCollection<string>
{
    public MyData()
    {
        Add("Item 1");
        Add("Item 2");
        Add("Item 3");
    }
}

В следующем примере демонстрируется привязка свойства ItemsSource к объекту MyData.

<!--Create an instance of MyData as a resource.-->
<src:MyData x:Key="dataList"/>


...


<ListBox ItemsSource="{Binding Source={StaticResource dataList}}"/>
Dim listBox1 As New ListBox()
Dim listData As New MyData()
Dim binding1 As New Binding()

binding1.Source = listData
listBox1.SetBinding(ListBox.ItemsSourceProperty, binding1)
ListBox listBox1 = new ListBox();
MyData listData = new MyData();
Binding binding1 = new Binding();

binding1.Source = listData;
listBox1.SetBinding(ListBox.ItemsSourceProperty, binding1);

На следующем рисунке показан объект ListBox, созданный в предыдущем примере.

ListBox

Дополнительные сведения о привязке данных см. в разделе Общие сведения о связывании данных.

Свойство элементов

Если не требуется использовать объект, который реализует интерфейс IEnumerable для заполнения объекта ItemsControl, можно добавить элементы с помощью свойства Items. Элементы в объекте ItemsControl могут иметь типы, отличные друг от друга. Например, объект ListBox может содержать один элемент, который является строкой, и другой элемент, являющийся объектом Image.

Bb613556.alert_note(ru-ru,VS.90).gifПримечание.

   После установки свойства ItemsSource можно использовать свойство Items для чтения коллекции ItemCollection, но нельзя добавлять или изменять элементы в коллекции ItemCollection. Установка для свойства ItemsSource значения ссылки NULL (Nothing в Visual Basic) удаляет коллекцию и восстанавливает возможность использования свойства Items, которое будет пустым объектом ItemCollection.

В следующем примере создается объект ListBox с четырьмя различными типами элементов.

<!--Create a ListBox that contains a string, a Rectangle,
     a Panel, and a DateTime object. These items can be accessed
     via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
         Name="simpleListBox">

  <!-- The <ListBox.Items> element is implicitly used.-->
  This is a string in a ListBox

  <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>

  <Rectangle Height="40" Width="40"  Fill="Blue"/>

  <StackPanel Name="itemToSelect">
    <Ellipse Height="40" Fill="Blue"/>
    <TextBlock>Text below an Ellipse</TextBlock>
  </StackPanel>

  <TextBlock>String in a TextBlock</TextBlock>
  <!--</ListBox.Items>-->
</ListBox>
' Create a Button with a string as its content.
listBox1.Items.Add("This is a string in a ListBox")

' Create a Button with a DateTime object as its content.
Dim dateTime1 As New DateTime(2004, 3, 4, 13, 6, 55)

listBox1.Items.Add(dateTime1)

' Create a Button with a single UIElement as its content.
Dim rect1 As New Rectangle()
rect1.Width = 40
rect1.Height = 40
rect1.Fill = Brushes.Blue
listBox1.Items.Add(rect1)

' Create a Button with a panel that contains multiple objects 
' as its content.
Dim ellipse1 As New Ellipse()
Dim textBlock1 As New TextBlock()

ellipse1.Width = 40
ellipse1.Height = 40
ellipse1.Fill = Brushes.Blue

textBlock1.TextAlignment = TextAlignment.Center
textBlock1.Text = "Text below an Ellipse"

stackPanel1.Children.Add(ellipse1)
stackPanel1.Children.Add(textBlock1)

listBox1.Items.Add(stackPanel1)
// Add a String to the ListBox.
listBox1.Items.Add("This is a string in a ListBox");

// Add a DateTime object to a ListBox.
DateTime dateTime1 = new DateTime(2004, 3, 4, 13, 6, 55);

listBox1.Items.Add(dateTime1);

// Add a Rectangle to the ListBox.
Rectangle rect1 = new Rectangle();
rect1.Width = 40;
rect1.Height = 40;
rect1.Fill = Brushes.Blue;
listBox1.Items.Add(rect1);

// Add a panel that contains multpile objects to the ListBox.
Ellipse ellipse1 = new Ellipse();
TextBlock textBlock1 = new TextBlock();

ellipse1.Width = 40;
ellipse1.Height = 40;
ellipse1.Fill = Brushes.Blue;

textBlock1.TextAlignment = TextAlignment.Center;
textBlock1.Text = "Text below an Ellipse";

stackPanel1.Children.Add(ellipse1);
stackPanel1.Children.Add(textBlock1);

listBox1.Items.Add(stackPanel1);

На следующем рисунке показан объект ListBox, созданный в предыдущем примере.

ListBox с четырьмя типами содержимого

Классы контейнеров элементов

Каждый объект ItemsControl, который поставляется с приложением WPF, имеет соответствующий класс, представляющий элемент в объекте ItemsControl. В следующей таблице перечислены объекты ItemsControl, которые поставляются с приложением WPF, и соответствующие им контейнеры элементов.

Объект ItemsControl

Контейнер элемента

ComboBox

ComboBoxItem

ContextMenu

MenuItem

ListBox

ListBoxItem

ListView

ListViewItem

Menu

MenuItem

StatusBar

StatusBarItem

TabControl

TabItem

TreeView

TreeViewItem

Можно явно создать контейнер для каждого элемента в объекте ItemsControl, но это не обязательно. Создавать ли контейнер элемента в объекте ItemsControl в большей степени зависит от сценария. Например, если связать данные со свойством ItemsSource, не нужно явно создавать контейнер элемента. Необходимо иметь в виду следующие важные моменты.

  • Тип объектов в классе ItemCollection отличается в зависимости от того, создается ли явно контейнер элемента.

  • Можно получить контейнер элемента, даже если не создавать его явно.

  • Объект Style со свойством TargetType, установленным для контейнера элемента, применяется независимо от того, явно ли создан контейнер элемента.

  • Наследование свойств различается для явно и неявно созданных контейнеров элементов, так как только явно созданный контейнер элементов является частью логического дерева.

Чтобы проиллюстрировать эти моменты, в следующем примере создаются два элемента управления ListBox. В примере создаются объекты ListBoxItem для первого объекта ListBox, но не для второго объекта ListBox. Во втором случае объект ListBoxItem неявно создан для каждого элемента в объекте ListBox.

<!--Explicitly create a ListBoxItem for each item in the ListBox-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
         Name="listBoxItemListBox">
  <!-- The <ListBox.Items> element is implicitly used.-->
  <ListBoxItem>
    This is a string in a ListBox
  </ListBoxItem>
  <ListBoxItem>
    <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
  </ListBoxItem>
  <ListBoxItem>
    <Rectangle Height="40" Width="40" Fill="Blue"/>
  </ListBoxItem>
  <ListBoxItem>
    <StackPanel>
      <Ellipse Height="40" Width="40" Fill="Blue"/>
      <TextBlock>Text below an Ellipse</TextBlock>
    </StackPanel>
  </ListBoxItem>
  <!--</ListBox.Items>-->
</ListBox>


...


<!--Create a ListBox that contains a string, a Rectangle,
     a Panel, and a DateTime object. These items can be accessed
     via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
         Name="simpleListBox">

  <!-- The <ListBox.Items> element is implicitly used.-->
  This is a string in a ListBox

  <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>

  <Rectangle Height="40" Width="40"  Fill="Blue"/>

  <StackPanel Name="itemToSelect">
    <Ellipse Height="40" Fill="Blue"/>
    <TextBlock>Text below an Ellipse</TextBlock>
  </StackPanel>

  <TextBlock>String in a TextBlock</TextBlock>
  <!--</ListBox.Items>-->
</ListBox>

Объект ItemCollection для каждого объекта ListBox отличается. Каждый элемент в свойстве Items первого объекта ListBox является объектом ListBoxItem, но является другим типом во втором объекте ListBox. В следующем примере это подтверждается путем перебора элементов в обоих элементах управления ListBox и проверкой типа каждого элемента.

    Console.WriteLine("Items in simpleListBox:")

    For Each item As Object In simpleListBox.Items
        Console.WriteLine(item.GetType().ToString())
    Next item

    Console.WriteLine(vbCr + "Items in listBoxItemListBox:")

    For Each item As Object In listBoxItemListBox.Items
        Console.WriteLine(item.GetType().ToString())
    Next item

End Sub 'ReportLBIs


...


'
'        Items in simpleListBox:
'        System.String
'        System.Windows.Shapes.Rectangle
'        System.Windows.Controls.StackPanel
'        System.DateTime
'
'        Items in listBoxItemListBox:
'        System.Windows.Controls.ListBoxItem
'        System.Windows.Controls.ListBoxItem
'        System.Windows.Controls.ListBoxItem
'        System.Windows.Controls.ListBoxItem
'        
Console.WriteLine("Items in simpleListBox:");
foreach (object item in simpleListBox.Items)
{
    Console.WriteLine(item.GetType().ToString());
}

Console.WriteLine("\rItems in listBoxItemListBox:");

foreach (object item in listBoxItemListBox.Items)
{
    Console.WriteLine(item.GetType().ToString());
}


...


/*
Items in simpleListBox:
System.String
System.Windows.Shapes.Rectangle
System.Windows.Controls.StackPanel
System.DateTime

Items in listBoxItemListBox:
System.Windows.Controls.ListBoxItem
System.Windows.Controls.ListBoxItem
System.Windows.Controls.ListBoxItem
System.Windows.Controls.ListBoxItem
*/

На следующем рисунке показано два элемента управления ListBox, которые были созданы в предыдущем примере.

Сравнивает явные и не явные контейнеры элементов

Часто требуется контейнер для элемента, но в приложении нет явно созданного контейнера. Чтобы получить контейнер элемента, который связан с конкретным элементом, используйте метод ContainerFromItem. В следующем примере показано, как получить контейнер, связанный с элементом, когда не создан явно объект ListBoxItem. В примере предполагается, что объект с именем itemToSelect не является элементом ListBoxItem и был добавлен в объект ListBox, simpleListBox.

Dim lbi As ListBoxItem = _
    CType(simpleListBox.ItemContainerGenerator.ContainerFromItem(itemToSelect),  _
          ListBoxItem)

If Not (lbi Is Nothing) Then
    lbi.IsSelected = True
End If
ListBoxItem lbi =
    simpleListBox.ItemContainerGenerator.ContainerFromItem(itemToSelect) 
    as ListBoxItem;

if (lbi != null)
{
    lbi.IsSelected = true;
}

Стили, содержащие свойство TargetType, для которого задано значение контейнера элемента, применяются к явно и неявно созданным контейнерам элементов. В следующем примере создается стиль Style в качестве ресурса объекта ListBoxItem, который центрирует по горизонтали содержимое в объекте ListBoxItem. Когда этот стиль применяется к объектам ListBox, элементы в обоих объектах ListBox выравниваются по центру.

<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
  <Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>

На следующем рисунке показано два элемента управления ListBox после применения стиля из предыдущего примера.

Два элемента управления ListBox

Порядок работы наследования свойства со стилями и контейнерами элементов зависит от структуры логического дерева. При явном создании контейнера элемента он является частью логического дерева. Если не создавать контейнер элемента, он не будет являться частью логического дерева. На следующем рисунке показано различие в логическом дереве двух элементов управления ListBox из предыдущего примера.

Визуальные деревья для двух объектов ListBox

Объекты, наследующие от класса Visual, наследуют значения свойств от их логического родительского объекта. В следующем примере создается объект ListBox с двумя элементами управления TextBlock и для свойства Foreground объекта ListBox устанавливается значение синего цвета. Первый объект TextBlock, textBlock1, содержится в явно созданном объекте ListBoxItem, а второй объект TextBlock, textBlock2, — не содержится. В примере также определяется объект Style для элементаListBoxItem, задающий для свойства Foreground объекта ListBoxItem значение зеленого цвета.

<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
  <Setter Property="Foreground" Value="Green"/>
</Style>


...


<ListBox Foreground="Blue">
  <ListBoxItem>
    <TextBlock Name="textBlock1">TextBlock in a ListBoxItem.</TextBlock>
  </ListBoxItem>
  <TextBlock Name="textBlock2">TextBlock not in a ListBoxItem.</TextBlock>
</ListBox>

На следующем рисунке показан объект ListBox, созданный в предыдущем примере.

Два ListBoxItems в ListBox

Строка в объекте textBlock1 зеленого цвета, а строка в объекте textBlock2 — синего, так как каждый элемент управления TextBlock наследует свойство Foreground от соответствующего логического родительского объекта. Логическим родительским объектом textBox1 является ListBoxItem, а логическим родительским объектом textBox2 является ListBox. Дополнительные сведения см. в разделе Наследование значения свойства.

HeaderedItemsControl

Объект HeaderedItemsControl наследует от класса ItemsControl. Объект HeaderedItemsControl определяет свойство Header, которое следует тем же правилам, что и свойство Header объекта HeaderedContentControl. Приложение WPF поставляется с тремя элементами управления, которые наследуют от класса HeaderedItemsControl:

В следующем примере создается объект TreeViewItem. Элемент управления TreeView содержит один объект TreeViewItem, который имеет подпись TreeViewItem 1 и содержит следующие элементы:

  • Строка.

  • Объект DateTime.

  • Объект TreeViewItem, который содержит Rectangle в своем свойстве Header.

  • Объект TreeViewItem, для свойства которого Header установлено значение элемента управления StackPanel, содержащего два объекта.

Bb613556.alert_note(ru-ru,VS.90).gifПримечание.

В примере явно создаются объекты TreeViewItem для двух последних элементов, поскольку объекты Rectangle и StackPanel наследуют от класса Visual. Стиль по умолчанию для объекта TreeViewItem устанавливает свойство Foreground. Дочерние объекты наследуют значение этого свойства от явно созданного объекта TreeViewItem, который обычно имеет требуемое поведение.

<TreeView xmlns:sys="clr-namespace:System;assembly=mscorlib"
          Margin="10">
  <TreeViewItem Header="TreeViewItem 1" IsExpanded="True">
    TreeViewItem 1a
    <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
    <TreeViewItem>
      <TreeViewItem.Header>
        <Rectangle Height="10" Width="10" Fill="Blue"/>
      </TreeViewItem.Header>
    </TreeViewItem>
    <TreeViewItem>
      <TreeViewItem.Header>
        <StackPanel Orientation="Horizontal">
          <Ellipse Width="10" Height="10" Fill="DarkGray"/>
          <TextBlock >TreeViewItem 1d</TextBlock>
        </StackPanel>
      </TreeViewItem.Header>
    </TreeViewItem>
  </TreeViewItem>
</TreeView>

См. также

Основные понятия

Модель содержимого WPF