I'm learning UWP in VS2015 Community right now and having trouble with one section in regards to a ComboBox and could really use some help.
I'm writing a Bible app and have 3 ComboBoxes for Translation, Book, and Chapter. When I change the Book dropdown it should change the Chapter to 1. At least until I make a forward and back button for chapters, just covering the basics right now. When I change the Translation let's say from NIV to KJV it should change to the currently selected Book/Chapter in that translation.
I've preloaded the texts from XML and loaded them into an object called dataLoader. I'm doing selections on it via LINQ in the code below.
So right now I say something like:
private void DataLoader_Completed(object sender, EventArgs e)
{
dataLoaded = true;
cmb_Translation.ItemsSource = from t in dataLoader.Translations select new { t.TranslationShortName };
cmb_Book.ItemsSource = from b in dataLoader.Translations[0].Books select new { b.BookName };
cmb_Chapter.ItemsSource = from c in dataLoader.Translations[0].Books[0].Chapters select new { c.Index };
cmb_Book.SelectedIndex = 0;
cmb_Translation.SelectedIndex = 0;
cmb_Chapter.SelectedIndex = 0;
}
private void translationChanged()
{
chapterChanged();
}
private void bookChanged()
{
cmb_Chapter.ItemsSource = from c in dataLoader.Translations[cmb_Translation.SelectedIndex].Books[cmb_Book.SelectedIndex].Chapters select new { c.Index };
cmb_Chapter.SelectedIndex = 0;
}
private void chapterChanged()
{
textBlock_Verses.Text = dataLoader.Translations[cmb_Translation.SelectedIndex].Books[cmb_Book.SelectedIndex].Chapters[cmb_Chapter.SelectedIndex].TextLineSeparated;
}
private void cmb_Translation_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
translationChanged();
}
private void cmb_Book_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
bookChanged();
}
private void cmb_Chapter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
chapterChanged();
}
I'm getting errors back though on the first run that the index is out of range because at first the SelectedIndex of the translation is -1, if I run translation first it will give me an out of range on the book for SelectedIndex being -1.
I want the selected index changing to trigger the proper events but as you can see that's not going to work how it is now. Also the code is pretty messy, I've started looking a bit into Binding but there are a lot of hurdles like figuring out how to bind to a property that returns a LINQ result. I'm not sure how to move forward on this and definitely appreciate any help I can get.
Combobox can have no selection - selected item is null and this is how it's initialized, so before you set SelectedInexes all are null (this means that SelectedIndex == -1):
private void DataLoader_Completed(object sender, EventArgs e)
{
dataLoaded = true;
cmb_Translation.ItemsSource = from t in dataLoader.Translations select new { t.TranslationShortName };
cmb_Book.ItemsSource = from b in dataLoader.Translations[0].Books select new { b.BookName };
cmb_Chapter.ItemsSource = from c in dataLoader.Translations[0].Books[0].Chapters select new { c.Index };
cmb_Book.SelectedIndex = 0; // <- you set here selected index for book
// which fires bookchanged even right away
// In that event cmb_Translation.SelectedIndex
// is still -1 which will surely throw exception
cmb_Translation.SelectedIndex = 0;
cmb_Chapter.SelectedIndex = 0;
}
You probably should put some check-ups if values are properly set before using them. Also think if there is a chance when there is no selection state.
private void bookChanged()
{
if (cmb_Translation.SelectedIndex >= 0)
{
cmb_Chapter.ItemsSource = from c in dataLoader.Translations[cmb_Translation.SelectedIndex].Books[cmb_Book.SelectedIndex].Chapters select new { c.Index };
cmb_Chapter.SelectedIndex = 0;
}
}
There is one hiccup with this - you will have to launch bookchanged() at the endo of DataLoader_Completed manually, as it won't process before, due to -1.
Here's the solution I came to that works, although I think maybe a better solution could have been achieved using Binding to properties I've created, some feedback on improvements could always be helpful from a design perspective. What I did essentially was create an UpdateChapterText Boolean property that when set to false won't process my SelectedIndex change events, but will still allow me to update underlying data sources and change the SelectedIndex on the fly, that way I can process events in one go, otherwise it might be fire multiple events to update the underlying data sources. I needed to be able to do this because for the chapter text view it relies on both the translation and book selected indexes to be set, but the default behavior only allows one or the other to be set before events process, which becomes unsolvable at least I think for now unless you turn off events processing I found.
/// <summary>
/// This is a helper property for setting the Translation SelectedIndex
/// </summary>
private int TranslationIndex
{
get
{
return cmb_Translation.SelectedIndex;
}
set
{
cmb_Translation.SelectedIndex = value;
}
}
/// <summary>
/// This is a helper property for setting the Book SelectedIndex
/// </summary>
private int BookIndex
{
get
{
return cmb_Book.SelectedIndex;
}
set
{
cmb_Book.SelectedIndex = value;
}
}
/// <summary>
/// This is a helper property for setting the Chapter SelectedIndex
/// </summary>
private int ChapterIndex
{
get
{
return cmb_Chapter.SelectedIndex;
}
set
{
cmb_Chapter.SelectedIndex = value;
}
}
/// <summary>
/// Retrieves the currently selected Chapter listing
/// </summary>
public IEnumerable<ChapterIndex> CurrentChapters
{
get
{
return from c in dataLoader.Translations[TranslationIndex].Books[BookIndex].Chapters select new ChapterIndex { Index = c.Index };
}
}
/// <summary>
/// Retrieves Genesis in the first loaded translation
/// </summary>
public IEnumerable<ChapterIndex> Genesis
{
get
{
return from c in dataLoader.Translations[0].Books[0].Chapters select new ChapterIndex { Index = c.Index };
}
}
public IEnumerable<BookNames> CurrentBooks
{
get
{
return from b in dataLoader.Translations[TranslationIndex].Books select new BookNames { BookName = b.BookName };
}
}
/// <summary>
/// Allows events to process on ComboBoxes and Back/Forward Buttons
/// to change Chapters, you usually don't want to do this lots of
/// times in one second if changing the Translation/Book/Chapter
/// all at one time so you may set it to false first, update your
/// variables, and then set it back to true so updating events will
/// process correctly.
/// </summary>
public bool UpdateChapterText { get; set; }
/// <summary>
/// The DataLoader object loads up the various Bible translation texts
/// </summary>
TheBible.Model.DataLoader.DataLoader dataLoader;
public MainPage()
{
this.InitializeComponent();
dataLoader = new Model.DataLoader.DataLoader();
dataLoader.Completed += DataLoader_Completed;
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
}
private void DataLoader_Completed(object sender, EventArgs e)
{
UpdateChapterText = false;
cmb_Translation.ItemsSource = from t in dataLoader.Translations select new { t.TranslationShortName };
cmb_Book.ItemsSource = from b in dataLoader.Translations[0].Books select new { b.BookName };
cmb_Chapter.ItemsSource = Genesis;
cmb_Translation.SelectedIndex = 0;
cmb_Book.SelectedIndex = 0;
UpdateChapterText = true;
cmb_Chapter.SelectedIndex = 0;
}
private void translationChanged()
{
chapterChanged();
}
private void bookChanged()
{
UpdateChapterText = false;
cmb_Chapter.ItemsSource = CurrentChapters;
UpdateChapterText = true;
cmb_Chapter.SelectedIndex = 0;
}
private void chapterChanged()
{
textBlock_Verses.Text = dataLoader.Translations[TranslationIndex].Books[BookIndex].Chapters[ChapterIndex].TextLineSeparated;
}
private void decrementChapter()
{
UpdateChapterText = false;
if (this.cmb_Chapter.SelectedIndex == 0)
{
if (this.cmb_Book.SelectedIndex > 0)
{
this.cmb_Book.SelectedIndex--;
UpdateChapterText = true;
this.cmb_Chapter.SelectedIndex = CurrentChapters.Count() - 1;
}
}
else
{
UpdateChapterText = true;
this.cmb_Chapter.SelectedIndex--;
}
UpdateChapterText = true;
}
private void incrementChapter()
{
UpdateChapterText = false;
if (this.cmb_Chapter.SelectedIndex == this.cmb_Chapter.Items.Count - 1)
{
if (this.cmb_Book.SelectedIndex < this.cmb_Book.Items.Count - 1)
{
this.cmb_Book.SelectedIndex++;
UpdateChapterText = true;
this.cmb_Chapter.SelectedIndex = 0;
}
}
else
{
UpdateChapterText = true;
this.cmb_Chapter.SelectedIndex++;
}
UpdateChapterText = true;
}
private void cmb_Translation_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (UpdateChapterText)
translationChanged();
}
private void cmb_Book_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (UpdateChapterText)
bookChanged();
}
private void cmb_Chapter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (UpdateChapterText)
chapterChanged();
}
private void btn_Forward_Click(object sender, RoutedEventArgs e)
{
incrementChapter();
}
private void btn_Back_Click(object sender, RoutedEventArgs e)
{
decrementChapter();
}
private void btn_FullScreen_Click(object sender, RoutedEventArgs e)
{
var view = ApplicationView.GetForCurrentView();
if (view.IsFullScreenMode)
{
sym_FullScreen.Symbol = Symbol.FullScreen;
view.ExitFullScreenMode();
}
else
{
sym_FullScreen.Symbol = Symbol.BackToWindow;
view.TryEnterFullScreenMode();
}
}
Related
I'm using a SwipeRefeshLayout with a RecycleView inside.
This recycleView contains view-holders that have a click function that changes the display. This works fine until I refresh the view multiple times.
For example: If I refresh it 5 times, click on a viewholder, I will have to click 5 times on the return button to return to the recycleview fragment.
The code:
HomeFragment.cs:
private void HandleRefresh(object sender, EventArgs e)
{
try
{
page = 0;
adapter.clearMoments();
RefreshData(adapter, 0);
mySwipeRefreshLayout.Refreshing = false;
}
private async void RefreshData(MomentAdapterRV adapter, int page)
{
JsonValue json = await model.getMoments(page);
try
{
InitData(adapter, json, page);
}
private void InitData(MomentAdapterRV adapter, JsonValue json, int pageNum)
{
var myActivity = (MainActivity)this.Activity;
try
{
if (json.Count > 0)
{
for (var i = 0; i < json.Count; i++)
{
// Some code
adapter.addMoment(Moment moment)
}
// Some code
}
}
MomentAdapterRV.cs:
public MomentAdapterRV(Context context, List<Moment> items, MainActivity activity)
{
mItems = items;
mContext = context;
mActivity = activity;
cb = CommunityBuilderModel.Instance;
}
/// <summary>
/// This is the constuctor function of this adapter where the given class arguments (streamFragment, Moments) are being passed to the class variables.
/// </summary>
/// <param name="streamFragement"></param>
/// <param name="mItems"></param>
public MomentAdapterRV(StreamFragment streamFragement, List<Moment> mItems)
{
this.streamFragement = streamFragement;
this.mItems = mItems;
}
public void addMoment(Moment moment)
{
mItems.Add(moment);
NotifyDataSetChanged();
}
public void clearMoments()
{
mItems.Clear();
}
public override RecyclerView.ViewHolder
OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.MomentListItem, parent, false);
MomentViewHolder vh = new MomentViewHolder(itemView);
return vh;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
// Some code
vh.llMain.Click += (object sender, EventArgs e) =>
{
//Check if you don't get a negative position from the header.
if (holder.AdapterPosition >= 0)
{
// Create a new fragment and a transaction.
FragmentTransaction fragmentTx = mActivity.FragmentManager.BeginTransaction();
MomentFragment aDifferentDetailsFrag = new MomentFragment();
// Some code
// Replace the fragment that is in the View fragment_container (if applicable).
fragmentTx.Replace(Resource.Id.frameLayout1, aDifferentDetailsFrag);
// Add the transaction to the back stack.
fragmentTx.AddToBackStack(null);
// Commit the transaction.
fragmentTx.Commit();
//Put Argument
aDifferentDetailsFrag.Arguments = utilBundle;
}
};
}
}
}
Your problem is that you assign the Click event in OnBindViewHolder and you never unsubscribe this click event.
So for each time you refresh your items in your RecyclerView, OnBindViewHolder is called. This will also happen if your list is longer than can be displayed on screen and you scroll up and down.
Instead you should assign the Click even in OnCreateViewHolder so it is only hooked up once, and not every time it is shown.
I am trying to store multiple values from numerous buttons so I can return values of two or more things e.g. if chocolate and vanilla clicked both prices and names can be returned. I will also need to make calculations on the data set later. Whenever I return the data only the most recent values return rather than all of those I have selected.
private void VanillaBtn_Click(object sender, RoutedEventArgs e)
{
items.Price = 450;
items.Name = "Vanilla"
}
private void ChocolateBtn_Click(object sender, RoutedEventArgs e)
{
items.Price = 500;
items.Name = "Chocolate";
}
This is my class, any help or tips would be appreciated.
class Items
{
private int thePrice;
private string theName;
public int Price
{
get
{
return thePrice;
}
set
{
thePrice = value ;
}
}
public string Name
{
get
{
return theName;
}
set
{
theName = value;
}
}
Keep a list of whatever was clicked.
private List<Items> selectedItems = new List<Items>();
So, every time something is clicked, you store the object in the list defined above.
private void VanillaBtn_Click(object sender, RoutedEventArgs e)
{
var newItem = new Items();
newItem.Price = 450;
newItem.Name = "Vanilla";
selectedItems.Add(newItem);
}
I'm having strange issues with the check box control in C# .Net
My code below shows all logic that is required - _itemsChecked is a private dictionary containing all of the _fixtures and whether they are true or false (checked or un checked)
What I want is to be able to search my check list whilst retaining those which have been checked previously. If a checked item is included in the search results I want it to be checked.
The code nearly works! But for some reason boxes are randomly checked here and there, and it appears to work through debug but when the screen returns to the control it then hasn't worked.
Sure I'm missing something very simple.
My logic is:
DataSource includes those which match the typed search query,
Iterate through this list and check if the Guid is true in the dictionary.
If it is true then we set it as checked.
Hope I have provided adequate information.
Many thanks in advance.
private void searchTextBox_KeyUp(object sender, EventArgs e)
{
lst.DataSource = _fixtures
.OrderBy(f =>
f.Description)
.Where(f =>
f.Description.ToLower().Contains(searchFixturesTextBox.Text.ToLower()))
.ToList();
lst.DisplayMember = "Description";
for (var i = 0; i < lst.Items.Count; i++)
if(_itemsChecked.Contains(new KeyValuePair<Guid, bool>(((Fixture)lst.Items[i]).Guid, true)))
lst.SetItemChecked(i, true);
}
void lst_ItemCheck(object sender, ItemCheckEventArgs e)
{
var selectedItem = ((ListBox) sender).SelectedItem as Fixture;
if (selectedFixtureItem != null)
_itemsChecked[selectedItem.Guid] = e.CurrentValue == CheckState.Unchecked;
}
So I put this together from a few examples I found. The majority of the work came from How do I make a ListBox refresh its item text?
public class Employee
{
public string Name { get; set; }
public int Id { get; set; }
public bool IsChecked { get; set; }
public override string ToString()
{
return Name;
}
}
public partial class Form1 : Form
{
// Keep a bindable list of employees
private BindingList<Employee> _employees;
public Form1()
{
InitializeComponent();
// Load some fake employees on load
this.Load += new EventHandler(Form1_Load);
// Click once to trigger checkbox changes
checkedListBox1.CheckOnClick = true;
// Look for item check change events (to update there check property)
checkedListBox1.ItemCheck +=
new ItemCheckEventHandler(CheckedListBox_ItemCheck);
}
// Load some fake data
private void Form1_Load(object sender, EventArgs e)
{
_employees = new BindingList<Employee>();
for (int i = 0; i < 10; i++)
{
_employees.Add(new Employee()
{ Id = i, Name = "Employee " + i.ToString() });
}
// Display member doesnt seem to work, so using ToString override instead
//checkedListBox1.DisplayMember = "Name";
//checkedListBox1.ValueMember = "Name";
checkedListBox1.DataSource = _employees;
// Another example databind to show selection changes
txtId.DataBindings.Add("Text", _employees, "Id");
txtName.DataBindings.Add("Text", _employees, "Name");
}
// Item check changed, update the Employee IsChecked property
private void CheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
CheckedListBox clb = sender as CheckedListBox;
if (clb != null)
{
Employee checked_employee = clb.Items[e.Index] as Employee;
if (checked_employee != null)
{
checked_employee.IsChecked = (e.NewValue == CheckState.Checked);
}
}
}
// Just a simple test that removes an item from the list, rebinds it
// and updates the selected values
private void btnChangeList_Click(object sender, EventArgs e)
{
_employees.RemoveAt(1);
checkedListBox1.DataSource = _employees;
for (var i = 0; i < checkedListBox1.Items.Count; i++)
{
Employee employee_to_check = checkedListBox1.Items[i] as Employee;
if (employee_to_check != null)
{
checkedListBox1.SetItemChecked(i, employee_to_check.IsChecked);
}
}
}
}
I've been puzzling over this one for a few days now and it's got me pretty beaten, but to be honest I'm not all that experienced yet and I'm having trouble with DataGridView - which seems a common topic.
public partial class frmMain : Form
{
ServerConnection sabCom;
private BindingSource jobSource = new BindingSource();
private void timer1_Tick(object sender, EventArgs e)
{
if (bgUpdateThread.IsBusy == false)
{
bgUpdateThread.RunWorkerAsync(sabCom);
}
}
}
private void frmMain_Load(object sender, EventArgs e)
{
timer1.Interval = 3000;
timer1.Start();
}
private void bgUpdateThread_DoWork(object sender, DoWorkEventArgs e)
{
ServerConnection s = e.Argument as ServerConnection;
s.update();
e.Result = s;
}
private void bgUpdateThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.sabCom = e.Result as ServerConnection;
if (dgvQueueFormatted == false)
{
dgvQueue_Init(); //Applies formatting and loads column width. Inits data sources.
}
else
{
dgvQueue_Update();
}
}
private void dgvQueue_Update()
{
dgvQueue.Refresh();
}
private void dgvQueue_Init()
{
try
{
jobSource.DataSource = sabCom.queue.jobs;
dgvQueue.DataSource = jobSource;
try
{
//Apply saved column spacing to the dgvQueue
//Uses reflection to set dgvQueue to DoubleBuffer
}
catch
{ }
}
catch
{ }
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
//Saves information about the dgvQueue on shutdown.
}
Queue Class:
public class Queue
{
private string _status;
public string status { get { return _status; } set { _status = value; } }
private string _eta;
public string eta { get { return _eta; } set { _eta = value; } }
private List<Job> _jobs;
public List<Job> jobs
{
get
{
return _jobs;
}
set
{
_jobs = value;
}
}
private List<string> _categories;
public List<string> categories { get { return _categories; } set { _categories = value; } }
private XmlDocument xmld;
private ServerConnection m_parent;
private XmlNodeList _xmljobs;
public Queue(ServerConnection srvConn)
{
//fetch the Queue xml
m_parent = srvConn;
xmld = new XmlDocument();
_jobs = new List<Job>();
}
public void update()
{
updateXml();
updateQueue();
updateJobs();
}
private void updateXml()
{
//Loads xml file into xmld
}
private void updateQueue()
{
XmlNodeList elements = xmld.SelectNodes("queue");
foreach (XmlNode element in elements)
{
_status = element.SelectSingleNode("status").InnerText;
_eta = element.SelectSingleNode("eta").InnerText;
}
}
private void updateJobs()
{
_xmljobs = xmld.SelectNodes("queue/job");
jobs.Clear();
foreach (XmlNode xmljob in _xmljobs)
{
Job t_job;
_status = xmljob.SelectSingleNode("status").InnerText;
_eta = xmljob.SelectSingleNode("eta").InnerText;
//Create temp job to match against list.
t_job = new Job(_status, _eta);
jobs.Add(t_job);
}
}
Job class: In reality it holds around 30 values of varying types, but they're all in the same format:
public class Job
{
private int _status;
public int status { get { return _status; } set { _status = value; } }
private string _eta;
public string eta { get { return _eta; } set { _eta = value; } }
public Job(string status, string eta)
{
_status = status;
_eta = eta;
}
}
When interacting with the DataGridView I get the error:
The following exception occured in the DataGridView:
System.IndexOutOfRangeException: Index does not have a value.
at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
at System.Windows.Forms.DataGridViewDataConnection.GetError(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)
And when entering the debugger it's triggered on the initial Application.Run(new frmMain(). What on earth am I doing wrong? The program still functions and updates normally but I can't even handle the event to suppress the default error message!
Edit - Answer!
Instead of clearing the list and re-creating it, just updating the values within it works better. For the moment I have this code:
t_job = _jobs.FirstOrDefault(c => c.nzo_id == t_nzo_id);
if (t_job == null) //Job not in list, insert
{
t_job = new Job(t_status, i_index, t_eta, i_timeLeft, t_age, i_mbleft, i_mb, t_filename, i_priority, t_category, i_percentage, t_nzo_id, this);
jobs.Add(t_job);
}
else //update object in current list
{
jobs[t_job.Index].status = t_status;
jobs[t_job.Index].priority = i_priority;
jobs[t_job.Index].category = t_category;
jobs[t_job.Index].percentage = i_percentage;
jobs[t_job.Index].timeleft = i_timeLeft;
jobs[t_job.Index].mbleft = i_mbleft;
}
Which prevents it!
The problem seems to be that with a refresh interval of say 1 second, while the user is scrolling or trying to access the fields of the data the data is constantly being removed and readded.
This causes the binding to to call try call a value that it thinks might still be there, but isn't and that is why you are getting the index out of range exception
The first thing I would suggest doing is in your updateJobs method as well as all the other lists that are refreshed like the queue list, instead of clearing the list everytime is first checking to see if the job from the xml exists in the current job list, if it does then you can change the current values if the value has changed otherwise do nothing.
To check if the job exists it is as easy as:
t_job = _jobs.FirstOrDefault(c => c.Filename == t_filename);
This will return a null if the job does not exist, I would assume filename might not be unique, so might want to change it so that it really is unique i.e.
t_job = _jobs.FirstOrDefault(c => c.Filename == t_filename && c.nzo_id == t_nzo_id);
One thing you will now have to cater for is that old jobs won't be automatically removed, so whenever a new history job is added, first check to see if it exists in the queue and then remove it there before adding it to the history list.
so change your properties to be:
public int Index
{
get { return _index; }
set
{
if (_index != value)
_index = value;
}
}
instead of:
public int Index { get { return _index; } set { _index = value; } }
The other thing is that try catch exceptions are expensive, so instead of having:
try { i_percentage = double.Parse(t_percentage); } catch { }
because you know that if there is a value it is going to be a double, you can change it to:
if (!string.IsNullOrEmpty(t_percentage))
i_percentage = double.Parse(t_percentage);
now if you don't know if the value in t_percentage is going to be a double you can use a try-parse:
if (!string.IsNullOrEmpty(t_percentage))
double.TryParse(t_percentage,out i_percentage);
This way you avoid the overhead caused by an exception. This might be micro-optimizing and is not always neccessary if it doesn't actually cause a problem, but given that you can have hundreds of jobs, each with 10 or so properties refreshing everysecond, things can actually get noticeably slower if even 2 of the 10 properties throws an exception.
One more thing, after your backgroundworker is completed, in the method: dgvQueue_Update() you are calling the ResetBindings(true); this causes your whole datagrid to refresh instead of just the individual items.
try changing it to:
for (int i = 0; i < jobSource.List.Count; i++)
jobSource.ResetItem(i);
The difference is this:
this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex));
compared to when you say ResetBindings:
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
I have two listBoxes. The first listbox contains the list of traffic violations. When you click the add button and execute the code, the listbox2 got this item "ListBoxTest.Violation", not the item being displayed from the listBox1...
What is wrong with my code?
namespace ListBoxTest
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
private List<Violation> violationList = new List<Violation>();
public MainForm()
{
InitializeComponent();
}
void MainFormLoad(object sender, EventArgs e)
{
LoadViolations(); // Initialize and add violations to violationList.
listBox1.DataSource = violationList; // Set the DataSource property.
listBox1.ValueMember = "Code";
listBox1.DisplayMember = "Description";
}
void LoadViolations()
{
Violation violation;
violation = new Violation("001", "Beating the red light");
violationList.Add(violation);
violation = new Violation("002", "Exceeding posted speed limit on the road");
violationList.Add(violation);
violation = new Violation("003", "Driving a vehicle without license to drive");
violationList.Add(violation);
violation = new Violation("004", "Driving a non registered vehicle");
violationList.Add(violation);
violation = new Violation("005", "Vehicle has no plate number");
violationList.Add(violation);
}
void BtnAddClick(object sender, EventArgs e)
{
listBox2.Items.Add(listBox1.SelectedItem); // Add item from listBox1 to listBox2;
}
}
/// <summary>
/// Violation Class
/// Properties: Code, Description
/// </summary>
public class Violation
{
private string _code;
private string _description;
public Violation(string code, string description)
{
_code = code;
_description = description;
}
public String Code
{
get { return _code; }
set { _code = value; }
}
public String Description
{
get { return _description; }
set { _description = value; }
}
}
}
Type cast the selected item to Violation. This should fix the problem.
Edit: I have modified the code to fix the issue.
private void AddClick(object sender, EventArgs e)
{
// Set the DataSource property.
listBox2.ValueMember = "Code";
listBox2.DisplayMember = "Description";
listBox2.Items.Add((Violation)listBox1.SelectedItem);
}
Make sure listbox2 has the same settings as listbox1, e.g. listbox2.ValueMember, listbox2.DisplayMember..