Home > Back-end >  Pass an object Variable as a String to a Textbox in WPF/C#
Pass an object Variable as a String to a Textbox in WPF/C#

Time:12-12

I'm creating a practice application to practice using text files as a database.

  1. Take a pipe delineated text file with 14 People Data Entries
  2. Split the text file on the pipes and add each entry to a add each entry (about 150) to a list of the objects.
  3. I've casted list of objects to a string.

Now I want a button click to display that list in a textbox.

Here is the code.

namespace StaffRosterLewis.ViewModel
{
    class LoadData
    {
        public static void LoadRosterData()
        {

            
            string mypath = @"J:\zUtilities - Program Files\";
            mypath  = "StaffRoster - RosterOld.txt";
            List<Model.Person> people = new List<Model.Person>();
            List<string> lines = File.ReadAllLines(mypath, Encoding.ASCII).ToList();

            foreach (var line in lines)
            {
                string[] entries = line.Split('|');

                Model.Person newPerson = new Model.Person
                {
                    LastName = entries[1],
                    FirstName = entries[2],
                    Extension = entries[3],
                    Department = entries[4],
                    Team = entries[5],
                    Group = entries[6],
                    Title = entries[7],
                    Shift = entries[8],
                    EmergencyResponder = entries[9],
                    AEDCPRCert = entries[10],
                    Languages = entries[11],
                    Notary = entries[12],
                    Note = entries[13],
                    DutyLocation = entries[14]
                };
                //newPerson.Unknown15 = entries[15];

                people.Add(newPerson);


            }
            people.ToString();
            
        }
    }
}

I think the problem is here where the button click happens. I am simply trying to show the content of people above, but I lose the variable "people" no matter how I try to reference it. The above code is public and I made it static so the people variable should be accessible anywhere within the project. (I thought)

 private void Button_Show_Click(object sender, RoutedEventArgs e)
        {
            Button_Show.Content = $"{LoadData.LoadRosterData.people.ToString()}";
        }

CodePudding user response:

There are many problems with your code.

You are trying to access a method like a static property here.

Second, you have return type a void, which should be of type string.

Third you should override the ToString method to return the list items as sting in the required format.

You should use Path.Combine to get the path.

If you are planning to make the people as static variable then you have to accept that it’s not thread safe and make sure you reset it whenever it’s necessary else you may face unexpected items in the list.

And your code will throw exception if you have less than 15 |in a line

CodePudding user response:

You don't need to make LoadRosterData static. However, as mentioned by other commenters the method must return a result (in context of your current usage).

  • Because reading from a file can be resource consuming, you could increase performance when you store the result in a e.g. public property Peoples.
    This allows to access the collection without forcing to read the probably unchanged file. Since this would mean to introduce an instance variable, it's recommended to make LoadRosterData an instance member too.

  • To create a string from the collection you can use the StringBuilder. The below example uses the StringWriter, which allows asynchronous string creation. StringWriter also uses a StringBuilder internally.

  • You can further improve the code by overriding ToString of the Person type.

  • To improve the file reading performance you should use the asynchronous API of the StreamReader instead of the synchronous File class. Using StreamReader.ReadLineAsync will also save you an extra loop.

  • To make file handling and data model creation in particular more convenient, you should consider to make use of serialization.
    The recommended text format would be JSON. See Microsoft Docs: How to serialize and deserialize (marshal and unmarshal) JSON in .NET to learn how to do it. Deserializing the JSON file (preferably asynchronously) will automatically produce a collection of Person items without any hassle (this will eliminate the ugly and fragile index based access (to initialize instance properties) as well as making any delimiters like your pipe '|' redundant).

  • You are accessing the array starting from index '1'. But in computer science, index always starts from '0'. Not sure if you start from index '1' on purpose.

The following code fixes some issues and implements some performance improvements. It also shows how to convert the collection to a string, where each Person item is displayed in it's own line:

PersonDataReader.cs

class PersonDataReader
{
  // Consider to use an ImmutableList to prevent modification.
  // In the current context, this property could (and probably should) defined private.
  public List<Person> Persons { get; }

  // If Person is private, 'HasData' has to be defined private too
  public bool HasData => this.Persons.Any();

  // Constructor
  public PersonDataReader() => this.Persons = new List<Person>();

  public async Task<string> CreateRosterSummaryAsync()
  {
    // Use StringWriter to enable asynchronous string creation.
    // StringWriter also uses a StringBuilder internally to improve performance.
    using (var textWriter = new StringWriter())
    {
      if (!this.HasData)
      {
        await LoadRosterDataAsync();
      }

      // Alternatively use LINQ, 
      // for example Enuemrable.Select together with Enumerable.Aggregate
      // to concatenate the Person.ToString values
      foreach (Person person in this.Persons)
      {
        string personString = person.ToString();

        // Write a Person per line
        await textWriter.WriteLineAsync(personString);
      }

      return textWriter.ToString();
    }
  }

  private async Task LoadRosterDataAsync()
  {
    this.Persons.Clear();

    // Use Path.Combine to ensure a valid formatted path (improve robustness)
    string sourcePath = Path.Combine(@"J:\zUtilities - Program Files", "StaffRoster - RosterOld.txt");

    // Test if the file actually exists to avoid the expensive exception.
    // If the file not found exception is desired, remove the File.Exists condition.
    if (File.Exists(sourcePath))
    {
      return;
    }

    using (var fileReader = new StreamReaeder(sourcePath, Encoding.ASCII))
    {
      while (!fileReader.EndOfFile)
      {
        var line = await reader.ReadLineAsync();
        string[] personValues = line.Split('|', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);

        // If you would use serialization, constructing the type explicitly
        // can be avoided. A recommended text format to allow easy de-/serialization is the JSON format.
        var newPerson = new Model.Person 
        {
          // Check if starting from index '1' is really correct
          LastName = personValues[1],
          FirstName = personValues[2],
          Extension = personValues[3],
          Department = personValues[4],
          Team = personValues[5],
          Group = personValues[6],
          Title = personValues[7],
          Shift = personValues[8],
          EmergencyResponder = personValues[9],
          AEDCPRCert = personValues[10],
          Languages = personValues[11],
          Notary = personValues[12],
          Note = personValues[13],
          DutyLocation = personValues[14]
        };

        this.Persons.Add(newPerson);
      }
    }       
  }
}

Person.cs

class Person
{
  /* Properties of Person */

  // Example text representation of a 'People' object
  // using string interpolation
  public override string ToString()
    => $"Lastname: {this.LastName}; Firstname: {this.FirstName}; Duty location: {this.DutyLocation}"; 
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private PersonDataReader PersonDataReader { get; }

  public MainWindow()
  {
    InitializeComponent();

    this.PersonDataReader = new PersonDataReader();
  }

  private async void Button_Show_Click(object sender, RoutedEventArgs e)
  {    
    string personPerLineText = await this.PersonDataReader.CreateRosterSummaryAsync();

    // Why did you chose a Button here? It should be
    // this.DisplayTextBox.Text = personPerLineText;
    this.Button_Show.Content = personPerLineText;

    // Consider to display the complete List<Person> in a ListBox.
  }
}

Remarks

A ListBox is probably the better choice to display the list of Person. It's more intuitive to implement and would eliminate the need to worry about string representations and creation.

CodePudding user response:

Your method LoadRosterData has a return value void, means nothing. If you would like to return a string value and use it you may do it like this:

//returns a string
public static string MyStringMethod()
{
     string str = "My string";
     //Specify the return value with the return keyword
     return str;
}

Or if you want to return a list:

public static List<string> MyListMethod()
{
     List<string> list = new List<string>();
     list.Add("one");
     list.Add("two");
     list.Add("three");
     return list;
}

You can't do ToString() with list, but you can refer to the index of a person. And the useage:

button.Content = $"{MyStringMethod()}"

Or if you want to get person by name you can use IndexOf() method:

List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");

string number = "one";

button.Content = $"list[list.IndexOf(number)]";
  • Related