Home > Back-end >  Trouble with SelectedItemChanged C#
Trouble with SelectedItemChanged C#

Time:03-25

I am having trouble trying to get the labels to change to the current item selected in the list box. Is there a way to do this without creating a list? The original selected item is loading just fine, but I can't get the labels to update when the values change or when the selected items change. I need fresh eyes.

        public frmStudentScores()
    {
        InitializeComponent();
    }
    
    //load list box with initial students and grades
    private void frmStudentScores_Load(object sender, EventArgs e)
    {
        lstStudentInfo.Items.Add("Joel Murach|97|91|83");
        lstStudentInfo.Items.Add("Doug Lowe|99|93|97");
        lstStudentInfo.Items.Add("Anne Boehm|100|100|100");
        //set focus on first item in list
        lstStudentInfo.SelectedIndex = 0;

        //string to seperate items in list box by | to use for labels
        string[] str = lstStudentInfo.SelectedItem.ToString().Split('|');
        int total = 0;

        for (int i = 1; i < str.Length; i  )
        {
            total  = int.Parse(str[i]);
        }

        //total of all scores in selected row
        lblTotal.Text = total.ToString();

        //count of the scores in the selected row
        lblCount.Text = (str.Length - 1).ToString();

        //total divided by the count of the scores in selected row
        lblAverage.Text = (total / (str.Length - 1)).ToString();

    }

    

    private void lstStudentInfo_SelectedIndexChanged(object sender, EventArgs e)
    {
        //if the index is changed in the list box
        string current = lstStudentInfo.SelectedItem.ToString();

        if (lstStudentInfo.SelectedIndex >= 0)
        {


            string[] str = lstStudentInfo.SelectedItem.ToString().Split('|');


            int total = 0;

            {
                for (int i = 0; i < str.Length; i  )
                {
                    total  = int.Parse(str[i]);
                }

                {

                    lblTotal.Text = lstStudentInfo.SelectedItem.ToString();
                    lblCount.Text = (str.Length - 1).ToString();
                    lblAverage.Text = (total / (str.Length - 1)).ToString();
                }

            }
        }
    }

CodePudding user response:

Little confused by your code.

There are a lot of extra '{' '}' in there.

Also the comments don't align with the code, see comments below.

     private void lstStudentInfo_SelectedIndexChanged(object sender, EventArgs e)
        {
            //if the index is changed in the list box
            string current = lstStudentInfo.SelectedItem.ToString();
    
    #region  IF STATEMENT
            //Do you want it to only update if changed?
            //If so, you need to keep track of the index
            if (lstStudentInfo.SelectedIndex >= 0)
            {
                string[] str = lstStudentInfo.SelectedItem.ToString().Split('|');
                int total = 0;
    
                #region UNKNOWN REGION
                {
                    for (int i = 0; i < str.Length; i  )
                    {
                        total  = int.Parse(str[i]);
                    }
    
                    #region UNKNOWN REGION
                    {
                        lblTotal.Text = lstStudentInfo.SelectedItem.ToString();
                        lblCount.Text = (str.Length - 1).ToString();
                        lblAverage.Text = (total / (str.Length - 1)).ToString();
                    }
                    #endregion
                }
            }
            #endregion
    #endregion  
        }

I cleaned it up a little bit, added some error handling and debugging.

private void lstStudentInfo_SelectedIndexChanged(object sender, EventArgs e)
    {
        string current = string.empty;
        string lblCount = string.empty;
        string lblAverage = string.empty;
        string[] str = null;
        int total = 0;
        int valCount = 0;
        int valAvg = 0;

        //added some error handling
        try{
            if (lstStudentInfo.SelectedIndex >= 0)
            {
                //moved inside if statement, avoid errors if nothing is selected;
                current = lstStudentInfo.SelectedItem.ToString();
                str = current.Split('|');
                
                for (int i = 0; i < str.Length; i  )
                    {
                        total  = int.Parse(str[i]);
                    }

                valCount = (str.Length - 1);
                lblCount = valCount.ToString();

                valAvg = total/valCount;
                lblAverage = valAvg.ToString();
            }
        }
        catch(Exception e){
            lblCount = "####";
            lblTotal = "####";
            current = "ERROR";
        }

        //moved outside if statment, updates labels when nothing is selected
        lblTotal.Text = current;
        lblCount.Text = lblCount;
        lblAverage.Text = lblAverage;

        //added debugging output
        Debug.WriteLine($"Student Info Selection Changed: Count: {valCount} Avg: {valAvg}");
    }

Another thing to consider is to create single methods where you are performing the same code several times.

ie

private func<lstStudentInfo,string[]> get_items = x => x.Split('|');

Hopefully this puts you on the right path.

There also could be a problem in the XAML, hopefully the debug text helps you out there.

Lets add a bit more for some inspiration maybe?

Sounds like your not properly binding your view model to the window.

See below.

Window.xaml

Create a basic layout of what you want to display.

Bind everything over to the data model, event the selected item.

Let the view model handle all and the window just look pretty.

Window.xaml.cs

Create an instance of your view model and assign it as the Data Context.

From here, you can do anything you like to the view model, and the window will be updated.

My_View_Model.cs

Here is where everything resides that is bound to the Window.xaml.

Notice when the Label_Text is set, it then calls the OnPropertyChanged method.

There are many ways to implement this, but basically they are all the same, all you are doing is invoking the PropertyChanged Event with the name of the object that needs to be refreshed in the window.

The PropertyChanged event goes off and tells the window to update.

Any questions, feel free to ask.

Window.xaml:

<Window x:Class="WPF_Label_Update.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_Label_Update"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="The_WPF_Grid">
        <Grid.DataContext>
            <local:My_View_Model/>
        </Grid.DataContext>
        <Grid.Resources>
            <DataTemplate x:Key="myDataTemplate">
                <StackPanel>
                    <TextBlock Text="{Binding Path=obj_name}" />
                </StackPanel>
            </DataTemplate>   
        </Grid.Resources>
        
        
        <StackPanel Orientation="Horizontal">

            <ListBox x:Name="list_choice" 
                     SelectedItem="{Binding My_Selected_Object}" 
                     SelectionMode="Single" 
                     ItemsSource="{Binding My_Objects}"
                     ItemTemplate="{StaticResource myDataTemplate}">
            </ListBox>

            <StackPanel Orientation="Vertical">
            <Label x:Name="lbl_output_0" Content="{Binding Label_Text_0}"/>
            <Label x:Name="lbl_output_1" Content="{Binding Label_Text_1}"/>
            <Label x:Name="lbl_output_2" Content="{Binding Label_Text_2}"/>
            </StackPanel>

        </StackPanel>

    </Grid>
</Window>

Window.xaml.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPF_Label_Update
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        My_View_Model myViewModel;


        private void generateData()
        {
            StringBuilder _text = new StringBuilder() ;  

            for (int i = 0; i < 10; i  )
            {
                _text.Clear();
                _text.Append($"Number {i}");

                for (int ii = 0; ii < i; ii  )
                {
                    _text.Append($"|{ii}");   
                }                                                  

                myViewModel.Add_Item(new my_obj(_text.ToString()));
            }
        }

        public MainWindow()
        {
            InitializeComponent();

            myViewModel =
                new My_View_Model();

            this.The_WPF_Grid.DataContext = myViewModel;

            generateData();
        }

        private void ListBoxItem_Selected(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("Something Was Selected");

        }
    }
}

My_View_Model.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace WPF_Label_Update
{
    public class My_View_Model : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public string Label_Text_0 { get { return lbl0; } set { lbl0 = value; OnPropertyChanged(); } }
        private string lbl0 = string.Empty;

        public string Label_Text_1 { get { return lbl1; } set { lbl1 = value; OnPropertyChanged(); } }
        private string lbl1 = string.Empty;

        public string Label_Text_2 { get { return lbl2; } set { lbl2 = value; OnPropertyChanged(); } }
        private string lbl2 = string.Empty;

        public my_obj My_Selected_Object {
            get {
                return mySelectedObj;
            }
            set {
                mySelectedObj = value;
                on_mySelectedObjChange();
            } }
        private my_obj mySelectedObj = null;

        /// <summary>
        /// Update lables when something changes.
        /// </summary>
        private void on_mySelectedObjChange()
        {
            switch (mySelectedObj == null)
            {
                case true:
                    Label_Text_0 = string.Empty;
                    Label_Text_1 = string.Empty;
                    Label_Text_2 = string.Empty;
                    break;
                default:

                    Label_Text_0 = mySelectedObj.obj_text;
                    Label_Text_1 = mySelectedObj.obj_name;
                    Label_Text_2 = mySelectedObj.obj_count.ToString();

                    break;
            }  
        }

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }


        public ObservableCollection<my_obj> My_Objects { get { return my_objs; } }
        private readonly ObservableCollection<my_obj> my_objs = new ObservableCollection<my_obj>();

        public void Add_Item(my_obj new_obj)
        {
            my_objs.Add(new_obj);
        }

        public My_View_Model()
        {

        }
    }

    public class my_obj
    {
        public string obj_text { get; set; }         
        public string obj_name { get { return obj_text.Split('|')[0]; } }
        public int obj_count { get { return obj_text.Split('|').Length; } }

        public my_obj(string inputText)
        {
            this.obj_text = inputText.Trim();
        }
    }
}

CodePudding user response:

I got it to work with this - thanks everyone

    private void lstStudents_SelectedIndexChanged(object sender, EventArgs e)
 {
 if (lstStudents.SelectedIndex != -1)
 {


 string student =
 (string)studentScores[lstStudents.SelectedIndex];
 string[] scores = student.Split('|');
 int total = 0;
 for (int i = 1; i < scores.Length; i  )
 total  = Convert.ToInt32(scores[i]);
 int count = scores.Length - 1;
 int average = 0;
 if (total > 0)
 average = total / count;
 lblScoreTotal.Text = total.ToString();
 lblScoreCount.Text = count.ToString();
 lblAverage.Text = average.ToString();
 }
 }
  • Related