Чтение файла .txt в listbox без привязки к источнику элементов ИЛИ удаление привязки без очистки listbox от строк

#c# #wpf #text-files #listboxitem #itemssource

#c# #wpf #текстовые файлы #элемент списка #itemssource

Вопрос:

Я довольно новичок в C #, но понимаю основные концепции.

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

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

 if (File.Exists("PersonalFile.txt"))
{
 string[] line = File.ReadAllLines("PersonalFile.txt");
 lbxStaffDetails.ItemsSource = line;
}
  

Однако, делая это таким образом, я не могу добавлять новые записи в listbox из-за привязки данных, я получаю это сообщение об ошибке System.Исключение InvalidOperationException: ‘Операция недопустима, пока используется ItemsSource. Доступ и изменение элементов с помощью ItemsControl.Вместо этого используется ItemsSource.’

Есть ли способ удалить привязку, но сохранить данные в listbox? Использование lbxStaffDetails.ItemsSource = null; очищает listbox; или есть другой способ прочитать все строки файла .txt в listbox без использования файла в качестве источника привязки?

Примечания:

  • lbxStaffDetails об этом listbox идет речь
  • PersonalFile.txt содержит ли файл .txt записи в новых строках.
  • Это первый раз, когда я связываю данные и файлы.

Редактировать:

Забыл упомянуть, как я добавляю данные в listbox, поэтому вот код для этого.

 private void btnAddWaitingList_Click(object sender, RoutedEventArgs e)
{
 _EmployeeID = tbxEmployeeID.Text;
 _Name = tbxName.Text;
 _PayRate = tbxPayRate.Text;            
 _Email = tbxEmail.Text;

 string employeeDetails = _EmployeeID   ","   _Name   ","   _PayRate   ","   _Email;

 lbxStaffDetails.Items.Add(employeeDetails);
}
  

Когда код запускается и доходит до нижней строки, он выдает ошибку, упомянутую выше.

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

1. В вашем вопросе недостаточно подробностей, чтобы дать хороший ответ. Но ваша проблема, похоже, коренится в том факте, что вы неправильно обрабатываете связанные свойства модели. В частности, единственная цель модели представления, содержащей свойства для привязки, заключается в том, чтобы эти свойства отражали то, что представление должно представлять в любой момент времени. Должна быть коллекция (например ObservableCollection<string> ), привязанная к ListBox.ItemsSource (в XAML, не назначенная в code-behind), и эта коллекция должна быть заполнена путем копирования в нее данных, считанных из файла. …

2. string[] Возвращаемый File.ReadAllLines() сам по себе не является коллекцией для использования ListBox . Хотя, в качестве альтернативы, если вы хотите использовать его таким образом, вам следует привязать ItemSource к string[] свойству в модели представления и обновить это свойство с помощью string[] чтения из файла при чтении файла. Другие изменения в представлении пользователю должны обрабатываться аналогичным образом, всегда опосредованно через модель представления, при этом базовая «бизнес-логика» обрабатывается отдельно от этой модели представления.

3. Поскольку вы хотите иметь возможность добавлять элементы в ListBox один за раз, когда пользователь вводит данные, вам, вероятно, следует пойти по ObservableCollection<T> маршруту. Добавление в эту коллекцию автоматически обновит, ListBox если вы правильно привязали коллекцию к ItemsSource .

Ответ №1:

Не путайте привязку данных с простым присвоением значения. Привязка данных — это другая концепция, когда цель привязывается к источнику данных с помощью Binding . Binding будет отслеживать цель и источник и передавать изменения от одного к другому. Это двунаправленный динамический канал передачи данных.
Вы можете настроить Binding в XAML или C # (см. Обзор привязки данных в WPF).

Вы не привязываете ListBox к файлу. Вы прочитали содержимое файла в массив строк. Затем этот массив присваивается ListBox.ItemsSource свойству.

Поскольку вы заполнили его, ListBox используя ItemsSource свойство, вам не разрешается изменять его элементы с помощью Items свойства ( InvalidOperationException ).
Вам нужно либо снова назначить измененную коллекцию ListBox.ItemsSource (что приведет к повторному созданию всех элементов complete ListBox , что плохо сказывается на производительности), либо использовать ObservableCollection .

Это специальная коллекция, которая позволяет наблюдать (шаблон наблюдателя). Наблюдатель получает уведомление от наблюдаемой коллекции через событие, что коллекция изменилась (добавить / переместить / удалить). Каждый ItemsControl может прослушать это событие и автоматически обновит себя.

MainWindow.xaml.cs

 partial class MainWindow : Window
{
  public ObservableCollection<string> StaffDetails { get; set; }

  public MainWindow()
  {
    InitializeComponent();

    // Set the DataContext to MainWindow for data binding (XAML version)
    this.DataContext = this;
  }

  private void ReadFile()
  {    
    if (!File.Exists("PersonalFile.txt"))
    {
      return;
    }
     
    string[] lines = File.ReadAllLines("PersonalFile.txt");
    
    // Create a new ObservableCollection and initialize it with the array
    this.StaffDetails = new ObservableCollection<string>(lines);

    // You write to the file using this same collection directly,
    // without accessing the ListBox
    File.WriteAllLines("PersonalFile.txt", this.StaffDetails);
    
    // Option 1: assignment (static data link)
    this.lbxStaffDetails.ItemsSource = this.StaffDetails;
    
    // Alternative option 2: C# data binding (dynamic data link)
    var binding = new Binding(nameof(this.StaffDetails)) { Source = this };
    this.lbxStaffDetails.SetBinding(ItemsControl.ItemsSourceProperty, binding);
    
    // Alternative option 3 (recommended): XAML data binding (dynamic data link). See MainWindow.xaml
  }

  private void btnAddWaitingList_Click(object sender, RoutedEventArgs e)
  {
    _EmployeeID = tbxEmployeeID.Text;
    _Name = tbxName.Text;
    _PayRate = tbxPayRate.Text;            
    _Email = tbxEmail.Text;
    
    var employeeDetails = $"{_EmployeeID},{_Name},{_PayRate},{_Email}";
    
    // Modify the ObservableCollection. 
    // Since ListBox is observing this collection, it will automatically update itself
    this.StaffDetails.Add(employeeDetails);
  }
}
  

MainWindow.xaml

 <Window>

  <!-- Alternative option 3: XAML data binding (recommended) -->
  <ListBox x:Name="lbxStaffDetails" 
           ItemsSource="{Binding StaffDetails}" />
</Window>