Pull to refresh

GridView, и с чем его едят (часть вторая, большая)

Reading time 11 min
Views 21K
В прошлой вводной части я немного познакомил тех, кто не был знаком, с элементом GridView, предназначенным для отображения табличной информации на форме. Я рассказал о том, что GridView (для своего удобства я буду называть этот элемент далее везде как гридвью) можно связать с источником данных. Источников может быть несколько типов. В моих примерах везде будет в качестве источника использоваться ObjectDataSource.



Итак, как я уже сказал, используем ObjectDataSource. Эта часть будет посвящена именно ему. Ниже мы определим методы, возвращающие данные, добавим штришки, которые в будущем помогут нам при разработке из визуальной среды, а также повторю все действия, но на этот раз уже через codebehind страницы.

Условимся, что для разработки у нас используется VS 2008. Открываем студию, создаем новый проект типа ASP.NET WebSite, язык разработки – C#. Открываем страницу Default.aspx в режиме Split или Design. Бросаем на форму гридвью. Кликаем на стрелочку в правом верхнем углу гридвью, выбираем датасурс – new data source. Тип источника укажем Object. Переходим дальше и видм форму, где нас просят выбрать бизнес объект. image
Так как никаких объектов пока у нас нет, то выбрать мы ничего не сможем. Поэтому приступим к созданию нашего бизнес объекта.

Чтобы в будущем ускорить разработку объектов, создадим абстрактный класс. Этот класс будет содержать как минимум два публичных метода, общих для всех предков нашего класса. Первый – назовём его Select – будет возвращать нам массив объектов, второй – назовем его SelectCount – будет возвращать количество всех элементов, удовлетворяющих запросу. Не забудем пропейджинг, поэтому в качестве обязательных параметров для метода Select укажем два входных параметра: int maximumRows, int startRowIndex. Данные имена по умолчанию используются ObjectDataSource для передачи в метод количество строк для выборки и индекс, с которого начинать выбор. Для метода SelectCount пока никаких параметров не указываем.

[System.ComponentModel.DataObject]
public abstract class BusinessObject<T>
{
    protected T[] _objs;
    private int maximumRows, startRowIndex;

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, true)]
    public virtual T[] Select(int maximumRows, int startRowIndex)
    {
        this.maximumRows = maximumRows;
        this.startRowIndex = startRowIndex;

        List<T> arr = new List<T>();
        int to = startRowIndex + maximumRows;
        if (to > _objs.Length)
            to = _objs.Length;
        for (int i = startRowIndex; i < to; i++)
        {
            arr.Add(_objs[i]);
        }
        return arr.ToArray();
    }

    public int SelectCount()
    {
        return _objs.Length;
    }
}

* This source code was highlighted with Source Code Highlighter.

Рассотрим класс поподробнее. Что такое после имени класса я объяснять не буду. Незнающие могут погуглить по поводу генериков. Внутри класса объявлена переменная-массив _objs типа Т. По-умолчанию получается, что внутри каждого класса наследника у нас будет переменная, содержащая тип, который мы укажем (посмотрим дальше). Пока не буду объяснять к чему нам атрибуты у заголовка класса и метода Select, скажу лишь, что они на код никак не влияют, и вполне можно обойтись без них.
Метод Select сделаем виртуальным, чтобы иметь возможность переопределить его в классах-наследниках. Метод SelectCount возвращает все количество элементов для метода Select. У себя в проекте я условился, что для каждого селекта, возвращающего массив обектов, существует свой метод, возвращающий количество записей. Название этого метода всегда состоит из имени метода селекта плюс суффикс Count. Т.е. если у нас есть метод SelectById, то для него должен быть метод SelectByIdCount. Внимательные могли бы возразить мне, что для метода SelectById не нужно возвращать количество элементов, ибо этот метод будет всегда возвращать всего один элемент. Поэтому в данном случае мы можем не определять метод SelectByIdCount.

Идём дальше, создадим наш бизнес класс на основе абстрактного класса BusinesObject.

[System.ComponentModel.DataObject]
public class SimpleBusinesObject:BusinessObject<Person>
{
    private int cnt = 0;

    public SimpleBusinesObject()
    {
        List<Person> p = new List<Person>();
        for (int i = 0; i < 24; i++)
        {
            p.Add(new Person("John " + i.ToString(), i, "Lenina str., " + i.ToString()));
        }
        p.Add(new Person("Misha", "123-4567", "Kremlin"));
        _objs = p.ToArray();
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Person[] SelectByName(int maximumRows, int startRowIndex, String name)
    {
        List<Person> p = new List<Person>();
        for (int i = 0; i < _objs.Length; i++)
        {
            if (_objs[i].Name.IndexOf(name))
                p.Add(_objs[i]);
        }
        cnt = p.Count;
        return p.GetRange(startRowIndex, maximumRows).ToArray();
    }

    public int SelectByNameCount(String name)
    {
        return cnt;
    }
}


* This source code was highlighted with Source Code Highlighter.


Класс Person:

public class Person
{
    private String _name;
    private String _phone;
    private String _address;

    public String Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public String Phone
    {
        get { return _phone; }
        set { _phone = value; }
    }

    public String Address
    {
        get { return _address; }
        set { _address = value; }
    }

    public Person()
    {
        _name = "No Name";
        _phone = "555-5555";
        _address = "без определенного места жительства";
    }

    public Person(String name, String phone, String address)
    {
        _name = name;
        _phone = phone;
        _address = address;
    }
}

* This source code was highlighted with Source Code Highlighter.

Теперь можно заняться нашей страничкой default.aspx

image.
Как видно на скриншоте, мы можем выбрать наш созданный бизнес объект SimpleBusinessObject. Справа от списка с выбором бизнес объектов стоит галочка Show only data components. Что это такое, зачем оно нужно. Я сейчас объясню. Вспомним, какой атрибут мы указывали у нашего класса: [System.ComponentModel.DataObject]. Этим атрибутом мы помечаем классы, которые у нас используются для доступа к данным. Представьте. Если бы в нашем проекте было определено сотня и больше различных классов, было бы сложновато найти нужный нам объект. А использование этого атрибута вместе с установленной галочкой отфильтровывают только те классы. Которые нам нужны. На следующей форме, после выбора нашего бизнес объекта, нас просят выбрать метод, который будет использоваться для селекта. Здесь снова нам поможет атрибут [System.ComponentModel.DataObjectMethodAttribute (System.ComponentModel.DataObjectMethodType.Select, true)]
Которым мы помечаем наши методы. Возвращающие данные. В конце можно заметить, что у нас стоит true, это говорит о том, что данный метод будет сразу выбран в списке методов. Поэтому имеет смысл выставить для одно из методов true, если он будет использоваться чаще других. Так, для метода Select стоит true, а для метода SelectByName стоит false. Итак выбираем первый метод в списке, идем дальше, видим, что у нас есть список параметров. Здесь пока ничего менять не будем, жмем пимпу, готово. Теперь посмотрим на наш сгенерированный html код:
<asp:GridView ID="GridView1"
runat="server"
AllowPaging="True"
PageSize="10"
     AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1">
     <Columns>
         <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
         <asp:BoundField DataField="Phone" HeaderText="Phone" SortExpression="Phone" />
          <asp:BoundField DataField="Address" HeaderText="Address"
                    SortExpression="Address" />
      </Columns>
</asp:GridView>

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="Select"
     SelectCountMethod="SelectCount"
     EnablePaging="true"
     TypeName="SimpleBusinesObject">
</asp:ObjectDataSource>

* This source code was highlighted with Source Code Highlighter.

Я уже подправил здесь всё, что нужно, организовал пейджинг. Замечу, что если мы хотим пейджинг, который создан нами вручную (выбор идет именно того, что нужно и сколько нужно, без фильтрации необходимых элементов средствами asp.net), то к чёрту выкидываем все параметры из ObjectDataSource (если точнее, то не все, а только два, maximumRows и startRowIndex)! Это очень важное замечание. Ибо тут и бывает куча всяких проблем, вынос мозга и т.п. Еще раз отмечу:

Если пейджинг организован руками:
  • Выкидываем два веселых параметра maximumRows и startRowIndex, оставляя только те, которые задают дополнительную фильтрацию
  • Эти параметры не указываем в методе SelectCount, оставляем только те, которые задают дополнительную фильтрацию
  • Добавляем в GridView атрибуты AllowPaging=true, PageSize=сколько_нужно
  • Добавляем в ObjectDataSource атрибуты SelectCountMethod=имя_метода, EnablePaging=true

Запускаем наш проект и видим, что все прекрасно работает.

Теперь отобразим только список тех персон, корые имеют определеннуюподстроку в имени

<asp:GridView ID="GridView2" runat="server"
            AllowPaging="True"
            PageSize="10"
            AutoGenerateColumns="False"
            DataSourceID="ObjectDataSource2">
            <Columns>
                <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
                <asp:BoundField DataField="Phone" HeaderText="Phone" SortExpression="Phone" />
                <asp:BoundField DataField="Address" HeaderText="Address"
                    SortExpression="Address" />
            </Columns>
        </asp:GridView>
        <asp:ObjectDataSource ID="ObjectDataSource2" runat="server"
            SelectMethod="SelectByName"
            SelectCountMethod="SelectByNameCount"
            EnablePaging="true"
            TypeName="SimpleBusinesObject">
            <SelectParameters>
                <asp:QueryStringParameter DefaultValue="John" Name="name"
                    QueryStringField="name" Type="String" />
            </SelectParameters>
        </asp:ObjectDataSource>

* This source code was highlighted with Source Code Highlighter.

Лишнее я уже все убрал, добавил необходимое. Запускаем, ура! Работает. Но заметим, что последнюю страничку мы открыть не можем, ибо «Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.» Желающие могут исправить сие недоразумение и убедиться, что в конце списка у нас не будет персоны с именем Misha. А теперь можно сходить покурить (если Вы курите, либо попить кофейку), Представить себя мега крутым программистом на asp.net и забить на все 

Итак, мы рассмотрели простой пейджинг, отфильтровали все, что нам нужно. Сейчас лишь могу сделать несколько замечаний. Если вы используете ORM, то, вполне возможно, что Вам придется в методе Select переделать sql запрос таким образом, чтобы БД отдавала необходимую порцию данных. Также для метода SelectCount Вам снова придется переделать sql запрос, чтобы он не возвращал ничего, кроме количества элементов, удовлетворяющих запросу. Таким образом Вы значительно уменьшите объем передаваемых данных между сервером БД и веб сервером.

За сим откланяюсь, а в будущем, возможно, разберем сортировку, вставку новых элементов, удаление и обновление. Но это уже будет намного проще, ибо азы работы с GridView & ObjectDataSource мы уже имеем.

Ну и напоследок приложим созданный проект files.mail.ru/4UKA8Z

P.S. А. ну да. забыл ведь, завтра мы настроим гридвью и датасорс из codebehind.
Tags:
Hubs:
+14
Comments 25
Comments Comments 25

Articles