I try to add strings that are in a string array to an ObservableCollection.
The way i do it is:
In my constructor i instantiate the collection and add the eventhandler to call my method that adds the strings from the array to the list:
public CLoggerViewModel()
{
this._currenLogContent = new ObservableCollection<string>();
this._memoryAppender.LogContentChanged += new LoggingEventHandler(OnLogContentChanged);
}
In the eventhandler i want to iterate through the string array and add each string to the collection:
public void OnLogContentChanged(object sender, LoggingEventArgs e)
{
string[] tmpString = { "A", "B", "C", "D" };
foreach (string s in tmpString)
{
_currenLogContent.Add(s);
}
}
In my Argument
e.LogEntries
which holds my string array i have always all the strings that i expect.
For simplicity/test i dont use the array that i get from my argument and instantiate it with A,B,C,D , since i was asked to change it in way that it can be verified here.
My problem is that always just the first string of the array will be added to my ObservableCollection.
Once the first string is added it seems that my foreach iteration gets terminated.
EDIT:
In my WPF XAML file if have the following list box where my ObservableCollection is bound to:
<ListBox Name="listViewLog" ItemsSource="{Binding LoggerViewModel.CurrentLogContent,UpdateSourceTrigger=PropertyChanged}" Margin="0,0,-248,-377" Grid.ColumnSpan="2" RenderTransformOrigin="0.586,0.51" Height="367" VerticalAlignment="Bottom" />
If i remove the listbox with the binding to my Observablecollection it iterates through the foreachloop. So the listbox or the binding from the listbox to my collection is causing the stop of the iteration. I already tried to use all the UpdateSourceTrigger options (Default,Explicit,LostFocus,PropertyChanged).
But in all cases it stops the iteration after the first round.
Is there anything else that i have to set in the ListBox properties to avoid stopping to add strings to the Observablecollection?
EDIT:
The only solution i found that works for me is to Wrap the strings that i want to add to the collection in a separate class and adding the objects to the list. In this case it does not break my foreach loop if i add objects to the list.
public void OnLogContentChanged(object sender, LoggingEventArgs e)
{
string[] tmpString = { "A", "B", "C", "D" };
foreach (string s in tmpString)
{
this.LogEntries.Add(new CLogEntry(s));
}
}
CLogEntry is just a Wrapper that exposes a single string.
With this workaround it works, but still, i cant understand why it does not work if i "directly" add a string to the Observablecollection.
Have you set up another Data Binding to your ObservableCollection? Or have you set up Control to Control Data Binding to your ListBox? As your code seems to be right and I can not reproduce the problem, I think there is a data bounded "logic" triggered by the ObservableCollection cases an error - this error prevent the iteration to be completed.
Related
I am unable to filter listbox data using linq and textbox.Listbox values don 't change when I enter text to be filtered inside the textbox. This function is important so that I can send the data to another form which contains the listbox.
public void GetList(List<SemesterDetails> modules)
{
string filter = txtFilter.Text;
foreach (var item in modules.Where(m => m.ModuleName.Contains(filter)))
{
LstModules.Items.Add(item);
}
}
I tried to call the function to the textbox event function:
private void txtFilter_TextChanged(object sender, TextChangedEventArgs e)
{
List<SemesterDetails> semesters = new List<SemesterDetails>();
GetList(semesters);
}
The result is:
Items remain unfiltered in the listbox.
List<SemesterDetails> semesters = new List<SemesterDetails>();
GetList(semesters);
This is an empty list.
public void GetList(List<SemesterDetails> modules)
{
string filter = txtFilter.Text;
foreach (var item in modules.Where(m => m.ModuleName.Contains(filter)))
{
LstModules.Items.Add(item);
}
}
Since you're passing in an empty list, the foreach will be executed 0 times, and therefore nothing can ever be added.
The filtering is irrelevant in this scenario.
Items remain unfiltered in the listbox
Nowhere in your code do you ever remove the items that are already in the listbox. Therefore, it's not possible for the listbox' contents to reduce.
When I add clear Listbox prior, I get no items
If you clear a list and then not add items to it (as discussed above), an empty list is indeed the logical outcome.
I have a string that separated by comma like this:
"test1,test2,test3"
and i want to convert those string to list with folowing code :
private void convertToList()
{
try{
List<string> myList = occ.Split(',').ToList();
listBox1.Items.Add(myList);
}catch(Exception e){
MessageBox.Show(e.Message);
}
}
I think that code will convert the string into a list and add it into a Listbox, instead, it shows only "collection"(yes only show the word "collection", nothing more) not the actual list.
why that's happened? can you tell me what's the right code?
Well, Add adds a single item which is a List<string> in your case. What should ListBox show for this? Collection seems to be a good enough solution. If you want to add entire collection in one go, try AddRange:
listBox1.Items.AddRange(occ.Split(','));
If you insist on Add, you have to loop in order to Add each item of the collection:
// To stop unwanted redrawing after each item addition
listBox1.BeginUpdate();
try {
foreach (var item in occ.Split(','))
listBox1.Items.Add(item);
}
finally {
listBox1.EndUpdate();
}
I wish for my ListBox to update the old values with new values rather than simply adding more and more lines to the ListBox like it does at the moment. However, I'm not sure where to look to implement something that can handle this.
My current code looks like this:
private void DisplayText(string rawData)
{
textArduinoData.Text = rawData;
string[] sortedData = rawData.Split(';');
for (int i = 0; i < sortedData.Length; i++)
{
listPortData.Items.Add(sortedData[i].ToString());
}
}
Could someone please point me in the right direction to implementing this update feature? Any advice would be much appreciated.
You need to manage the process. It is easy in concept but depending on how much data is needed to be processed, it could get slow quickly. Steps
Create a specialized token class which implements to INotifyPropertyChanged.
Have an ObservableCollection hold the class items from #1. The observable collection notifies the ListBox when an item is added or removed. This will allow your code to add items one at a time. (Solves 1 problem)
To solve the next problem of data changing: Have a property named Text, on the class in #1 which will hold the data, provide a property change notification.
In the list box bind to the list of items created in step 1 and specify to bind to the Text. Use of a data template for the listbox will allow you to bind to the Text property of the list's instance.
Provide the heuristics/ smarts to read incoming data and find the associated data in the observable collection from step 2. When found change the Text property of the existing data to the new and the binding of that list item will change accordingly.
You could check if the ListBox contains the string using the IndexOf method and then update the existing string (or simply do nothing) or add a new one depending on whether you get an index other than the default value of -1 back:
private void DisplayText(string rawData)
{
textArduinoData.Text = rawData;
string[] sortedData = rawData.Split(';');
int index;
for (int i = 0; i < sortedData.Length; i++)
{
if ((index = listPortData.Items.IndexOf(sortedData[i])) == -1)
{
listPortData.Items.Add(sortedData[i]);
}
}
}
So i am getting this error
{"'drpButton1' has a SelectedValue which is invalid because it does not exist in the list of items.\r\nParameter name: value"}
From everything i can read this is because the DropDownList either had existing items or had a selected index or value which is not in the new databound items.
But the thing is i can GUARANTEE that there are no existing items in the object and i can also say with confidence that there has not been an index selected that could be out of range.
Here is what the DropDownList object looks like directly before the .databind() call.
Here it is directly after the databind() call that has caused all the explosions.
My list object contains 7 items and in particular it contains the item that the databind method randomly decides to pick.
But here is the kicker i literally fill up 8 dropdowns with the EXACT same data and it works on the first dropdown just fine. No idea why the second one explodes.
EDIT: here is the code that does the binding:
Here is a snippet from the load method. the first call succeeds the second one fails but it doesn't fail all the time.
private void LoadShortCodeDropDownData()
{
// Initilization junk to get the resultList to use.
base.LoadListDropDown(drpButton0, (IList)resultList, "DeviceShortCodeIndexID", "DeviceShortCodeName", select);
MessageTextEnabled(drpButton0);
base.LoadListDropDown(drpButton1, (IList)resultList, "DeviceShortCodeIndexID", "DeviceShortCodeName", select);
MessageTextEnabled(drpButton1);
}
protected void LoadListDropDown(DropDownList dropDown, IList list, string valueField, string textField, string insertItem)
{
LoadListDropDown(dropDown, list, valueField, textField);
//dropDown.Items.Insert(0, new ListItem(insertItem, ""));
}
protected void LoadListDropDown(DropDownList dropDown, IList list, string valueField, string textField)
{
dropDown.DataSource = list;
dropDown.DataValueField = valueField;
dropDown.DataTextField = textField;
dropDown.DataBind();
}
EDIT2: I think the real question i have here is how is the databind picking which item to select? I noticed that the first dropdown that gets databound randomly selects the first value in the list while the second one tries to bind to the very last one in the list for some reason.
Is this happening on the first load of the page, or after a postback? Because if it's a postback, you very well could have SelectedIndex == 0 just by default.
I can't guarantee that this will solve the problem, but you could try adding
dropDown.SelectedIndex = -1;
... to the top of your second LoadListDropDown overload.
I've run in to this before, and I dont think you can bind the same list to multiple drop downs.
So lets say I have these classes:
public class Person
{
public string Name { get; set; }
}
public class PersonCollection : ObservableCollection<Person> { }
And lets say I have a ListView whose ItemsSource is bound to a PersonCollection. Now lets say I have this code:
public void AddPeople()
{
Person p = new Person() { Name = "Someone" };
MyPersonCollection.Add(p);
MyPersonCollection.Add(p);
MyPersonCollection.Add(p);
}
So now I have a ListView with three items in which all three items are references to the SAME object. So now I select lets say items with index 0 and 2 in the ListView.
The ListView.SelectedItems property will say I have ONE item selected since both visually selected items are the SAME object.
So how can I get the visually selected items so I can remove the items at indices 0 and 2, without removing the item at index 1?
In WinForms there is the ListBox.SelectedIndices property that would be useful here, but we don't have that in WPF, unfortunately...
You could iterate through the ListViewItems using ItemContainerGenerator.ContainerFromIndex, check ListViewItem.IsSelected and then remove them by index. However, this doesn't play well with virtualization because ContainerFromIndex could return null if you scroll away from the item and it gets virtualized.
The code would look something like this:
for(int ixPerson = myListView.Items.Count - 1; ixPerson >= 0; ixPerson--)
{
ListViewItem personItem = myListView.ItemContainerGenerator.ContainerFromIndex(ixPerson);
if (personItem.IsSelected)
{
mySourcePersonCollection.RemoveAt(ixPerson);
}
}
There are cases where this makes sense, adding people to a queue where appearing more than once is desirable for instance. For this case it seems like WPF is designed poorly. Is it possible to manually iterate between all items in the collection and check their selection state?
I think there's something wrong with your model! Whatever it is you are trying to achieve, I would try and find a more robust way of doing it.