【问题标题】:How do I add, save and load items to a listview in WPF如何在 WPF 中添加、保存和加载项目到列表视图
【发布时间】:2019-10-13 02:04:16
【问题描述】:

大约两周前,我开始使用 WPF 进行开发,由于我只使用 WinForms 进行开发,因此遇到了一些常见问题,但设法找到了解决方案。但是,目前我遇到了一些问题:将具有多列的项目(通过 Visual Basic 代码,而不是 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 嘿,在 WinForms 中这很容易,但在 WPF 中则要复杂得多......这是使用 WinForms 保存的文本文件:imgur.com/a/8KatsDD 每行代表每个项目。每个 |代表每一列
  • 我检查了代码,它看起来与 WinForms 相同。您对哪一部分有问题?
  • 好吧,所以我检查了更多,想法是创建一个“DataTable”并将其绑定到 ListView 作为 DataSource 至少这是人们建议的,因为没有像 winforms 这样的子项。
  • 只是对你的结构的一个附注......像Public _artist As String这样的支持者字段不应该是公开的。 Private 是正确的 类的数据只能由属性过程设置,不能直接从类外设置。

标签: wpf vb.net listview binding items


【解决方案1】:

我刚刚更新了您的结构(我使用了一个类,只是出于习惯)以使用最新版本的 VS 中提供的自动属性值。属性的默认实现与支持字段一起内置。它和以前一样使用。

您在 ObservableCollection 的正确轨道上,但您需要通过添加 Event PropertyChanged 并提供 Sub OnPropertyChanged 来引发事件来实现 INotifyPropertyChanged。

在控件上设置绑定并将 ListView 的 ItemsSource 设置为您的 ObservableList。

保存 Import System.Text 以便您可以使用 StringBuilder。循环遍历列表以构建字符串以保存到文本文件。

我没有花时间尝试让 XAML Windows 看起来不错。请原谅我的丑陋。

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" 为例。我怎样才能解决这个问题?谢谢!
  • 我已经测试了代码,它对我有用。你能告诉我它在哪条线上失败了吗?我猜你指的是可怕的 NRE Null Reference Exception。当您单击 MainWindow 上的 Add Song 时,字段 mainForm 设置为 Me。所以这不应该是空的。我同意在 WinForms 中事情似乎更容易,但我认为这是因为我在 WinForms 工作了多年。
  • 再次抱歉,很晚才发表评论。尝试访问 MainForm 时失败。它说没什么:imgur.com/a/Ug9cggs
  • 另外,出于好奇,我应该从 Visual Basic 切换到 c# 还是应该继续使用 vb?
  • 您要关闭主窗口吗? WPF 可能会让 WinForms 程序员感到困惑。在 WinForms VB 中,在幕后创建表单的默认实例,这使得访问其他表单变得容易。在 WPF 中并非如此。在这里,我们必须将窗口的一个实例传递给第二个窗口,以便我们可以访问第一个窗口。当您在 MainWindow 上单击 BtnAdd 时,它会在 frmAdd 上设置属性 mainFrom。请注意,我们没有传递对 frmAdd 的引用,它只是类的名称。我们将引用传递给 frmAdd 的实例,即 frm。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
  • 1970-01-01
  • 2020-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多