Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > VB .NET > DataGridView - одинаковые записи


Автор: BugZZ 21.10.2009, 22:42
Привет, Эксперты! 
Итак. Добрались руки до VS 2008 Sp1 и до парочки XML документов, которые надо прочитать
Начну с задумки:

Сделать тулзу которая читает n-е кол-во XML документов и из них всех вытаскивает только один определённый элемент и записывает его значение в DataGridView

Что реализовано:
 - выбор неопребеделённого кол-ва файлов черех OpenFileDialog
 - чтение всех выбраных фалоф на предмет нужного эелемента 
 - сохраниние содержания элемента в "таблицу" (ДатаГридВью)

проблема: 
 - после прочтение более чем однго файла, в DGV (DataGridView) появляюся одинаковые записи - что абсолютно нормально

пояснение:
 - каждый xml файл - это результат онлайн воутинга к фотографиям. К каждому JPG был создан XML в котором написано с какого ИП какую оценку поставили. Чтоб небыло жульничество, в XML сохраняются ТОЛЬКО разные ИП - повторов нет. 
НО, с одного ИП было проголосовано за несколько фотографий - отсюда один ИП может быть записан сразу в нескольких xml-файлах. Отсюда и двойные, тройные ... n-e записи одного ИП в DGV после прочтения БОЛЕЕ ОДНОГО xml-файлов

Цель:
 - Заставить XMLTextReader сохранять данные элемента только в том случае если запись НЕ присутвует в DGV

моя логика, которая к сожалению не работает, точнее реализовать которую так и не получилось....:
1. Если DGv - пуст -> читаю первый xml-файл в DGv
2. Если DGV НЕ пуст -> проверяю, при добавлении новой записи, есть ли в DGV уже такая. НЕТ - добавляю. ДА - иду дальше...

пример XML-файла:
Код

<ratings>
    <rating>
        <userid>0</userid>
        <username>213.182.125.18</username>
        <date>14:21:08 12-Sep-2009</date>
        <mark>5</mark>
    </rating>
    <rating>
        <userid>0</userid>
        <username>79.226.188.115</username>
        <date>14:58:47 12-Sep-2009</date>
        <mark>5</mark>
    </rating>
    <rating>
        <userid>0</userid>
        <username>84.135.202.249</username>
        <date>15:13:22 12-Sep-2009</date>
        <mark>1</mark>
    </rating>
    <rating>
        <userid>0</userid>
        <username>79.197.108.160</username>
        <date>16:14:09 12-Sep-2009</date>
        <mark>5</mark>
    </rating>
    <rating>
        <userid>0</userid>
        <username>212.100.60.181</username>
        <date>18:41:28 12-Sep-2009</date>
        <mark>1</mark>
    </rating>
    <rating>
        <userid>0</userid>
        <username>84.134.178.188</username>
        <date>21:21:28 12-Sep-2009</date>
        <mark>1</mark>
    </rating>
    <rating>
    .............
    </rating>
</ratings>


в DGV должны сохранятся ТОЛЬКО ИП. Тобиш значение элемента <username>


код, который добавляет все найденые username во всех выделенных файлах
Код

Imports System.Xml

Public Class Form1

    Private Sub btn_add_ips_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_add_ips.Click
        If myOpenFileDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
            lst_loaded_files.Visible = True
            lbl_dateien.Visible = True
            Dim List_with_path As Array = (myOpenFileDialog.FileNames)
            lst_loaded_files.Items.Clear()
            lst_loaded_files.Items.AddRange(myOpenFileDialog.SafeFileNames)
            dgv_ips.Rows.Clear()
            For i As Integer = 0 To List_with_path.Length - 1


                Dim myXmlReader As New XmlTextReader(List_with_path(i).ToString)
                While myXmlReader.Read()
                    If myXmlReader.NodeType = XmlNodeType.Element And myXmlReader.Name = "username" Then
                        dgv_ips.Rows.Add(myXmlReader.ReadElementString)
                    End If
                End While
            Next
        End If
        dgv_ips.Sort(dgv_ips.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
        lst_loaded_files.ForeColor = Color.Green()
    End Sub


Буду рад советам, подсказам ну или какой-либо другой инфе. 

http://pic.ipicture.ru/uploads/091021/g5IJmaULlx.png Как видно из картинки, некоторые ИП записаны все 8 раз .....

Автор: diadiavova 22.10.2009, 01:40
Цитата(BugZZ @  21.10.2009,  23:42 Найти цитируемый пост)
онлайн воутинга к фотографиям.

 smile 

Забей данные в промежуточную коллекцию, потом отфильтруй функцией Distinct с самописным компэарером.

Код

Public Class Form1
    Sub FillGrid()
        'создаём список
        Dim rowList As New List(Of DataGridViewRow)
        'здесь список заполняется 
        'путём загрузки XML

        'создаём компэарер
        Dim comp As New RowComparer
        'Ну b добавляем в грид
        Me.DataGridView1.Rows.AddRange(rowList.Distinct(comp))

    End Sub
End Class

Class RowComparer
    Implements IEqualityComparer(Of DataGridViewRow)

    Public Function Equals1(ByVal x As System.Windows.Forms.DataGridViewRow, ByVal y As System.Windows.Forms.DataGridViewRow) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of System.Windows.Forms.DataGridViewRow).Equals
        Return x.Cells("ip").Value = y.Cells("ip").Value
    End Function

    Public Function GetHashCode1(ByVal obj As System.Windows.Forms.DataGridViewRow) As Integer Implements System.Collections.Generic.IEqualityComparer(Of System.Windows.Forms.DataGridViewRow).GetHashCode
        Return obj.Cells("ip").Value
    End Function
End Class


В данном примере я показал следующее:
Сначала по результату обработки файлов создаётся коллекция готовых строк для вставки в грид. Предполагается, что в коллекции есть столбец, под названием "ip"с типом данных Integer. Тут всё просто. Если есть IP к примеру такой 123.326.4.34, то мы вставляем в эту колонку число 123326004034.
Таким образом в этой колонке у нас будет одинаковое число при одинаковых IP.  Далее при вызове метода Distinct будет возвращена коллекция без повторов. 

Здесь весь секретв классе RowComparer. Функция Equals1 у него должна возвращать значение, указывающее равны ли объекты, а GetHashCode1 возвращать одинаковое значение для одинаковых объектов. Если правильно реализуешь логику сравнения, то код заполнения таблицы будет работать как надо.

Автор: BugZZ 22.10.2009, 13:23
Цитата(diadiavova @  21.10.2009,  22:40 Найти цитируемый пост)
онлайн воутинга к фотографиям.

smile 
 Тоже удивлялся. Это дело рук какой-то "мудрёной" CMS....

Цитата(diadiavova @  21.10.2009,  22:40 Найти цитируемый пост)
Предполагается, что в коллекции есть столбец, под названием "ip"с типом данных Integer.

Хммм а вот тут уже появятся другие проблемы
12.201.45.10 -> 122014510
122.0.145.10 -> 122014510

А вообще знакомый сказал, что у меня ошибка в стравениени.... Решением была бы функция которая возвращала тру\фалс если запись уже присутствует в датагрид

Автор: diadiavova 22.10.2009, 13:36
BugZZ, не знаю, возможно я непонятно объяснил, попытаюсь сформулировать иначе.
Для того, чтобы иметь возможность удалить повторяющиеся элементы списка, нужно точно сформулировать по каким критериям следует определять совпадение элементов. В дотнете для этого существует интерфейс IEqualityComparer(Of T). Реализовав его в класе ты обязан определить два метода:

Equals - получает два объекта сравниваемого типа и выдаёт True в случае, если объекты следует считать равными(логику прописываешь сам).

GetHashCode - этот метод возвращает для каждого объекта некоторое число таким образом, чтобы равным объектам соответствовало одно и то же число.

Если такой класс будет реализован, то его экземпляр можно передать методу Distinct и он отфильтрует коллекцию по описанному в этом классе правилу сравнения.

На счёт свойства у грида, позволяющего исключать повторяющиеся записи не в курсе, может и есть.

Добавлено через 2 минуты и 13 секунд
Цитата(BugZZ @  22.10.2009,  14:23 Найти цитируемый пост)
Хммм а вот тут уже появятся другие проблемы
12.201.45.10 -> 122014510
122.0.145.10 -> 122014510

Ну я ведь не так описал. Каждый сегмент айпишника должен состоять из трёх разрядов, если меньше - заполняй нулями. В моём примере ведь так.

Автор: BugZZ 22.10.2009, 13:56
Цитата(diadiavova @  22.10.2009,  10:36 Найти цитируемый пост)
Для того, чтобы иметь возможность удалить повторяющиеся элементы списка, нужно точно сформулировать по каким критериям следует определять совпадение элементов. В дотнете для этого существует интерфейс IEqualityComparer(Of T). Реализовав его в класе ты обязан определить два метода:

Equals - получает два объекта сравниваемого типа и выдаёт True в случае, если объекты следует считать равными(логику прописываешь сам).

GetHashCode - этот метод возвращает для каждого объекта некоторое число таким образом, чтобы равным объектам соответствовало одно и то же число.

Если такой класс будет реализован, то его экземпляр можно передать методу Distinct и он отфильтрует коллекцию по описанному в этом классе правилу сравнения.

На счёт свойства у грида, позволяющего исключать повторяющиеся записи не в курсе, может и есть.

Окей буду, пробовать. Просто после твоего поста, в очередной раз увидел что know-how у меня мало и многое - просто не знаю!

Цитата(diadiavova @  22.10.2009,  10:36 Найти цитируемый пост)
Ну я ведь не так описал. Каждый сегмент айпишника должен состоять из трёх разрядов, если меньше - заполняй нулями. В моём примере ведь так. 

Извиняй, я не сразу обратил внимание.  smile 
Как добъюсь результатов - отпишусь....

Автор: diadiavova 22.10.2009, 13:59
Цитата(BugZZ @  22.10.2009,  14:56 Найти цитируемый пост)
Просто после твоего поста, в очередной раз увидел что know-how у меня мало и многое - просто не знаю!
Для этого и существует форум smile 

Автор: BugZZ 22.10.2009, 15:47
итак, пошел по легкому пути и сначала обкатал вариант с функцией которая говорит тру - если такой ИП уже есть и ФАЛС - если таких еще не обнаруженно.
итак вот функция:
Код

    Public Function IsElementValueAlreadySaved(ByVal DataGridView As Windows.Forms.DataGridView, ByVal compareString As String) As Boolean
        Dim result As Boolean = False
        For i As Integer = 0 To DataGridView.Rows.Count - 1
            ' IP - это имя столбца. Можено было бы и индекс использовать. Разницы нет.
            If DataGridView.Rows.Item(i).Cells("ip").Value.Equals(compareString) Then
                result = True
                Exit For
            End If
        Next
        Return result
    End Function


функцию я потестил. Все работает и делает то, что от нее требуется...

После проверки функции долго смеялся ибо выяснилось что мой myXmlReader почему то вообще без моего ведома творит что хочет.
итак. применяю фукцию.
Код

Dim myXmlReader As New XmlTextReader(List_with_path(i).ToString)
                myXmlReader.WhitespaceHandling = WhitespaceHandling.None
                While myXmlReader.Read()
                    If myXmlReader.NodeType = XmlNodeType.Element And myXmlReader.Name.Equals("username") Then
                        If Not IsElementValueAlreadySaved(dgv_ips, myXmlReader.ReadElementString.Trim) Then
                            dgv_ips.Rows.Add(myXmlReader.ReadElementString.Trim)
                        End If
                    End If
                End While

запускаю тулзу и вижу что вместо ИП-адресов записаны http://pic.ipicture.ru/uploads/091022/T9T13U6mY3.png, хотя о них в коде НЕТ НИ СЛОВА! 
Вобщем, после проверок, пришел к вывроду что по каким-т непонятным причинам, XMLReader действует по след. принципу:
1 - читает документ
2 - Элемент и тебя зовут "username"? ДА!
 2.1 Ты уже есть в списке (ДГВ) ? ДА
  2.2 Беру след Элемент в файле (<date>)
 -----а все даты уникальны. Повторов нет! 

может конечно и не совсем так, но причина по которой ни с того ни с сяго берётся другой НИГДЕ НЕДЕКЛАРИРОВАННЫЕ элемент- мне не ясна......

Автор: Pilger 22.10.2009, 16:47
Попробуй myXmlReader.ReadElementString.Trim сначала в переменную поместить, а уже оттуда в функцию и в столбец. Проблема в том, что ReadElementString после прочтения сам перескакивает на сл. элемент, а т.к. ты его вызываешь 2 раза, то он соответственно сдвигается два раза. В функцию засовывается то что надо, а в столбец сл. элемент.
Да и в экономическом плане удобнее. Зачем вызывать myXmlReader.ReadElementString.Trim дважды, когда можно записать в переменную.

Автор: BugZZ 22.10.2009, 17:09
Цитата(Pilger @ 22.10.2009,  13:47)
Попробуй myXmlReader.ReadElementString.Trim сначала в переменную поместить, а уже оттуда в функцию и в столбец. Проблема в том, что ReadElementString после прочтения сам перескакивает на сл. элемент, а т.к. ты его вызываешь 2 раза, то он соответственно сдвигается два раза. В функцию засовывается то что надо, а в столбец сл. элемент.
Да и в экономическом плане удобнее. Зачем вызывать myXmlReader.ReadElementString.Trim дважды, когда можно записать в переменную.

Макс, ты как обычно не подкачал. Все заработало.
Решение :
Код

Dim myXmlReader As New XmlTextReader(List_with_path(i).ToString)
                myXmlReader.WhitespaceHandling = WhitespaceHandling.None
                While myXmlReader.Read()
                    If myXmlReader.NodeType = XmlNodeType.Element And myXmlReader.Name.Equals("username") Then
                        Dim element As String = myXmlReader.ReadElementString.Trim
                        If Not IsElementValueAlreadySaved(dgv_ips, element) Then
                            dgv_ips.Rows.Add(element)
                        End If
                    End If
                End While


Макс, тебе плюсик!

Автор: diadiavova 22.10.2009, 20:43
Цитата(BugZZ @  22.10.2009,  16:47 Найти цитируемый пост)
итак, пошел по легкому пути
Ну если это лёгкий путь тада извиняйте. В таком случае гораздо проще было перед добавлением новой строки в грид просто в цикле обходить все существующие и проверять, есть ли уже строка с таким айпи.

Автор: Pilger 22.10.2009, 22:03
В общем то он так и сделал^^

 smile  100 сообщений. Щас буду плюсиками раскидываться.

Автор: diadiavova 22.10.2009, 22:11
Кстати, если бы он использовал лист, то вообще можно было без цикла обойтись...метод Find, передаёшь ему функцию(точнее лямбду) сравнивающую айпишники и сопсно всё. Но это не комильфо. Небольшое изменение условий потребует переписывания всего кода, а в моём случае переписать придётся только функции сравнения в компораторе.

Кстати тоже юбилей сегодня 2000+ в песочнице smile 

Автор: diadiavova 24.10.2009, 23:54
Кстати: на счёт интерфейса...вот тут описал более-менее универсальную реализацию.
http://forum.vingrad.ru/forum/topic-277734/kw-iequalitycomparer-gethashcode.html

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)