I actually display on my Listbox this list of item that i retrive from XML . When I click on an Item i am going back to the same method and creating a new list to display with different items.
I am wondering why it's not clearing the previous list.
This is the code I use, I can't figure this out ..
if (e.Error == null)
{
// Retrieving the subfolders
XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
XNamespace aNamespace = XNamespace.Get("http://schemas.datacontract.org/2004/07/System.IO");
var folders = from query in xdoc.Descendants(aNamespace.GetName("DirectoryInfo"))
select new Folder
{
Name = (string)query.Element("OriginalPath"),
};
ObservableCollection<Folder> LFolders = new ObservableCollection<Folder>();
foreach (Folder f in folders)
{
LFolders.Add(f);
}
listBox1.ItemsSource = LFolders;
listBox1.SelectionChanged += new SelectionChangedEventHandler(listBox1_SelectionChanged);
}
Two suggestions:
Consider using the MVVM pattern and then storing and updating your ObservableCollection on the view model instead.
Set the SelectionChanged event in XAML instead of where you're setting it now. For every call to this method you're appending an additional event handler to your listBox1.
If you set the Itemssource to null before you set the new value, I believe that will work. Also, you can try making LFolders a class variable. When you begin the method, clear the collection and then add to it. THe observable collection that is bound to the listbox will take care of updating the listbox.
Related
I'm fairly new to C# but understand basic concepts.
I'm currently working on a Uni assignment where I have to have multiple textboxes be entered as a single entry in a listbox, then save all entries to a text file. I also need to be able to load the text file and add new entries to the list.
I've figured out how to save data to a .txt file, as well as reloading the .txt file back into the listbox using
if (File.Exists("PersonalFile.txt"))
{
string[] line = File.ReadAllLines("PersonalFile.txt");
lbxStaffDetails.ItemsSource = line;
}
However, doing it this way I can't add new entries to the listbox due to the data binding, I get this error message System.InvalidOperationException: 'Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.'
Is there a way to remove the binding but keep the data in the listbox? Using lbxStaffDetails.ItemsSource = null; clears the listbox; or is there another way to read all lines of the .txt file to the listbox without using the file as a binding source?
Notes:
lbxStaffDetails is this listbox in question
PersonalFile.txt is the .txt holding the entries on new lines.
This is the first time I've bound data and files.
Edit:
Forgot to mention how I'm adding the data to the listbox so here's the code for that.
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);
}
As the code fires and gets to the bottom line it throws the error mentioned above.
Don't confuse data binding with simple value assignment. Data binding is a different concept, where a target binds to a data source using a Binding. A Binding will monitor target and source and delegates changes from one to the other. It's a bi-directional dynamic data link.
You can setup a Binding in XAML or C# (see Data binding overview in WPF).
You are not binding the ListBox to a file. You have read contents of a file to an array of strings. This array is then assigned to the ListBox.ItemsSource property.
Since you have populated the ListBox using the ItemsSource property, you are not allowed to modify its items using the Items property (InvalidOperationException).
You have to either assign the modified collection again to ListBox.ItemsSource (which will cause the complete ListBox to create all items again, which is bad for the performance) or make use of ObservableCollection.
It's a special collection that allows to be observed (Observer pattern). The observer gets notified by the observed collection via an event that the collection has changed (add/move/remove). Every ItemsControl is able to listen to this event and will automatically update itself.
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>
I'm trying to save and load the contents of a ListView list using C# .Net. I was hoping to save it by creating a variable System.Windows.Forms.ListView and then populating it with that.
Code snippet for Saving:
Properties.Settings.Default.followedUsersSettings = followerList;
Properties.Settings.Default.Save();
Code snippet for Loading:
if (Properties.Settings.Default.followedUsersSettings != null) {
followerList = Properties.Settings.Default.followedUsersSettings;
}
I can't seem to get it to work using that code. Are there any better ways of saving this that is as simple as possible? The list is single column, so an Array should also work, but i'm not sure what is recommended.
ok, I managed to get it working.
Saving:
//Convert the listview to a normal list of strings
var followerList = new List<string>();
//add each listview item to a normal list
foreach (ListViewItem Item in followerListView.Items) {
followerList.Add(Item.Text.ToString());
}
//create string collection from list of strings
StringCollection collection = new StringCollection();
//set the collection setting (created in Settings.settings as a specialized collection)
Properties.Settings.Default.followedUsersSettingsCollection = collection;
//persist the settings
Properties.Settings.Default.Save();
And for Loading:
//check for null (first run)
if (Properties.Settings.Default.followedUsersSettings != null) {
//create a new collection again
StringCollection collection = new StringCollection();
//set the collection from the settings variable
collection = Properties.Settings.Default.followedUsersSettingsCollection;
//convert the collection back to a list
List<string> followedList = collection.Cast<string>().ToList();
//populate the listview again from the new list
foreach (var item in followedList) {
followerListView.Items.Add(item);
}
}
Hopefully that makes sense to someone who found this from a Google search.
if (Properties.Settings.Default.followedUsersSettingsListView == null)
{
// adding default items to settings
Properties.Settings.Default.followedUsersSettingsListView = new System.Collections.Specialized.StringCollection();
Properties.Settings.Default.followedUsersSettingsListView.AddRange(new string [] {"Item1", "Item2"});
Properties.Settings.Default.Save();
}
// load items from settings
followerList.Items.AddRange((from i in Properties.Settings.Default.followedUsersSettingsListView.Cast<string>()
select new ListViewItem(i)).ToArray());
I have a TreeView that is displayed inside a panel. The data in the TreeView is based on data returned from the database. The first time, the data is correct. The second time, the TreeView is not refreshed, and the previous data is still showing in the tree. I checked the list that contain the data. The list returned the correct data. I've Google the issue, and could not resolved it with some of the answers that were posted. Here is a sample code of how the TreeView is being created and added to the Panel.
ReportGroups gr = new ReportGroups();
var Name = gr.GetReportName(groupID);
TreeView tr = new TreeView();
tr.BeginUpdate();
tr.Size = new Size(570, 600);
tr.Name = "Home";
tr.Nodes.Add("Reports Name");
tr.CheckBoxes = true;
if (Name.Count() > 0)
{
foreach (var item in Name)
{
if (item != null)
{
tr.Nodes[0].Nodes.Add(item.reportName);
}
}
}
tr.Nodes[0].ExpandAll();
tr.EndUpdate();
this.pDisplayReportName.Width = tr.Width * 2;
this.pDisplayReportName.Height = 300;
this.pDisplayReportName.Controls.Add(tr);
this.pDisplayReportName.Refresh();
What am I doing wrong?
try to add this.pDisplayReportName.Clear(); so data will not double up. :)
The easy option would be to use this.pDisplayReportName.Controls.Clear(); just after tr.EndUpdate();. But, this would cause an issue if you have other controls within the same Panel.
The best option would be to use this.pDisplayReportName.Controls.RemoveByKey("MyTree"); instead of this.pDisplayReportName.Controls.Clear();
And, another option would be to add a TreeView in design time (with name tr) rather than dynamically to the panel. Then, use tr.Nodes.Clear(); before tr.BeginUpdate(); and remove following two lines from your code.
TreeView tr = new TreeView();
.
.
.
this.pDisplayReportName.Controls.Add(tr);
Cheers
I'm working with windows phone 8 apps that getting data from restful service using json file and I getting problem to showing data I get from restful service into my listbox here is the code:
WebClient client = new WebClient();
client.DownloadStringCompleted += (s,e) =>
{
if(e.Error == null){
RootObject result = JsonConvert.DeserializeObject<RootObject>(e.Result);
CurrentDay = new ObservableCollection<Item>(result.results.items);
MessageBox.Show(CurrentDay[3].title.ToString());
}else{
MessageBox.Show("Sorry, try again at the next TechEd");
}
};
client.DownloadStringAsync(new Uri("http://places.nlp.nokia.com/places/v1/discover/explore?at=37.7851%2C-122.4047&cat=transport&tf=plain&pretty=true&app_id=xxxxx&app_code=xxxxx"));
the app_id and app_code sensored sorry :P
in that code i can't show the data into my listbox but when i using it showing the right data
MessageBox.Show(CurrentDay[3].title.ToString());
oh and I'm using mvvm light reference
I assume your ListBox has ItemsSource Property set to CurrentDay.
If that is true, the ListBox needs to know that the property was changed.
Since you are using ObservableCollection, the ListBox will notice chances to the current collection (e.g Add or Remove Items), but it can't know that a new ObservableCollection is assigned to the property unless you notify it.
To make it simple, I suggest you to Clear the CurrentDay collection and Add items to it, instead of creating a new ObservableCollection<Item>() each time:
CurrentDay.Clear();
foreach (var item in result.results.items)
CurrentDay.Add(item);
Make sure to initialize the collection in the constructor:
CurrentDay = new ObservableCollection<Item>();
I am using a Transitionals Slideshow control which has an observable collection of strings bound to the itemsource. These strings are the file paths to each picture in the slidehow. When I first load the WPF app, it runs this method correctly (using a directory path to generate the PicSlideShowCollection):
public void SelectImages(string path)
{
// Validate
if (string.IsNullOrEmpty(path)) throw new ArgumentException("path");
PicSlideShowCollection.Clear();
// Get directory info for specified path
DirectoryInfo di = new DirectoryInfo(path);
// Image mask
string[] extensions = new string[] { "*.jpg", "*.png", "*.gif", "*.bmp" };
// Search for all
foreach (string extension in extensions)
{
foreach (FileInfo fi in di.GetFiles(extension.ToLower()))
{
PicSlideShowCollection.Add(fi.FullName);
}
}
}
However, I have a button that allows the user to change the directory of images to use in the slideshow and re-runs the above method. When that is executed, I get this error:
GeneratorPosition '-1,1' passed to Remove does not have Offset equal
to 0.
This occurs on the PicSlideShowCollection.Clear() instruction.
If I comment that instruction, the new directory images get ADDED TO the original directory pictures which is NOT what I want.
I know this has to do with the PicSlideShowCollection being used as an item source to the Slide show control, but I need to know how I can prevent this error from occuring.
Thank you!
Slideshow.AutoAdvance = false;
Slideshow.SelcetedIndex=-1;
var count=PicSlideShowCollection.Count;
forearch(var item in newsources)
{
PicSlideShowCollection.Add(item);
}
while(count--)
PicSlideShowCollection.RemoveAt(0);
Slideshow.SelcetedIndex=0;
I can't explain why this error occurs. GeneratorPosition is used by the ItemContainerGenerator of an ItemsControl, which should simply work when you bind to its ItemsSource property and add or remove items to/from the source collection. Clearing the source collection is of course also a valid operation.
A possible workaround for the problem would be to reset the ItemsSource each time you switch to another image directory. So instead of clearing the existing collection
PicSlideShowCollection.Clear();
create a new collection and set ItemsSource to the new collection:
PicSlideShowCollection = new ObservableCollection<string>();
slideShowControl.ItemsSource = PicSlideShowCollection;