Попробуйте загрузить изображения (классифицированные по городам) из локальной базы данных в панель FlowLayoutPanel

#c# #database #winforms #sqlcommand #flowlayoutpanel

Вопрос:

Я пытаюсь создать форму, которая может вставлять изображения в локальную базу данных и загружать их в панель FlowLayoutPanel. Вот макет моей формы, я вставляю изображения во вкладку «администратор» (рис.1).

На вкладке «Просмотр фотографий» я пытаюсь загрузить фотографии определенного города в панель FlowLayout, нажав кнопку (рис.2).

Вставка изображений

Загружайте изображения в соответствии с нажатой кнопкой

Моя локальная база данных (тип данных)

Я вставил фотографии плавно, но при попытке загрузить их возникли проблемы.

вот мой код:

     public Frm_MyAlbum()
    {
        InitializeComponent();
    }

    private void loadImage (int id, FlowLayoutPanel flp)
    {
        try
        {
            using (SqlConnection conn = new SqlConnection())
            {
                conn.ConnectionString = Settings.Default.Database1ConnectionString;

                SqlCommand command = new SqlCommand();
                command.CommandText = $"Select * from Photos where ID = {id}";
                command.Connection = conn;

                conn.Open();
                SqlDataReader DR = command.ExecuteReader();

                this.flowLayoutPanel1.Controls.Clear();

                while (DR.Read())
                {
                    byte[] bytes = (byte[])DR["Image"];
                    MemoryStream MS = new MemoryStream(bytes);

                    PictureBox pics;
                    pics = new PictureBox();
                    pics.Image = Image.FromStream(MS);
                    pics.Size = new Size(200, 160);
                    pics.SizeMode = PictureBoxSizeMode.StretchImage;

                    this.flowLayoutPanel1.Controls.Add(pics);
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }


    private void GetID(string City, FlowLayoutPanel flp)
    {
        try
        {
            using (SqlConnection conn = new SqlConnection())
            {
                conn.ConnectionString = Settings.Default.Database1ConnectionString;
                conn.Open();

                SqlCommand command = new SqlCommand();
                command.CommandText = $"Select * from Photos where City = {City}";
                command.Connection = conn;

                
                SqlDataReader DR = command.ExecuteReader();        

                while (DR.Read())
                {
                    loadImage((int)DR["PhotoID"], flp);
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }       

    private void button1_Click(object sender, EventArgs e) //button London
    {
        this.flowLayoutPanel1.Controls.Clear();
        GetID("London", flowLayoutPanel1);
    }

    private void button7_Click(object sender, EventArgs e) //button Add to DB
    {
        try
        {
            using (SqlConnection conn = new SqlConnection())
            {
                conn.ConnectionString = Settings.Default.Database1ConnectionString;

                SqlCommand command = new SqlCommand();
                command.CommandText = $"Insert into Photos(City, Description, Image) values(@City, @Desc, @Image)";
                command.Connection = conn;

                byte[] bytes;

                MemoryStream MS = new MemoryStream();
                this.pictureBox1.Image.Save(MS, System.Drawing.Imaging.ImageFormat.Jpeg);
                bytes = MS.GetBuffer();

                command.Parameters.Add("@City", SqlDbType.Text).Value = this.textBox2.Text;
                command.Parameters.Add("@Desc", SqlDbType.Text).Value = this.textBox1.Text;
                command.Parameters.Add("@Image", SqlDbType.Image).Value = bytes;

                conn.Open();
                command.ExecuteNonQuery();
                MessageBox.Show("Adding Successfully");
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    private void button8_Click(object sender, EventArgs e) //button browse
    {
        if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            this.pictureBox1.Image = Image.FromFile(openFileDialog1.FileName);
        }
    }
 

Когда я запустил код, я нажал Лондон (кнопка 1), и появилось исключение «Недопустимое имя столбца «Лондон»». VS указывает на то, что в команде строки что-то не так.CommandText = $»Выберите * из фотографий, где Город = {Город}»;’.

жучок

Перепробовал много способов переписать его, но так и не понял. Что я должен сделать, чтобы решить эту проблему?

Заранее спасибо вам, ребята!!

Комментарии:

1. Сначала вы передаете прямую строку в свой sql (всегда используйте параметры). В любом случае попробуйте City = ‘{Город}’

2. ОК попробует параметры позже! и я попытался использовать «{Город}», но появилось еще одно исключение: «Типы данных text и varchar несовместимы в операторе»равно»».

3. Формат Image бд должен быть заменен на varbinary(max) — Это: bytes = MS.GetBuffer(); очень неправильно, вам нужно [MemoryStream].ToArray() , а не GetBuffer() — Принудительно переформатировать изображение из любого формата в JPEG плохо. — Наконец, это: this.flowLayoutPanel1.Controls.Clear(); вероятно, одна из худших вещей, которые вы можете сделать в приложении WinForms. Но у вас там есть и другие нерешенные проблемы.

4. Спасибо, Джими, я совсем новичок в программировании, поэтому я очень ценю ваш вклад. Я посмотрю, о чем вы упомянули.

Ответ №1:

**Короткий ответ: ** проблема заключается в командном тексте вашего запроса. Вы также забыли добавить параметр, содержащий название города

Отделите ваши данные от вашей формы (MVVM)

В современном программировании существует тенденция отделять ваши данные (=модель) от способа отображения ваших данных (=представление).

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

Довольно часто для подключения модели к представлению требуется класс адаптера, довольно часто называемый ViewModel. Вместе эти три класса сокращенно называются MVVM. Подумайте о том, чтобы прочитать некоторую справочную информацию о MVVM.

Ваша модель

Поэтому вам нужен класс, который представляет вашу фотографию. Вероятно, что-то похожее на это:

 class Photo
{
    public int Id {get; set;}
    public string City {get; set;}
    public string Description {get; set;}
    public Image Image {get; set;}
}
 

По-видимому, вы можете хранить фотографии где-то, где вы можете получить их позже, даже после перезагрузки компьютера. Такой класс довольно часто называют a Repository (склад, где вы можете хранить товары и получать их позже).

 interface IPhotoRepository
{
    int AddP(Photo photo);         // returns the Id of the added Photo
    Photo Fetch(int photoId);      // returns Photo with Id or null
    ...
}
 

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

Вам также понадобится метод, который вводит строковое название города и возвращает все фотографии, сделанные в этом городе:

     IEnumerable<Photo> FetchByCity(string cityName);
 

Реализация интерфейса:

 class PhotoRepository : IPhotoRepository
{
    private string ConnectionString => ...

    public IEnumerable<Photo> FetchByCity(string cityName)
    {
        using (var dbConnection = new SqlConnection(this.ConnectionString))
        {
            using (var dbCommand = dbConnection.CreateCommand())
            {
                const string sqlText = "Select Id, Description, Image"
                  " from Photos where City = @City";

                dbCommand.CommandText = sqlText;
                dbCommand.Parameters.AddWithValue("@City", cityName);
                dbConnection.Open();
                
                // execute the command and return the fetched Photos:
                using (var dbReader = dbCommand.ExecuteReader())
                {
                    while (dbReader.Read())
                    {
                        // There is still a fetched row to process:
                        Photo fetchedPhoto = new Phto
                        {
                            Id = dbReader.GetInt64(0),
                            City = cityName,
                            Description = dbReader.GetString(1),
                            Image = (Image) dbReader.GetValue(2),
                            // I'm not not sure how to read an Image
                        };
                        yield return fetchedPhoto;
                    }
                }
            }
        }
    }

    // TODO: implement other methods
}
 

В вашей форме:

 private IPhotoRepository PhotoRepository {get;} ...
// fill this in the constructor with a new PhotoRepository

private ICollection<Photo> FetchPhotosByCity(string cityName)
{
    return this.PhotoRepository.FetchByCity(cityName).ToList();
}
 

Поскольку вы отделили свой режим (= классы Фото и фоторепозиция) от своего представления (= своей формы), вы можете модульно протестировать доступ к базе данных без формы. Вы также видите, что ваши ошибки не имеют никакого отношения к вашей форме.

Если вы хотите показать свою форму с некоторыми макетными данными, вы просто создаете класс, реализующий IPhotoRepository, и заполняете его некоторыми макетными изображениями без базы данных.

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

Комментарии:

1. Дорогой Харальд, большое вам спасибо за ваше подробное объяснение. Я просто исправил это на поверхности, изменив тип данных с text на nvarchar (это основная причина, по которой база данных не могла сообщить мою команду Sql) и удалив дополнительный элемент управления. Очистить();. Тем не менее, я считаю, что настройка параметров, о которых вы упомянули, весьма важна и будет задачей, над которой мне нужно будет поработать, чтобы сделать это лучше. Я проверяю MVVM и примеры кодов, которые вы опубликовали. Еще раз спасибо вам за вашу помощь, это действительно очень помогает!