Как добавлять, сохранять и загружать элементы в список в WPF

Около двух недель назад я начал разрабатывать в WPF и, поскольку я разрабатывал только в WinForms, я столкнулся с общими проблемами, но мне удалось найти для них решение. Однако в настоящее время я кое-что застрял: добавление элементов с несколькими столбцами (с помощью визуального базового кода, а не xaml) в список.

Я не уверен, лучше ли использовать для этого элемент управления Listview или DataGrid, но в основном я хочу, чтобы пользователь мог добавлять любимые песни в список, сохранять их в файл и загружать файл всякий раз, когда приложение открывается. В настоящее время в списке есть три столбца: «Исполнитель», «Песня» и «Статус».

Когда я программировал в WinForms, я делал это:

        Dim Song As New ListViewItem
        Form1.ListView1.Items.Add(Song)
        Song.Text = TextBox1.Text
        Song.SubItems.Add(TextBox2.Text)
        Song.SubItems.Add(TextBox3.Text)

Затем, чтобы сохранить:

   Dim Song As New ListViewItem
            Form1.ListView1.Items.Add(Song)
            Song.Text = TextBox1.Text
            Song.SubItems.Add(TextBox2.Text)
            Song.SubItems.Add(TextBox3.Text)
            Try
                Dim myWriter As New IO.StreamWriter(PATH_DATABASE)
                For Each myItem As ListViewItem In Form1.ListView1.Items
                    myWriter.WriteLine(myItem.Text & "|" & myItem.SubItems(1).Text & "|" & myItem.SubItems(2).Text & "|" & myItem.SubItems(3).Text
                Next
                myWriter.Close()
            Catch ex As Exception
                MsgBox("Error: " & ex.Message, vbCritical, "Error")
            End Try

Я искал и обнаружил, что мне нужно использовать привязку, поэтому я попробовал это:

Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Windows


Public Structure Song

    Public _artist As String
    Public _title As String
    Public _status As String

    Property Artist() As String
        Get
            Return _artist
        End Get
        Set(ByVal Value As String)
            _artist = Value
        End Set
    End Property
    Property Title() As String
        Get
            Return _title
        End Get
        Set(ByVal Value As String)
            _title = Value
        End Set
    End Property
    Property Status() As String
        Get
            Return _status
        End Get
        Set(ByVal Value As String)
            _status = Value
        End Set
    End Property
End Structure



Public Class WINDOW_AddSong

    Dim songs As New ObservableCollection(Of Song)




    Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
        Dim Song As New ListViewItem

        For Each wnd As Window In Application.Current.Windows
            If wnd.GetType Is GetType(MainWindow) Then

                DirectCast(wnd, MainWindow).Listview1.Items.Add(Alimento)
                Alimento.Content = New Song() With {._artist = "Lol", ._title = "Lol2", ._status = "Lol3"}




            End If
        Next
    End Sub
End Class

И в списке XAML:

<GridViewColumn Header = "Artist" DisplayMemberBinding = "{Binding Artist}"/>
<GridViewColumn Header = "Title" DisplayMemberBinding = "{Binding Title}"/>
<GridViewColumn Header = "Status" DisplayMemberBinding = "{Binding Status}"/>

Это работает, однако я не уверен, что это WPF-способ ведения дел.

Однако я застрял в процессе сохранения:

 Try
            Dim myWriter As New IO.StreamWriter(PATH_DATABASE)
            For Each wnd As Window In Application.Current.Windows
                If wnd.GetType Is GetType(MainWindow) Then
                    For Each myItem As ListViewItem In DirectCast(wnd, MainWindow).Listview1.Items
                        myWriter.WriteLine(Song, Artist, Status)
                    Next
                End If
            Next
            myWriter.Close()
        Catch ex As Exception
            MsgBox("Error: " & ex.Message, vbCritical, "Error")
        End Try

Это не работает. Кроме того, PATH_DATABASE — это просто каталог.

Подводя итог, может ли эксперт просмотреть мой код и проверить, все ли я делаю «правильно», и, если возможно, не могли бы вы помочь мне с процессом сохранения и загрузки?

Спасибо.

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

CruleD 27.05.2019 15:56

@CruleD Эй, в WinForms это было легко, но в WPF это намного сложнее ... Если бы вы могли предоставить мне пример кода или просто общую идею, я был бы признателен. Вот текстовый файл, сохраненный с помощью WinForms: imgur.com/a/8KatsDD Каждая строка представляет каждый элемент. Каждый | представляет каждый столбец

WSC Productions 27.05.2019 16:14

Я проверил, и по коду он выглядит так же, как WinForms. С какой частью у вас проблемы?

CruleD 27.05.2019 17:19

Итак, я проверил еще кое-что, идея состоит в том, чтобы создать «DataTable» и связать его с ListView как DataSource, по крайней мере, это то, что люди предлагают, поскольку в winforms нет подэлементов.

CruleD 27.05.2019 18:10

Просто примечание о вашей структуре... поля поддержки, такие как Public _artist As String, не должны быть общедоступными. Private правильно. Данные класса могут быть установлены только процедурами свойств, а не непосредственно извне класса.

Mary 27.05.2019 19:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
542
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я только что обновил вашу структуру (я использовал класс, просто по привычке), чтобы использовать автоматические значения свойств, которые доступны в последних версиях VS. Реализация свойств по умолчанию встроена вместе с вспомогательными полями. Он используется так же, как и раньше.

Вы находитесь на правильном пути с ObservableCollection, но вам необходимо реализовать INotifyPropertyChanged, добавив Event PropertyChanged и предоставив Sub OnPropertyChanged для создания события.

Настройте привязки к элементам управления и установите ItemsSource или ListView в свой ObservableList.

Чтобы сохранить импорт System.Text, чтобы вы могли использовать StringBuilder. Цикл по списку для создания строки для сохранения в текстовый файл.

Я не тратил время на то, чтобы XAML-окна выглядели красиво. Простите некрасивость.

Class MainWindow
    Public Songs As New ObservableCollection(Of Song)

    Protected Sub OnLoad(sender As Object, e As RoutedEventArgs)
        Songs.Add(New Song("Nirvana", "Smells Like Teen Spirit", "Open"))
        Songs.Add(New Song("John Lennon", "Imagine", "In Stock"))
        Songs.Add(New Song("U2", "One", "Unknown"))
        Songs.Add(New Song("Michael Jackson", "Billie Jean", "Open"))
        lvSongs.ItemsSource = Songs
    End Sub

    Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs)
        Dim frm As New frmAdd
        frm.mainForm = Me 'send the instance of the current form to the new form
        frm.ShowDialog()
    End Sub

    Private Sub SaveList()
        Dim sb As New StringBuilder
        For Each item As Song In Songs
            sb.AppendLine($"{item.Artist}|{item.Title}|{item.Status}")
        Next
        'call sb.ToString and write it to a .txt file
    End Sub
End Class

XAML для основного окна

<Window x:Class = "MainWindow"
        xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local = "clr-namespace:WPF_BindComboBox"
        mc:Ignorable = "d"
        Title = "MainWindow" Height = "450" Width = "800">
    <Grid Loaded = "OnLoad"
          Name = "root">
        <Grid.RowDefinitions>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "*"/>

        </Grid.RowDefinitions>
        <ListView Margin = "10" Name = "lvSongs" Grid.Row = "0">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <WrapPanel>
                        <TextBlock Text = "Name: " />
                        <TextBox x:Name = "txtArtist" 
                                 Text = "{Binding Artist, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                                 FontWeight = "Bold"/>
                        <TextBlock Text = ", " />
                        <TextBlock Text = "Age: " />
                        <TextBox Text = "{Binding Title}" 
                                 FontWeight = "Bold" />
                        <TextBlock Text = " (" />
                        <TextBox Text = "{Binding Status}"  />
                        <TextBlock Text = ")" />
                    </WrapPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Button x:Name = "btnAdd" Grid.Row = "1" Height = "25" Width = "100" Content = "Add a Song" Click = "BtnAdd_Click"/>      
    </Grid>
</Window>

Класс песни

Public Class Song
    Implements INotifyPropertyChanged

    Public Property Artist() As String
    Public Property Title() As String
    Public Property Status() As String

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(ByVal name As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
    End Sub

    Public Sub New(art As String, Ttl As String, Stat As String)
        Artist = art
        Title = Ttl
        Status = Stat
    End Sub
End Class

Окно добавления

Public Class frmAdd

    Public mainForm As MainWindow

    Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs)
        mainForm.Songs.Add(New Song(txtArtist.Text, txtTitle.Text, txtStatus.Text))
        ClearForm()
    End Sub

    Private Sub ClearForm()
        txtArtist.Clear()
        txtTitle.Clear()
        txtStatus.Clear()
    End Sub

End Class

Добавить окно XAML

<Window x:Class = "frmAdd"
        xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local = "clr-namespace:WPF_BindComboBox"
        mc:Ignorable = "d"
        Title = "frmAdd" Height = "172.641" Width = "307.547">
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "*"/>
            <RowDefinition Height = "*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width = "100"/>
            <ColumnDefinition Width = "Auto"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column = "0" Grid.Row = "0" Text = "Artist" FontSize = "16" FontWeight = "Bold"/>
        <TextBox x:Name = "txtArtist" Grid.Column = "1" Grid.Row = "0" FontSize = "16" Width = "150" />
        <TextBlock Grid.Column = "0" Grid.Row = "1" Text = "Title" FontSize = "16" FontWeight = "Bold"/>
        <TextBox x:Name = "txtTitle" Grid.Column = "1" Grid.Row = "1" FontSize = "16" Width = "150"/>
        <TextBlock Grid.Column = "0" Grid.Row = "2" Text = "Status" FontSize = "16" FontWeight = "Bold"/>
        <TextBox x:Name = "txtStatus" Grid.Column = "1" Grid.Row = "2" FontSize = "16" Width = "150"/>
        <Button x:Name = "btnAdd" Grid.Column = "1" Grid.Row = "4" Content = "Add Song" Click = "BtnAdd_Click"/>
    </Grid>
</Window>

РЕДАКТИРОВАТЬ Требуемый импорт

Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Text

Привет! Большое спасибо за то, что буквально предоставили код. :) Спасибо, но у меня есть один вопрос: когда я добавляю этот код: Public mainForm As MainWindow Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs) mainForm.Songs.Add(New Song(txtArtist.Text, txtTitle.Text, txtStatus.Text)) End Sub Это дает мне ссылку на ошибку. В настоящее время я использую прямое литье, потому что я не нашел ничего, что сработало. В WinForms это было намного проще. Например, просто Form1.Label1.text = "aaa". Как я могу это исправить? Спасибо!

WSC Productions 04.06.2019 00:15

Я проверил код, и он работает для меня. Можете ли вы сказать мне, на какой линии он терпит неудачу? Я предполагаю, что вы имеете в виду ужасное исключение нулевой ссылки NRE. Когда вы нажимаете «Добавить песню» в MainWindow, для поля mainForm устанавливается значение «Я». Так что это не должно быть нулевым. Я согласен, что в WinForms все кажется проще, но я думаю, что это потому, что я провел годы, работая в WinForms.

Mary 04.06.2019 04:50

Еще раз извините за очень поздний комментарий. Не удается получить доступ к MainForm. Пишет, что ничего: imgur.com/a/Ug9cggs

WSC Productions 09.06.2019 14:14

Кроме того, просто из любопытства следует ли мне перейти на С# с Visual Basic или продолжать использовать vb?

WSC Productions 09.06.2019 15:26

Вы закрываете MainWindow? WPF может сбивать с толку программистов WinForms. В WinForms VB делает что-то за кулисами, создавая экземпляры форм по умолчанию, что упрощает доступ к другим формам. Не так в WPF. Здесь мы должны передать экземпляр окна второму окну, чтобы мы могли получить доступ к первому окну. Когда вы нажимаете BtnAdd в MainWindow, он устанавливает свойство mainFrom в frmAdd. Обратите внимание, что мы не передаем ссылку на frmAdd, которая является просто именем класса. Мы передаем ссылку на экземпляр frmAdd, который является frm.

Mary 09.06.2019 16:53

Re С# против vb.net. Я начал с VB3, поэтому долгое время программировал на vb. Мой мозг паттерн vb. Я достаточно изучил C#, поэтому могу смотреть примеры (большинство из них сейчас на C#) и кодировать достаточно просто. Оба языка используют фреймворк .net, поэтому все, что вы узнаете о фреймворке, будет работать на C#. Если вы молоды, обязательно изучите C#. Сейчас это модный язык. Существует огромная установленная база vb, и ее необходимо будет поддерживать в течение многих лет. Есть еще много компаний, которые кодируют на vb.

Mary 09.06.2019 17:02

Другая мысль; MainWindow должен быть вашим начальным URI, чтобы код работал. Ссылка на mainForm устанавливается с помощью кнопки в MainWindow.

Mary 09.06.2019 17:05

Буду признателен, если вы примете мой ответ, нажав на галочку (галочку) слева от моего ответа. Вот когда мы получим это работает.

Mary 09.06.2019 17:07

Я понял. Поскольку приложение не разработано, я сохраню резервную копию wpf vb и попытаюсь перейти на С#. Поскольку я уже «программирую» на С# с помощью Unity, это не должно быть чем-то другим, но если это так, я переключусь на VB: p. Кстати, я не закрываю MainWindow, и он установлен как URI по умолчанию. Я попытаюсь выяснить, что не так, основываясь на том, что вы мне сказали, и пока это не будет исправлено, должен ли я по-прежнему использовать прямую трансляцию? Или я должен избегать этого? Я действительно не хотел «затемнять AddSong как >>> New<<< Window_AddSong» или использовать прямое приведение, поскольку это более «ненужный код».

WSC Productions 10.06.2019 16:03

В общем, DirectCast хорош, когда вы уверены, что приведение будет успешным. Он пропускает некоторые внутренние проверки, которые делают другие виды приведения, поэтому он быстрый, но опасный. Я не совсем уверен, где вы используете DirectCast, потому что мне это не нужно в моем коде. Создание экземпляра Window_AddSong с New абсолютно необходимо в WPF. Это не WinForms, где vb делает экземпляр по умолчанию доступным с некоторой магией за кулисами.

Mary 10.06.2019 17:59

Понимаю. Я буду исследовать, спасибо за помощь до сих пор!

WSC Productions 10.06.2019 21:57

Эй, как жизнь? Я смог добавить код, который вы мне предоставили, но у меня есть несколько вопросов: 1. Как изменить ширину каждого столбца? 2. Как мне загрузить элементы в список (теперь это сетка данных, так как я изменил ее, чтобы она больше походила на Excel)? В настоящее время у меня есть это: pastebin.com/3JRyn064

WSC Productions 18.06.2019 14:12

Кстати, я исправил часть, в которой мое приложение выдавало мне исключение, когда я пытался сделать «Public MainWindow As New MainWindow» и получить доступ к MainWindow из окна «Песни». Видимо, я как-то совсем пропустил ту часть, где вы сказали "frm.mainForm = Me 'отправить экземпляр текущей формы в новую форму". Больше никаких прямых трансляций, большое спасибо!

WSC Productions 19.06.2019 01:27

Другие вопросы по теме