Add to Technorati Favorites

Сегодня мы поговорим об ItemsControl, DataBinding, VisualTreeHelper. Рано или поздно вы начнете создавать более или  менее серьезные приложения, которые будут решать конкретные задачи. И конечно же современным приложениям не обойтись без базы данных, а нам как программистам не обойтись без средств доступа к данным и их вывода.

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

Мы будем использовать ItemsControl он удобен для вывода любых данных с помощью DataBinding и позволяет нам менять шаблон, а в шаблоне по умолчанию содержится только StackPanel.

И так приступим. Кто не читал статью про DataBinding, советую для начала прочесть ее, чтобы вам было более понятно, что мы будем делать.
1) Создаем веб – сервис
2) Создаем наш класс, в нашем случае Film
3) Создаем метод которые вернет список наших Film
4) Получаем данные на стороне Silverlight
5) Биндим их к нашему ItemControls

Создайте проект. Затем в веб проекте добавляем веб сервис и у него создаем класс Film и метод отдающий список Film.
Подключаем Service Reference в проекте Silverlight.

Код нашего Веб сервиса:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace DataBindingExamples.Web
{
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]

    public class MyWebService : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Hello World";
        }

        [WebMethod]
        public List GetFimls ()
        {
            List list = new List();

            for (var i = 0; i < 10; i++)
            {
                Film film = new Film();
                film.Id = i;
                film.Title = String.Format("Film Title {0} ", i);
                film.FileSize = String.Format("{0}", 100*i);
                film.Time = i*200;
                film.Image = String.Format("http://localhost/silverlight/{0}.jpg", i);

                list.Add(film);
            }

            return list;
        }
    }

    public class Film
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public int Time { get; set; }
        public string FileSize { get; set; }
        public string Image { get; set; }
    }
}

Теперь нам нужен контрол, который будет отображать наши фильмы. Создаем ItemsControl и внутри прописываем DataBinding для всех полей нашего класса.

Код XAML:

<UserControl x:Class="DataBindingExamples.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="800">
<Grid x:Name="LayoutRoot" Background="White">
	<ItemsControl x:Name="FilmList">
			<ItemsControl.ItemTemplate>
			<DataTemplate>
				<StackPanel Orientation="Horizontal" Background="Bisque" Margin="0,5,0,5">
					<Image  Source="{Binding Image}" Width="30" Height="30" Margin="5" />
					<TextBlock Text="{Binding  Title}" Margin="5" />
					<TextBlock  Text="{Binding Time}" Margin="5" />
					<TextBlock  Text="{Binding FileSize}" Margin="5" />
				</StackPanel>
			</DataTemplate>
		</ItemsControl.ItemTemplate>
	</ItemsControl>
</Grid>
</UserControl>

Все очень просто теперь в CodeBehind добавлеем полученные данные от WebService в наш ItemsCotrol.

Код C#


using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DataBindingExamples.WebSrv;

namespace DataBindingExamples
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();

            var connector = new MyWebServiceSoapClient();
            connector.GetFimlsCompleted += connector_GetFimlsCompleted;
            connector.GetFimlsAsync();
        }

        void connector_GetFimlsCompleted(object sender, GetFimlsCompletedEventArgs e)
        {
            if (e.Error != null)
                return;

            if (e.Result.Count > 0)
            {
                List list = new List(e.Result);
                FilmList.ItemsSource = list;
            }
        }
    }
}

Посмотрим на результат. Все отлично. Выводиться как мы хотели. Но есть один очень большой недостаток. Который мне очень долго не удавлось исправить. У нас нет никакого события, которые позволяет, что то делать с элементами, которые генерируются, на основе нашего List.

Например, у нас стоит задача выделять какой то элемент при клике на него. Мы это можем сделать, повесив обработчик OnClick на наш StackPanel. Вот так:

Код XAML:


<StackPanel Orientation="Horizontal" Background="Bisque" Margin="0,5,0,5" MouseLeftButtonDown="StackPanel_MouseLeftButtonDown"  >
  <Image  Source="{Binding Image}" Width="30" Height="30" Margin="5" />
	  <TextBlock  Text="{Binding Title}" Margin="5" />
	  <TextBlock  Text="{Binding Time}" Margin="5" />
	  <TextBlock  Text="{Binding FileSize}" Margin="5" />
  </StackPanel>

Обрабатываем событие:

Код C#:

private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
	//для примера мы меняем его фон
	((StackPanel)sender).Background = new SolidColorBrush(Colors.Red);
}

Но если мы кликнем на другой элемент, мы поменяем его фон, а вот предыдущий у нас тоже останется выделенным, то есть мы не имеем к нему доступ. То есть мы не может получить доступ к коллекции элементов ItemsControl.

Конечно, это создает очень много неудобств и ограничение функционала. Изучение справки и помощь моего друга, помогло нам найти решение.  VisualTreeHelper класс помогающий нам проходить по коллекции элементов контрола с помощью его методов.

Для начала немного объясню, что у нас получается или точнее во что у нас генерируется ItemsControl. После того, как мы присвоили свойству ItemSource наш List мы получаем пример такую вот структуру.

<!--  Корневой  элемент представления контента ItemsControl -->
  <ItemsPresenter>
	  <!--  StackPanek по умолчанию, про которую я говорил содержит коллекцию элементов  ItemsControl -->
	  <StackPanel>
		  <ContentPresenter>
		  <!--  Наша текущая панель которую нам надо получить -->
			  <StackPanel>
				  <Image  />
				  <TextBlock  />
				  <TextBlock  />
				  <TextBlock  />
			  </StackPanel>
		  </ContentPresenter>
		  <ContentPresenter>
			  <StackPanel>
				  <Image  />
				  <TextBlock  />
				  <TextBlock  />
				  <TextBlock  />
			  </StackPanel>
		  </ContentPresenter>
		  <ContentPresenter>
			  <StackPanel>
				  <Image  />
				  <TextBlock  />
				  <TextBlock  />
				  <TextBlock  />
			  </StackPanel>
		  </ContentPresenter>
	  </StackPanel>
  </ItemsPresenter>
 

И так нам нужно достучаться до внутренних элементов и делаем мы это вот так:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DataBindingExamples.WebSrv;

namespace DataBindingExamples
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();

            var connector = new MyWebServiceSoapClient();
            connector.GetFimlsCompleted += connector_GetFimlsCompleted;
            connector.GetFimlsAsync();
        }

        void connector_GetFimlsCompleted(object sender, GetFimlsCompletedEventArgs e)
        {
            if (e.Error != null)
                return;

            if (e.Result.Count > 0)
            {
                List list = new List(e.Result);
                FilmList.ItemsSource = list;
            }
        }

        private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //Достаем ItemsPresenter для Нашего ItemsControl
            ItemsPresenter itemsPresenter = (ItemsPresenter)VisualTreeHelper.GetChild(FilmList, 0);
            //Достаем StackPanel для Нашего ItemsControl
            StackPanel rootStackPanel = (StackPanel)VisualTreeHelper.GetChild(itemsPresenter, 0);

            //бежим по коллекции ContentPresenter которые у нас генерируются в ItemsControl
            foreach (ContentPresenter current in rootStackPanel.Children)
            {
                //Наконец то получаем нужную нам StackPanel проверяем является ли она текущей, и выделяем ее
                StackPanel currentStackPanel = (StackPanel)VisualTreeHelper.GetChild(current, 0);
                if (currentStackPanel == (StackPanel)sender)
                    currentStackPanel.Background = new SolidColorBrush(Colors.Red);
                else
                    currentStackPanel.Background = new SolidColorBrush(Colors.Magenta);
            }
        }
    }
}

Вот что у нас получается, то есть мы можем теперь делать, что хотим с коллекцией элементов генерируемых в ItemsControl.

Достоинва:

Нет ни каких ограничений с доступом к любым элементам.

Недостатки:

Метод получения элементов не совсем очевиден. Громоздкий способ их получения, тем более, если внутренний элмент сложный. Придется переделывать весь доступ, если мы измени порядок или набор элементов.

PS – статья может показаться не совсем понятной некоторым посетителям из-за ее сложности. Постараюсь ответить на все вопросы.

Чтобы у вас отображались картинки. Создайте в IIS виртуальный каталог с именем silverlight, если у вас не стоит IIS (а он у вас должен стоять!), то воспользуйтесь другим вариантом. Положите файлы в корень проекта silverlight. Назовите все файлы соотвественно: 1.jpg, 2.jpg .. и т.д.

Popularity: 21% [?]

Add to zakladki:
2 комментария | Примеры | Вся статья

2 Комментария к статье “Управление контролами в ItemsControl (часть 1 - с помощью VisualTreeHelper)”

  1. [...] Управление контролами в ItemsControl (часть 1 - с помощью VisualTr… [...]

  2. [...] Управление контролами в ItemsControl (часть 1 - с помощью VisualTr… Posted ноя 29 2008, 12:36 by СильверРобот Помечено как: Блог, [...]

Leave a Reply

Ваш Комментарий * [b][/b] - [i][/i] - [u][/u]- [quote][/quote]