Привязка XML в WPF на примере программы чтения RSS-канала

Автор: Абхишек Сюр (Abhishek Sur)

Становящийся все популярнее XAML теперь появился и в приложениях для мобильных устройств. Недавно, изучая Интернет, я обнаружил, что последняя версия мобильных интерфейсов Windows 7 использует Silverlight для работы с интерфейсами. Мне тут же пришла в голову мысль попробовать Silverlight, чтобы расширить мою базу знаний. Таким образом, я установил этот пакет на свою машинку и попробовал. Он отлично работает, и мне действительно понравилось снова видеть Silverlight установленным на это мобильное устройство.

Вместо использования XNA для настоящего 3D-стиля построения приложения, я попробовал несколько простых программ для работы с этим выпуском Silverlight, и все отлично работало. Простое приложение работает максимально просто, как и обычные приложения Silverlight.

При создании простого приложения я обнаружил, что XML-привязка внутри приложения WPF — это интересная часть XAML-привязок. В этой статье я покажу, как можно реализовать привязку свойства с помощью XMLDataSource.

XML — это очень важная часть любого приложения. При создании приложения обычно нужно написать много кода для обработки потока данных XML. Загрузить данные с веб-сервера можно с локального компьютера или с помощью веб-клиента. В WPF встроена поддержка XML-привязок. Рассмотрим XML-привязки подробно на примере кода.

Пусть есть следующий фрагмент XML:

<data>
  <packet id="names">
    <Names description="All are selected">
      <nametype id="1" firstname="raja" lastname="das">58</nametype>
      <nametype id="2" firstname="anjan" lastname="chowdhuri">67</nametype>
      <nametype id="3" firstname="charu" lastname="singh">38</nametype>
      <nametype id="4" firstname="manju" lastname="sen">69</nametype>
      <nametype id="5" firstname="sanju" lastname="sharma">89</nametype>
      <nametype id="6" firstname="sanjana" lastname="mathur">77</nametype>
    </Names>
  </packet>
</data>

Теперь создадим код, привязывающий все элементы приведенного выше XML-фрагмента.

<Window.Resources>
        <XmlDataProvider Source="myxmldata.xml" x:Key="xdata" XPath="/data/packet/Names"></XmlDataProvider>
    </Window.Resources>

Прежде всего, мы добавили новое окно и добавили XmlDataSource в качестве ресурса. Обратите внимание, что также можно использовать x:Data, чтобы определить XML-код XmlDataSource прямо внутри файла, например так:

<XmlDataProvider x:Key="xdata" XPath="/data/packet/Names">
            <x:XData>
                <data >
                    <packet id="names">
                        <Names description="All are selected">
                            <nametype id="1" firstname="raja" lastname="das">58</nametype>
                            <nametype id="2" firstname="anjan" lastname="chowdhuri">67</nametype>
                            <nametype id="3" firstname="charu" lastname="singh">38</nametype>
                            <nametype id="4" firstname="manju" lastname="sen">69</nametype>
                            <nametype id="5" firstname="sanju" lastname="sharma">89</nametype>
                            <nametype id="6" firstname="sanjana" lastname="mathur">77</nametype>
                        </Names>
                    </packet>
                </data>
            </x:XData>
        </XmlDataProvider>

Обратите внимание, что я намеренно добавил xmlns в тег data, как если бы XML не должен был наследоваться от основного пространства имен. XPath внутри XmlDataProvider вызывает чтение XML-кода из указанного пути. Поэтому в случае нашего XML-кода для чтения данных будет использован путь /data/packet/names.

Перейдем к привязкам.

<Grid DataContext="{StaticResource xdata}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding XPath=@description, StringFormat='{}Status: {0}'}" Grid.Row="0"/>
        <ListBox x:Name="lstNames" ItemsSource="{Binding XPath=nametype}" IsSynchronizedWithCurrentItem="True" Grid.Row="1" SelectionChanged="lstNames_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock>
                        <TextBlock.Text>
                            <MultiBinding StringFormat="{}{0} {1} Got {2}">
                                <Binding XPath="@firstname"></Binding>
                                <Binding XPath="@lastname"></Binding>
                                <Binding XPath="text()"></Binding>
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <StatusBar Grid.Row="2">
            <TextBlock Text="Roll Number :"></TextBlock>
            <TextBlock x:Name="txtRoll"></TextBlock>
            <Separator/>
            <TextBlock Text="You Got : "></TextBlock> 
            <TextBlock Text="{Binding ElementName=lstNames, Path=SelectedItem.InnerText}"/> 
        </StatusBar>
    </Grid>

В приведенном выше XAML-коде можно видеть, что я вставил datacontext тега Grid (сетки) в ресурс XmlDataProvider.

Помните, что @ используется для атрибутов, а / используется для тегов. Поэтому первый блок текста содержит @description, что означает строку, написанную внутри атрибута описания (description) тега Names.

Tеперь, перейдя к коллекции имен, воспользуемся списком, чтобы показать все элементы XML-данных. Использование ItemsSource позволяет привязать данные с конкретным значением XPath. Здесь используется nametype, так как основная коллекция относится к типу nametype. В datatemplate (шаблон данных) помещен один TextBlock для каждого набора данных с множественной привязкой. Атрибуты firstname и lastname используются для привязки к свойству Text элемента TextBlock. В этой ситуации, как можно видеть, я использовал @firstname и @lastname, что будет гарантировать оценку модулем привязки элемента Attribute, а не элемента innerText. Строка состояния (StatusBar) позволяет показать привязанные данные для выбранного элемента SelectedItem списка ListBox.

Следует запомнить:

  1. Используйте XPath для перехода к пути конкретного узла.
  2. Перейдите к текущему элементу currentElement, используя XPath, и выберите @ для элемента attribute и текста text(), чтобы получить значение InnerText.
  3. / используется для перехода к дочерним узлам. 
  4. Если XML-код определен внутри XAML, не забудьте указать пустое пространство имен. В противном случае оно будет наследоваться от пространства имен XAML.

Небольшая программа чтения RSS-канала

Теперь, прояснив все моменты, построим пример программы чтения RSS-канала.

RSS-канал — это специальная XML-схема, поэтому при загрузке RSS-канала в элемент XMLDataProvider понадобится выполнить ее разбор в специальном формате. Посмотрим, на что похож XAML-код.

<Window.Resources>
        <XmlDataProvider x:Key="dnt" 
                     Source="http://www.abhisheksur.com/feeds/posts/default?alt=rss" 
                     XPath="/rss/channel" />
    </Window.Resources>
    <StackPanel DataContext="{Binding Source={StaticResource dnt}}">
        <StackPanel>
            <TextBlock Text="Title : " />
            <Label Content="{Binding XPath=title}"/>
            <TextBlock Text="Description : "/>
            <Label Content="{Binding XPath=description}"/>
        </StackPanel>
        <ListBox    IsSynchronizedWithCurrentItem="True" 
                    ItemsSource="{Binding XPath=item}"
                    MaxHeight="500" 
                    ScrollViewer.CanContentScroll="False">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding XPath=title}" FontSize="30" />
                        <TextBlock Text="{Binding XPath=link}" FontSize="12" />
                        <Expander Header="Read..">
                            <TextBlock Text="{Binding XPath=./description}" TextWrapping="Wrap" TextTrimming="WordEllipsis" MaxWidth="520"/>
                        </Expander>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
</StackPanel>

Код этого примера выглядит почти так же, как уже обсужденный код. Основное отличие состоит в том, что теперь выполняется разбор XML-кода, поступающего из RSS-канала. Здесь XMLDataProvider указывает на внешний веб-адрес. Программе после запуска требуется около секунды для загрузки RSS-канала из внешнего источника. После его загрузки конкретные узлы привязываются к заголовку (Title), а описания и элементы показываются в списке ListBox.

Загрузить пример приложения

Надеюсь, что эта статья поможет понять, как можно загрузить XML-код, используя привязку. Если появятся какие-то вопросы, напишите мне. Спасибо за внимание.