Около двух недель назад я начал разрабатывать в 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 Эй, в WinForms это было легко, но в WPF это намного сложнее ... Если бы вы могли предоставить мне пример кода или просто общую идею, я был бы признателен. Вот текстовый файл, сохраненный с помощью WinForms: imgur.com/a/8KatsDD Каждая строка представляет каждый элемент. Каждый | представляет каждый столбец
Я проверил, и по коду он выглядит так же, как WinForms. С какой частью у вас проблемы?
Итак, я проверил еще кое-что, идея состоит в том, чтобы создать «DataTable» и связать его с ListView как DataSource, по крайней мере, это то, что люди предлагают, поскольку в winforms нет подэлементов.
Просто примечание о вашей структуре... поля поддержки, такие как Public _artist As String
, не должны быть общедоступными. Private правильно. Данные класса могут быть установлены только процедурами свойств, а не непосредственно извне класса.
Я только что обновил вашу структуру (я использовал класс, просто по привычке), чтобы использовать автоматические значения свойств, которые доступны в последних версиях 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". Как я могу это исправить? Спасибо!
Я проверил код, и он работает для меня. Можете ли вы сказать мне, на какой линии он терпит неудачу? Я предполагаю, что вы имеете в виду ужасное исключение нулевой ссылки NRE. Когда вы нажимаете «Добавить песню» в MainWindow, для поля mainForm устанавливается значение «Я». Так что это не должно быть нулевым. Я согласен, что в WinForms все кажется проще, но я думаю, что это потому, что я провел годы, работая в WinForms.
Еще раз извините за очень поздний комментарий. Не удается получить доступ к MainForm. Пишет, что ничего: imgur.com/a/Ug9cggs
Кроме того, просто из любопытства следует ли мне перейти на С# с Visual Basic или продолжать использовать vb?
Вы закрываете MainWindow? WPF может сбивать с толку программистов WinForms. В WinForms VB делает что-то за кулисами, создавая экземпляры форм по умолчанию, что упрощает доступ к другим формам. Не так в WPF. Здесь мы должны передать экземпляр окна второму окну, чтобы мы могли получить доступ к первому окну. Когда вы нажимаете BtnAdd в MainWindow, он устанавливает свойство mainFrom в frmAdd. Обратите внимание, что мы не передаем ссылку на frmAdd, которая является просто именем класса. Мы передаем ссылку на экземпляр frmAdd, который является frm.
Re С# против vb.net. Я начал с VB3, поэтому долгое время программировал на vb. Мой мозг паттерн vb. Я достаточно изучил C#, поэтому могу смотреть примеры (большинство из них сейчас на C#) и кодировать достаточно просто. Оба языка используют фреймворк .net, поэтому все, что вы узнаете о фреймворке, будет работать на C#. Если вы молоды, обязательно изучите C#. Сейчас это модный язык. Существует огромная установленная база vb, и ее необходимо будет поддерживать в течение многих лет. Есть еще много компаний, которые кодируют на vb.
Другая мысль; MainWindow должен быть вашим начальным URI, чтобы код работал. Ссылка на mainForm устанавливается с помощью кнопки в MainWindow.
Буду признателен, если вы примете мой ответ, нажав на галочку (галочку) слева от моего ответа. Вот когда мы получим это работает.
Я понял. Поскольку приложение не разработано, я сохраню резервную копию wpf vb и попытаюсь перейти на С#. Поскольку я уже «программирую» на С# с помощью Unity, это не должно быть чем-то другим, но если это так, я переключусь на VB: p. Кстати, я не закрываю MainWindow, и он установлен как URI по умолчанию. Я попытаюсь выяснить, что не так, основываясь на том, что вы мне сказали, и пока это не будет исправлено, должен ли я по-прежнему использовать прямую трансляцию? Или я должен избегать этого? Я действительно не хотел «затемнять AddSong как >>> New<<< Window_AddSong» или использовать прямое приведение, поскольку это более «ненужный код».
В общем, DirectCast хорош, когда вы уверены, что приведение будет успешным. Он пропускает некоторые внутренние проверки, которые делают другие виды приведения, поэтому он быстрый, но опасный. Я не совсем уверен, где вы используете DirectCast, потому что мне это не нужно в моем коде. Создание экземпляра Window_AddSong с New абсолютно необходимо в WPF. Это не WinForms, где vb делает экземпляр по умолчанию доступным с некоторой магией за кулисами.
Понимаю. Я буду исследовать, спасибо за помощь до сих пор!
Эй, как жизнь? Я смог добавить код, который вы мне предоставили, но у меня есть несколько вопросов: 1. Как изменить ширину каждого столбца? 2. Как мне загрузить элементы в список (теперь это сетка данных, так как я изменил ее, чтобы она больше походила на Excel)? В настоящее время у меня есть это: pastebin.com/3JRyn064
Кстати, я исправил часть, в которой мое приложение выдавало мне исключение, когда я пытался сделать «Public MainWindow As New MainWindow» и получить доступ к MainWindow из окна «Песни». Видимо, я как-то совсем пропустил ту часть, где вы сказали "frm.mainForm = Me 'отправить экземпляр текущей формы в новую форму". Больше никаких прямых трансляций, большое спасибо!
Есть много способов сохранить его, например, вы можете использовать простой текстовый файл, затем для каждой «строки» вашего списка вы должны написать строку и отделить каждый «столбец» запятой или каким-либо символом, например это. И вы бы загрузили его, прочитав строку из текстового файла, разделив ее запятой, а затем поместив каждый элемент в свой «столбец».