Can I not remove items from a databound listview?
I bind the list view to a collection of users. lets say the listview is displaying name/address from the test object. If I try to remove the first item as below, it still displays all the records in the collection. This does not serve any good purpose. I just want to know what am I missing.
I'm suspecting it is because the datasource is still pointing to the collection which is unchanged. but if I manually change the listview items, shouldn't it take precedence?
protected void Page_Load(object sender, EventArgs e)
{
lvTest.DataSource = new List<TestObj>{ obj1, obj2..}; //pseudo code
lvTest.DataBind();
lvTest.Items.RemoveAt(0);
}
UPDATE
I understand this is not a good way of doing this. But the purpose of this question is to know why this does not work.
I think this might be a bug in the listview control. If you check the items count in the debugger after the first item is removed, you can see that the actual count is reduced by 1, but it still renders it.
Also, if you did lvTest.Items[0].Visible = false; after the RemoveAt(0), it actually makes the second item invisible which means that listview considers the first item removed but renders it regardless.
Update 2
On request, markup and codebehind used for test are below.
<asp:ListView ID="lvTest" runat="server" >
<LayoutTemplate><asp:PlaceHolder ID="itemPlaceholder" runat="server" /></LayoutTemplate>
<ItemTemplate>
<div><%# Eval("CompanyName")%><hr /></div>
</ItemTemplate>
<EmptyDataTemplate><div>None</div></EmptyDataTemplate>
</asp:ListView>
protected void Page_Load(object sender, EventArgs e)
{
lvTest.DataSource = GetCompanyList();
lvTest.DataBind();
lvTest.Items.RemoveAt(0);
}
public static List<Company> GetCompanyList()
{
List<Company> c = new List<Company>();
Company c1 = new Company();
c1.CustomerID = "2122";
c1.ContactName = "testc1";
c1.CompanyName = "test2";
Company c2 = new Company();
c2.CustomerID = "2123";
c2.ContactName = "testc2";
c2.CompanyName = "test2";
c.Add(c1);
c.Add(c2);
return c;
}
Yes you should be able to. As I understand it, the databinding is purely a process to populate the entries on the listview, so removing them after should work.
However, if this is what you want to do, a databound list does not seem like the right answer. Either get the data first, modify it, and populate the listview with the amended list; or amend the datastream to reflect jsut what you need - which you say is not an option.
What you are saying is that you want it to be databound, but you don't have a data source that actually matches the list you require. This means it should not be databound.
Another possibility that might be interesting is to use javascript/jquery to remove the items, which migh indicate whether this is a rendering problem or an object problem.
You may have to rebind the list with:
lvTest.DataBind();
Just for kicks, try binding the ListView only on the first page load.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
lvTest.DataSource = new List<TestObj>{ obj1, obj2 ..};
lvTest.DataBind();
}
lvTest.Items.RemoveAt(0);
}
Have you tried removing it from the datasource?
List<TestObj> l = lvTest.DataSource;
l.RemoveAt(0);
I don't think you can remove the items directly from the listview - they have to be removed from the bound datasource.
Another way to populate the listbox is with a loop and add it to the items:
List<string> l = new List<string>{"A", "B", "C"};
//this.listBox1.DataSource = l;
foreach (string s in l)
{
this.listBox1.Items.Add(s);
}
You should be able to use the RemoveAt now with it.
List data = new List<TestObj>{ obj1, obj2..}; //pseudo code
lvTest.DataSource = data;
lvTest.DataBind();
// if you want to remove, remove from data source and rebind
data.RemoveAt(0)
lvTest.DataSource =data;
lvTest.DataBind();
if you remove item from list view it will remove item from list view item collection but you already data blinded to list view with fist items, until you rebind, those items will display in the list view.
But when rebinding what is happening is it take data from data source not from list view items. again it will rebind fist items because list view remove not doing any change on data source.
best way is update the data source and rebind it.
I've had problems with this type of thing. Give this a try (VB but you get it)
for i as int = lvTest.Items.count -1 to 0
lvTest.Items.RemoveAt(i)
Next
There seems to be problems when the items in the list have to "reposition" themselves.
Related
I have two windows wpf.
On the first window I see data from DataGrid, on the second window I delete object from database.
First window:
private void mnRemoveProduct_Click(object sender, RoutedEventArgs e)
{
RemoveProduct window = new RemoveProduct();
window.ShowDialog();
ShowWatchTable();
} // mnRemoveProduct_Click
Second window:
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
if (cmbNameOfWatch.SelectedIndex > 0)
{
var value =
(from watch in obj.Watch.Where(w => w.mark == cmbNameOfWatch.SelectedValue.ToString())
select new { watch.id }).ToArray().First();
Watch watchObj = obj.Watch.Find(value.id);
obj.Watch.Remove(watchObj);
obj.SaveChanges();
this.Close();
}
} // btnDelete_Click
and function ShowTable
public void ShowWatchTable()
{
obj.Watch.ToList();
dataGrid.ItemsSource =
from watch in obj.Watch.Local
select new
{
ID = watch.id,
Brand = watch.mark,
Typ = watch.TypeWatch.typeName,
Price = watch.price + " $".ToString(),
Quantity = watch.number,
Fabricator = watch.Fabricator.fName,
Country = watch.Fabricator.Country.countryName
};
}
When I delete value from database, I still see this value in DataGrid. Only when I close and open this form then I no longer see this object. When I add an element, the same logic is used. Datagrid is updated with the ShowTable function and I see the updated database. But when deleting it it does not work. Please help
From what I can see, you are deleting from obj.Watch using
obj.Watch.Remove(watchObj);
obj.SaveChanges();
but when refreshing your list, you are binding to obj.Watch.Local which I assume is a local cache of your datasource. I don't see where you are syncing this so you might want to sync that before binding or perform your operations on the local sync and ensure that it sends it's changes to the remote data source.
Well, my guess your data source is a query result, but i'm not pretty sure that it's observable(f.e. when I need to bind to collections, I use IObservableCollection to notify that collection changed).
Probably you can try to call dbGrid.Items.Refresh(); method when your primary views gets focus back
You just need to manually refresh your DataGrid.
Add this after your datasource is updated:
dataGrid.Items.Refresh();
You should make sure the collection backing the source of your data items implements INotifyCollectionChanged.
MSDN link to docs, also copied relevant section here.
Binding to Data
To bind the DataGrid to data, set the ItemsSource property to an IEnumerable implementation. Each row in the data grid is bound to an object in the data source, and each column in the data grid is bound to a property of the data object. In order for the DataGrid user interface to update automatically when items are added to or removed from the source data, the DataGrid must be bound to a collection that implements the INotifyCollectionChanged interface, such as an ObservableCollection. To automatically reflect property changes, the objects in the source collection must implement the INotifyPropertyChanged interface. For more information, see Data Binding (WPF).
So I've found a lot of questions similar to this tho nothing really solve my problem..
I have a combobx that is bounded by a datasource
cmbProduct.DataSource = this.masterDataSet.Product.Where(x => x.Location == getLocation).AsDataView();
cmbProduct.DisplayMember = "Product";
cmbProduct.ValueMember = "Product";
But whenever i update the source, the combobox items does not update automatically. I still need to close then reopen the form.
is there a method to refresh/reload/or update the combobox?
Solution 1
You can use an implementation of IBindingList as DataSource to view changes of data source in the bound list control (complex two-way data binding). The most suitable implementation is System.ComponentModel.BindingList<T>.
Then when you add items to the binding list, or remove item from it you will see changes immediately in the control.
Solution 2
But as a more simple solution with less changes for you, you can reset the databinding of your cmbProduct this way when you need; for example after a change, call RefreshBindings();:
public void RefreshBindings()
{
var list = put your updated list here;
this.cmbProduct.DataSource = null;
this.cmbProduct.DataSource = list;
this.cmbProduct.DisplayMember = "set the display member here";
this.cmbProduct.ValueMember = "set the value member here";
}
You could implement an event that fires whenever the DataSet changes. The event could reset the Datasource and rebind it.
Somewhere in your code:
yourDataController.DataChanged += OnDataChanged;
and the implementation
public void OnDataChanged(object sender, EventArgs e)
{
cmbProduct.Items.Clear();
cmbProduct.DataSource = this.masterDataSet.Product.Where(x => x.Location == getLocation).AsDataView();
cmbProduct.DisplayMember = "Product";
cmbProduct.ValueMember = "Product";
}
Edit: Of course you need to manually implement the event and cause it to fire every time your data changes.
I found that Items.Clear does not always clear a listbox when the listbox has been filled via a DataSource. Setting the DataSource to Null allows it to be cleared with Items.Clear().
Is this the wrong way to do it this way? Is my thinking a bit wrong to do this?
Thanks.
Below is the code I prepared to illustrate my problem. It includes one Listbox and three buttons.
If you click the buttons in this order everything Everything works:
Fill List With Array button
Fill List Items With Array button
Fill List Items With DataSource button
But if you click the "Fill List Items With DataSource" button first, clicking on either of the other two buttons causes this error: "An unhandled exception of type 'System.ArgumentException' occurred in System.Windows.Forms.dll" with "Items collection cannot be modified when the DataSource property is set."
Comments?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnFillListWithArray_Click(object sender, EventArgs e)
{
string[] myList = new string[4];
myList[0] = "One";
myList[1] = "Two";
myList[2] = "Three";
myList[3] = "Four";
//listBox1.DataSource = null; <= required to clear list
listBox1.Items.Clear();
listBox1.Items.AddRange(myList);
}
private void btnFillListItemsWithList_Click(object sender, EventArgs e)
{
List<string> LStrings = new List<string> { "Lorem", "ipsum", "dolor", "sit" };
//listBox1.DataSource = null; <= required to clear list
listBox1.Items.Clear();
listBox1.Items.AddRange(LStrings.ToArray());
}
private void btnFillListItemsWithDataSource_Click(object sender, EventArgs e)
{
List<string> LWords = new List<string> { "Alpha", "Beta", "Gamma", "Delta" };
//listBox1.DataSource = null; <= required to clear list
listBox1.Items.Clear();
listBox1.DataSource = LWords;
}
}
According to Microsoft it looks like setting the Datasource to Null then Clearing the list is acceptable.
Source: http://support.microsoft.com/kb/319927
If your listbox is bound to a datasource, then that datasource becomes the 'master' of the listbox. You then don't clear the listbox, but you need to clear the datasource.
So if the listbox is bound to LWords, you do Lwords.clear() and the listbox would be cleared.
And that is correct behaviour, because that is what being databound is all about.
If you set the datasource to null, you are basically telling the listbox that it is no longer databound. And of course as a side effect of that it becomes empty.
But depending on the situation you might not want the listbox just to be cleared, but you might want to clear the datasource and the listbox both.
Suppose you want to clear LWords via your GUI, and that LWords is the source of your listbox, you press a button and you set the datasource to null, you see the listbox becoming empty, thinking that LWords is not empty, but LWords is not empty at all, and then in this situation that would be a bug.
May I ask why does both comboboxes trigger each other such that both have same values?
Can't I share a single list and have 2 comboboxes with different selected text?
private void Form1_Load(object sender, EventArgs e)
{
BindingList<string> list = new BindingList<string>();
list.Add("A");
list.Add("B");
list.Add("C");
list.Add("D");
bind(cbo1, list);
bind(cbo2, list);
}
private void bind(ComboBox combobox, BindingList<string> list)
{
// commented lines are in actual code,
// but appears unimportant in this question
//combobox.DropDownStyle = ComboBoxStyle.DropDown;
//combobox.AutoCompleteSource = AutoCompleteSource.ListItems;
//combobox.AutoCompleteMode = AutoCompleteMode.Suggest;
combobox.DataSource = list;
//combobox.Focus();
//combobox.Text = string.Empty;
//combobox.SelectedText = string.Empty;
}
UPDATE:
Ok, now I found out the issue is that the DataSource is managed by some BindingContext and CurrencyManager to automatically synchronise the list. But I feel someone must know how to disable this behaviour.
I don't wish to use 2 different lists because I want to be able to modify this single list at runtime and have the changes be reflected on all ComboBoxes. Any method to achieve this would be greatly appreciated.
You can "solve" it like this:
// combobox.DataSource = list;
var curr = new BindingSource(list, null);
combobox.DataSource = curr;
There is a default BindingSource (Currencymanager) linked to each Form that was keeping the 2 cbx in sync. But I'm not sure what the exact rules are here. I'm not even sure if the above is a good idea or not.
For small lists I would just make separate copies.
You cannot use the same object as the datasource for 2 seperate combo boxes. You should have list1 and list2 defined and populate each combobox with each one. Using the same datasource means that a selection in one combobox is reflected in the other.
I am trying to add some data inside my DataGrid.
I added some columns with the designer. Now I want to add rows with data inside the DataGrid.
Here's my code so far:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var dataContext = new PurchaseOrderDataContext();
var purchaseOrderTable = dataContext.GetTable<PurchaseOrder>();
var query = from a in purchaseOrderTable
select a;
var purchaseOrders = query;
foreach (var purchaseOrder in purchaseOrders)
{
// I believe that this method is the right one, but what do I pass to it?
// dataGrid1.Items.Add(test);
}
}
All I want to know is: What kind of object do I need to use to add something in the DataGrid, and what kind of object do I need to pass to that last method? Also how do I add, let's say, text to a certain column of a row I added?
Thanks a lot!
In general, you would bind the ItemsSource of the grid to a collection that supports change notification (IObservableCollection is idea) and just add to the collection. If the collection supports change notification, the grid will automatically display the new row.
Try this:
dataGrid1.ItemsSource = query;