DataGridView i zmiana typu kolumny

0

Witam!

Chcę wyswietlić w DataGridView tabele, gdzie jedna z kolumn zawiera pliki różnego rodzaju. W tej chwili napisałem sobie klasę z zapytaniem Linq wyświetlającym daną kolumnę, którą podpinam do istniejącego DataGridView, na której ma byc to wyświetlane. sposób taki jednak jest mało efektywny bo w polach kolumny która zawiera obrazki albo pliki .pdf, są wyświetlane tylko początkowe piksele licząc od lewego górnego rogu obrazka, czyli wsp. (0,0) - w przypadku obrazka, a w przypadku pliku pdf zaraz po wyświetleniu tabeli wyskakuje komunikat o błędzie DataGridView ze względu na niezgodność wyświetlenia typu, tyle że nie mogę sobie pozwolic na włączenie AutoSize w tym DataGridView, bo tabela zrobiła by się bardzo nieczytelna w przypdaku dużej ilości rekordów.

Myslę, że najlepiej by było zastąpić pola w tej kolumnie buttonami albo linkami do znajdującego sie pod nim pliku, aby mozna było go ściągnąć, ale problem w tym że VS 2010 nie przyjmuje tworzenia nowych kolumn z kodu w obrębie istniejącego DataGridView (można tylko zmieniać jego właściwości :/)

Miejsce gdzie w pliku nazwa.Designer.cs powinna byc zdefiniowana ta kolumna wygląda tak:

            // 
            // dataGridView1
            // 
            this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.Location = new System.Drawing.Point(12, 12);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.ReadOnly = true;
            this.dataGridView1.Size = new System.Drawing.Size(793, 351);
            this.dataGridView1.TabIndex = 0;

            //this.dataGridViewButtonColumnSkan = new System.Windows.Forms.DataGridViewButtonColumn();
            //this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] 
            //{
            //    this.dataGridViewButtonColumnSkan,
            //});

klasa wyświetlająca:

    class ClassShowBadania
    {
        public static BindingSource getStandardBindingSource()
        {
            BudSystemEntities bazaBudSystem = new BudSystemEntities();
            BindingSource bindingSource = new BindingSource();
            bindingSource.DataSource =
                from badanie in bazaBudSystem.Badania
                select new
                {
                    Nazwa = badanie.nazwa,
                    Numer = badanie.numer,
                    Imię = badanie.Pracownicy.imie,
                    Nazwisko = badanie.Pracownicy.nazwisko,
                    DataWydania = badanie.dataOd,
                    DataWażności = badanie.dataDo,
                    Skan = badanie.skanDokumentu
                };
            return bindingSource;
        }
    }

Czy ktoś może wie jak to przerobić??

0

Ok, udało mi się w końcu dodac kolumne typu ButtonColumn, aczkolwiek, co za tym idzie, mam z czym inny teraz problem. A mianowicie, chce aby po naciśnięciu na dany przycisk przy danym rekordzie można było zapisac na dysk plik który był wczesniej załadowany do tej tabeli, do kolumny typu nvarbinary: W tym celu nalezy oprogramować zdarzenie dataGridView1_CellContentClick. Do tej pory udało mi się sklecić coś takiego:

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            int idDanegoBadania = (int)dataGridView1[1, e.RowIndex].Value;
            var pasujacySkan =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select skan.skanDokumentu
                ).AsEnumerable().First();

            var pasujacaNazwaSkanu =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select skan.nazwaDokumentu
                ).AsEnumerable().First();

            saveFileDialog1.ShowDialog();
            saveFileDialog1.FileName = pasujacaNazwaSkanu;
            FileStream strumienZapisywaniaDoPliku = new FileStream(saveFileDialog1.FileName, FileMode.Append, FileAccess.Write, FileShare.Write);
            strumienZapisywaniaDoPliku.Close();
            StreamWriter strumienZapisywanychDanych = new StreamWriter(saveFileDialog1.FileName, true, Encoding.ASCII);
            strumienZapisywanychDanych.Write(pasujacySkan);
            strumienZapisywanychDanych.Close();
        }

aczkolwiek definiujac strumiń do zapisywania do pliku w taki sposób nic więcej nie mogę z nim zrobić, tzn. VS nic nie podpowiada a jedyna logiczna rzecz to zadziałanie metodą SaveFile na strumień tak jak sie to ma w przypadku richTextBox'ów czy też pictureBox'ów np.

richTextBox1.SaveFile(saveFileDialog1.FileName);

Jak widać, mam dostęp do tablicy w której zapisany jest plik, jak i do nazwy z jaką został załadowany, ale w dalszym ciągu nie wiem jak taki plik zapisać na dysk, albo ewentualnie podglądnąć na nowym Form'ie(tyle że wtedy dochodzi problem z obsługą plików pdf na form'ie). Czy ktoś z was może mi pomóc i dac kilka wskazówek jak to zrobić?

0
  1. saveFileDialog1.FileName = pasujacaNazwaSkanu; chyba powinieneś to ustawić przed pokazaniem dialogu
  2. dlaczego tworzysz strumień typu FileStream i go od razu zamykasz? masz przecież metodę Write
  3. dlaczego nie sprawdzasz co zwróciło ShowDialog()? Jak ktoś kliknie [Anuluj] to i tak uszczęśliwiasz go podglądem pliku

Możesz rozpoznawać typ załącznika i jeśli to obrazek, czy inny typ, który umiesz wyświetlić możesz pokazać go samodzielnie.
Jeśli typ, którego nie umiesz wyświetlić, to zapisz plik do katalogu tymczasowego i zrób process start na tym pliku, otworzy się domyślny program do odczytu tego typu, jaki użytkownik ma zainstalowany. Albo zawsze tak możesz robić.

0

Ok, poprawiłem te strumienie. Dlatego zamykałem i otwierałem że wcześniej tam pisałem coś innego i zostało przez przypadek. Ustosunkowując się do twoich rad zrobiłem coś takiego:

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            int idDanegoBadania = (int)dataGridView1[1, e.RowIndex].Value;
            var pasujacySkan =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select skan.skanDokumentu
                ).AsEnumerable().First();

            var pasujacaNazwaSkanu =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select skan.nazwaDokumentu
                ).AsEnumerable().First();

            saveFileDialog1.FileName = pasujacaNazwaSkanu;

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                FileInfo daneZapisywanegoPliku = new FileInfo(pasujacaNazwaSkanu);
                rozmiarDokumentu = (int)daneZapisywanegoPliku.Length;

                FileStream strumienZapisywaniaDoPliku = new FileStream("C:\BudSystem_temp\\" + pasujacaNazwaSkanu, FileMode.Append, FileAccess.Write, FileShare.Write);
                while (rozmiarDokumentu > 0)
                {
                    strumienZapisywaniaDoPliku.Write(pasujacySkan, rozmiarZapisywanychDanych, rozmiarDokumentu);

                    rozmiarZapisywanychDanych += 1;
                    rozmiarDokumentu -= 1;
                }
                rozmiarDokumentu = pasujacySkan.Length;

                strumienZapisywaniaDoPliku.Close();
            }
        }

saveFileDialog zapisuje plik w rozmiarze 1KB tyle że za każdym razem trzeba dopisać rozszerzenie, ale myśle że to kwestia ustawienia filtrów w saveFileDialog.

Wizja otwierania pliku domyślnym programem po jego zapisaniu mi sie bardzo podoba. Jednakże, jeśli mój kod, a zarazem moje myslenie idzie w dobrym kierunku to powiedzcie mi jak otworzyć tak zapisany plik. Próbowałem Open na strumieniu ale VS tego nie akceptuje.

0

Czemu dwa razy łączysz się z bazą i osobno pobierasz skan i jego nazwę, jeśli można to zrobić raz i dostać typ anonimowy. Zamiast First() lepiej użyć Single(), bo first robi top 1, a single spodziewa się jednego rekordu.

var skan =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select new {skan.skanDokumentu, skan.nazwaDokumentu}
                ).AsEnumerable().Single();

skan.skanDokumentu i skan.nazwaDokumentu

pasującySkan to tablica bajtów? byte[]? Czyli plik zapisany w bazie?
Co ci daje FileInfo? wg mnie nic, chyba że masz w katalogu już plik o takiej nazwie, który zaraz zapiszesz (ponownie). Tego pliku nie ma, nie ma więc jego info.

Kompletnie nie rozumiem twojego zapisu pliku. Chyba ty też nie :P
Writes a block of bytes to this stream using data from a buffer.
public override void Write(
byte[] array,
int offset,
int count
)
Teraz zapis...
Jako array podajesz skan.skanDokumentu, jako offset 0 (słownie: zero), chyba że nie chcesz pisać od początku pliku. Jako count skan.skanDokumentu.Length.
Ten while i inkrementacje/dekremantacje są bez sensu. Do tego samego pliku zapisujesz na kolejnych pozycjach ten sam bufor krótszy o jeden w każdym cyklu pętli. Efekt - wszystkie bajty w pliku są takie same i równe pierwszemu bajtowi pliku.

Plik zamiast we własnym dziwnym niby tymczasowym katalogu, należy zapisać w katalogu plików tymczasowych użytkownika.
http://msdn.microsoft.com/en-us/library/system.io.path.gettemppath.aspx
http://msdn.microsoft.com/en-us/library/system.environment.specialfolder.aspx pobierasz LocalApplicationData i tam tworzysz sobie temp dla swojej aplikacji

Jeśli chodzi o otwarcie pliku przez aplikację domyślną: http://msdn.microsoft.com/en-us/library/53ezey2s.aspx
Używasz tej metody żeby wystartować proces podając ścieżkę do pliku, to działa podobnie jak wpiszesz w okienko uruchom ścieżkę jakiegoś pliku. Też otworzy się on za pomocą domyślnego programu.

0

Ok, kod po poprawkach:

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            int idDanegoBadania = (int)dataGridView1[1, e.RowIndex].Value;
            var pasujacySkan =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select new
                {
                    skan.skanDokumentu,
                    skan.nazwaDokumentu
                }
                ).AsEnumerable().Single();

            saveFileDialog1.FileName = pasujacySkan.nazwaDokumentu;

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string sciezkaFolderuTymczasowego = Path.GetTempPath();
                FileStream strumienZapisywaniaDoPliku = new FileStream(sciezkaFolderuTymczasowego + pasujacySkan.nazwaDokumentu, FileMode.Append, FileAccess.Write, FileShare.Write);
                strumienZapisywaniaDoPliku.Write(pasujacySkan.skanDokumentu, 0, pasujacySkan.skanDokumentu.Length);
                strumienZapisywaniaDoPliku.Close();
                Process.Start(pasujacySkan.nazwaDokumentu, sciezkaFolderuTymczasowego);
            }
        }

Tak, skan.skanDokumentu to tablica byte[].

Rozumiem że LocalApplicationData generuje ścieżekę do miejsca na komputerze gdzie może być stworzony katalog tymczasowy, potem należy taki katalog w tym miejscu stworzyć z poziomu kodu i w efekcie mając coś takiego zapisywać pliki do tego katalogu?

Mimo to, że wyrzuciłem while'a zapisywanie daje ten sam efekt, czyli powstaje plik o rozmiarze 1KB.

0

To przypomnij mi jak masz odczyt pliku i zapis bo bazy. Oraz sprawdź jakie dane (ile) jest w bazie. Bo pytanie czy już do bazy źle zapisujesz plik, czy źle następuje odczyt z bazy.
A o LocalApplicationData poczytaj na MSDN, zobacz który to jest katalog. Generalnie powinieneś tam utworzyć katalog swojej aplikacji i to będzie dla aktualnego użytkownika. Wtedy możesz tam zapisywać dane swojej aplikacji. Teoretycznie tymczasowe też, ale tymczasowe raczej powinieneś zapisywać do odpowiedniego katalogu tymczasowego, bo w windows są mechanizmy, które automatycznie mogą czyścić takie katalogi. Jeśli zrobisz to we własnym zakresie, musisz też samodzielnie umieć czyścić taki katalog, żeby nie zaśmiecić użytkownikowi dysku.

0

Zapis pliku do bazy:

        private void buttonPrzegladaj_Click(object sender, EventArgs e)
        {
            Badania badanie = new Badania();

            wpiszDaneDoBazy(badanie);
            bazaBudSystem.AddToBadania(badanie);
            bazaBudSystem.SaveChanges();

            if (openFileDialogSkanDokuemntu.ShowDialog() == DialogResult.OK)
            {
                FileInfo daneWczytywanegoPliku = new FileInfo(openFileDialogSkanDokuemntu.FileName);
                byte[] skanDok = new byte[daneWczytywanegoPliku.Length];
                nazwaDok = daneWczytywanegoPliku.Name;
                sciezkaDokumentu = daneWczytywanegoPliku.ToString();
                rozmiarDokumentu = (int)daneWczytywanegoPliku.Length;

                FileStream strumienWczytywaniaPliku = new FileStream(sciezkaDokumentu, FileMode.Open, FileAccess.Read);
                while (rozmiarDokumentu > 0)
                {
                    int n = strumienWczytywaniaPliku.Read(skanDok, rozmiarPrzeczytanychDanych, rozmiarDokumentu);
                    if (n == 0) break;

                    rozmiarPrzeczytanychDanych += n;
                    rozmiarDokumentu -= n;
                }
                rozmiarDokumentu = skanDok.Length;
                strumienWczytywaniaPliku.Close();

                SqlConnection polaczenie = new SqlConnection("Data Source=localhost;Initial Catalog=BudSystem;Integrated Security=True;");
                string zapytanieInsert = "UPDATE Badania SET nazwaDokumentu = @Name, skanDokumentu = @ImageData WHERE id = @ID";
                SqlCommand zapytanie = new SqlCommand(zapytanieInsert, polaczenie);
                zapytanie.Parameters.AddWithValue("@ID", badanie.id);
                zapytanie.Parameters.AddWithValue("@Name", nazwaDok);
                zapytanie.Parameters.AddWithValue("@ImageData", skanDok);
                try
                {
                    polaczenie.Open();
                    zapytanie.ExecuteNonQuery();
                }
                finally
                {
                    polaczenie.Close();
                    labelKomunikatOPliku.Text = "PLIK WCZYTANY";
                }
            }
            else MessageBox.Show("Plik nie został wczytany do bazy! Spróbuj ponownie.");
        }

W bazie jest on dobrze zapisywany, bo jak wyświetle tabele z tą kolumna w DataGridView i dam AutoSize na true to obrazki się wyświetlają, mimo że nie zmieniam typu kolumny tylko jest domyslny tzn. DataGridViewTextBoxColumn.

Ok, o LocalApplicationData poczytam jeszcze, aczkolwiek mysle że zostanie taka wersja jaka jest czyli z tempem systemowym, a jak znajde troche czasu to będe sie bawił z własnym katalogiem tymczasowym dla mojej aplikacji, bo tu chodzi tylko i wyłącznie o podgląd.

Z tego wniosek, że tylko i wyłącznie odczyt pliku z bazy jest błędny :/

0

Nie widzę deklaracji rozmiarPrzeczytanychDanych, która powinna być zmienną lokalną i definitywnie przed pętlą while ustawiana na 0 (zero).
poza tym trochę za mało try-catch, using na FileStream itp. Exceptiony na czytaniu z pliku są dość prawdopodobne. Plik otwarty przez inny proces i zablokowany, brak uprawnień, skasowany podczas czytania etc.
Ale to było na marginesie.

Wróćmy do zapisu pliku. Jeśli teraz zapisujesz go do temp systemowego to bardzo dobrze, wg mnie może tak zostać.
Jaki rozmiar ma tablica pasujacySkan.nazwaDokumentu? Może plik ma 1KB? Dla każdego pliku obserwujesz takie zachowanie? Może problem wynika z wcześniejszego błędnego zapisu pliku na którym testujesz?

0

Dyrektywa using i zmienna rozmiarPrzeczytanychDanych ustawiony na 0, są zadeklarowane na początku pliku(poza tą funkcją). Bloki try i catch na pewno dodam ale jak już wszystko będzie poprawnie działało. To tyle odnośnie dodawania plików do bazy.

Dane na których testuje są na pewno dobre, bo sprawdzane po kilka razy. Gdzieś musi byc błąd podczas samego odczytywania danych z bazy do pliku. W tej chwili mając ściezke tymczasową wyrzuciłem saveFileDialog, bo jest do niczego nie potrzebny, tyle że w tej chwili jakby funkcja pobierająca ścieżke folderu tymczasowego nie działała a strumień czytał do jakiegoś nieznanego miejsca bo Process.Start(...) wywala że nie mógł odnaleźć pliku.

Kod, o którym pisze:

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            int idDanegoBadania = (int)dataGridView1[1, e.RowIndex].Value;
            var pasujacySkan =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select new
                {
                    skan.skanDokumentu,
                    skan.nazwaDokumentu
                }
                ).AsEnumerable().Single();

            string sciezkaFolderuTymczasowego = Path.GetTempPath(); //sciezka do folderu tymczasowego
            FileStream strumienZapisywaniaDoPliku = new FileStream(sciezkaFolderuTymczasowego + pasujacySkan.nazwaDokumentu, FileMode.Append, FileAccess.Write, FileShare.Write);
            strumienZapisywaniaDoPliku.Write(pasujacySkan.skanDokumentu, 0, pasujacySkan.skanDokumentu.Length);
            strumienZapisywaniaDoPliku.Close();
            Process.Start(pasujacySkan.nazwaDokumentu, sciezkaFolderuTymczasowego);

            //saveFileDialog1.FileName = pasujacySkan.nazwaDokumentu;
            //if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            //{
            //    string sciezkaFolderuTymczasowego = Path.GetTempPath(); 
            //    FileStream strumienZapisywaniaDoPliku = new FileStream(sciezkaFolderuTymczasowego + pasujacySkan.nazwaDokumentu, FileMode.Append, FileAccess.Write, FileShare.Write);
            //    strumienZapisywaniaDoPliku.Write(pasujacySkan.skanDokumentu, 0, pasujacySkan.skanDokumentu.Length);
            //    strumienZapisywaniaDoPliku.Close();
            //    MessageBox.Show("Plik został zapisany poprawnie.");
            //    Process.Start(pasujacySkan.nazwaDokumentu, sciezkaFolderuTymczasowego);
            //}
        }
0

rozmiarPrzeczytanychDanych musi być przed while zawsze zerowany, bo inaczej wyjdą głupoty. Wpiszesz do tablicy bajty pliku nie od jej początku czyli dostaniesz tablicę bajtów nieodpowiadającą plikowi, o ile wcześniej nie poleci exception że wykraczasz po za rozmiar tablicy, czy coś w tym stylu.

Użyj FileMode.Create aby utworzyć nowy plik lub nadpisać istniejący.
Odpal debuggera i sprawdź jaki rozmiar ma tablica bajtów pobrana z bazy. To da nam obraz czy odczyt z bazy jest problemem, czy zapis do pliku. Oraz przy okazji co zwraca u ciebie Path.GetTempPath();.

0

Wydawało by się że funkcja Path.GetTempPath() zwraca dobrą ścieżkę, a mianowicie: C:\Users\Admin\AppData\Local\Temp

0
sciezkaFolderuTymczasowego + pasujacySkan.nazwaDokumentu

czyli
"C:\Users\Admin\AppData\Local\Temp" + "mojPlik.pdf"
faktycznie daje kiepskie wyniki
Użyj Path.Combine(...).

0

Dalej to samo, po uzyci Path.Combine(...) w tej chwili Process.Start(...) wywala że nie ma pliku do odczytu. W sumie plik który został zapisany o rozmiarze 1KB był zapisywany jeszcze przez saveFileDialog(), po tych modyfikacjach pliki, tak jakby nie są w ogóle zapisywane, albo aplikacja je w tempie zapisuje a Windows od razu to czyści zanim zdąży go otworzyć, ale to jest raczej mało prawdopodobne :/

Kod aktualnie wykonywany:

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            int idDanegoBadania = (int)dataGridView1[1, e.RowIndex].Value;
            var pasujacySkan =
                (
                from skan in bazaBudSystem.Badania
                where skan.id == idDanegoBadania
                select new
                {
                    skan.skanDokumentu,
                    skan.nazwaDokumentu
                }
                ).AsEnumerable().Single();

            string sciezkaFolderuTymczasowego = Path.GetTempPath();
            FileStream strumienZapisywaniaDoPliku = new FileStream(Path.Combine(sciezkaFolderuTymczasowego, pasujacySkan.nazwaDokumentu), FileMode.Create, FileAccess.Write, FileShare.Write);
            strumienZapisywaniaDoPliku.Write(pasujacySkan.skanDokumentu, 0, pasujacySkan.skanDokumentu.Length);
            strumienZapisywaniaDoPliku.Close();
            Process.Start(pasujacySkan.nazwaDokumentu, sciezkaFolderuTymczasowego);

            //saveFileDialog1.FileName = pasujacySkan.nazwaDokumentu;
            //if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            //{
            //    string sciezkaFolderuTymczasowego = Path.GetTempPath(); 
            //    FileStream strumienZapisywaniaDoPliku = new FileStream(sciezkaFolderuTymczasowego + pasujacySkan.nazwaDokumentu, FileMode.Append, FileAccess.Write, FileShare.Write);
            //    strumienZapisywaniaDoPliku.Write(pasujacySkan.skanDokumentu, 0, pasujacySkan.skanDokumentu.Length);
            //    strumienZapisywaniaDoPliku.Close();
            //    MessageBox.Show("Plik został zapisany poprawnie.");
            //    Process.Start(pasujacySkan.nazwaDokumentu, sciezkaFolderuTymczasowego);
            //}
        }
    }
0

Jakbyś siedział obok to powinieneś teraz poczuć dojmujący ból w potylicy, bo właśnie oberwałeś mega kuksańca.
http://msdn.microsoft.com/en-us/library/h6ak8zt5.aspx
public static Process Start(string fileName, string arguments)
Gdzie widzisz napisane że drugi parametr to ścieżka do katalogu gdzie jest plik?

Process.Start(Path.Combine(sciezkaFolderuTymczasowego, pasujacySkan.nazwaDokumentu))

A poza tym zamiast o takie głupoty pytać, to odpal debuggera (w VS jest to zadanie banalne) i prześledź kod linijka po linijce i zobacz co się jak wykonuje. Jak czyta plik, gdzie go zapisuje, czy po zamknięciu strumienia plik jest w katalogu tymczasowym, jak powinien być.
Zwracaj uwagę na to czym są argumenty metod, których używasz. Używaj najpierw msdn jeśli coś jest niejasne.

1 użytkowników online, w tym zalogowanych: 0, gości: 1