refresh button to update xml bounded listbox - c#

I have a listbox which is bounded to xml datasource.
I want to create a button that refreshes the list box.
listbox1.refresh is not working.
Thaks
private void LoadXML()
{
presets.Clear();
if(System.IO.File.Exists(GetXMLFileName()))
{
XDocument xDoc = XDocument.Load(GetXMLFileName());
XElement xMain = xDoc.Element("Main");
foreach(var xPreset in xMain.Elements())
{
Preset preset = new Preset(xPreset);
presets.Add(preset);
}
}
else
{
for (int i = 0; i < maxPresets; i++)
presets.Add(new Preset() { Id = i });
}
listBox1.Items.Clear();
listBox1.DataSource = presets;
listBox1.DisplayMember = "name";
}

Take a look at this line:
listBox1.DataSource = presets;
Although you are setting DataSource, after the first call to LoadXML(), you are setting it to the same value it already had. Since the collection reference is the same, the assignment is effectively a no-op: the underlying data source didn't change, so the list box won't refresh. The simplest fix would be to reset DataSource to null before setting it to presets. Ideally, though, you should simply bind the data source to an ObservableCollection, or some other collection that supports change notifications.
Also, as #user2880486 noted, you should not use both Items and DataSource; they are designed to be mutually exclusive. Use one or the other, but not both.

Related

Sorting WPF datagrid with ComboBox

I have filled a WPF DataGrid by setting the ItemSource with the desired table (DataTable). Now I do want to sort the table by a value in one of the DataTable column. I do not want to sort the table using the default sorting which can be used by clicking on the table headers. I do want want sort the table using a ComboBox. You can select one of the items in the combobox and the table will sort. I have implemented the code below. However when I change the selected item in the combobox, the DataGrid will be empty. All records are not shown. The headers are still visible.
private void DbFilterSortByBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (DbFilterSortByBox.SelectedItem != null)
{
DbMainTable.ItemsSource = SortedTable(DbFilterSortByBox.SelectedItem.ToString()).DefaultView;
for (int i = 0; i < propertiesHandler.TablePropertiesIndex.Length; i++)
{
if (propertiesHandler.TablePropertiesValue[i] == false)
{
DbMainTable.Columns[propertiesHandler.TablePropertiesIndex[i]].Visibility = Visibility.Hidden;
}
}
}
}
private DataTable SortedTable(string sortingBy)
{
DataView sortedTable = dataHandler.KicksTable.Clone().DefaultView;
if (sortingBy == "Type")
{
sortedTable.Sort = "Type";
} else if (sortingBy == "Size, ascending")
{
sortedTable.Sort = "Size asc";
}
return sortedTable.ToTable();
}
The DbFilterSortByBox_SelectionChanged event occurs when the selected item in the ComboBox is changed. The SortedTable function is then called to sort the table by the desired value.
Does someone see where it goes wrong and how to solve this problem?
If the itemssource is set to
dataHandler.KicksTable.DefaultView = dataHandler.KicksTable.DefaultView;
You can just do:
dataHandler.KicksTable.DefaultView.Sort = "Size asc";
dataHandler.KicksTable.DefaultView.Refresh();
No cloning.
Obviously, you still need the logic chooses which sort string to set.
DataTable.Clone creates a new DataTable with the same structure as the original DataTable, but does not copy any data (the new DataTable will not contain any DataRows - it will always be empty).
To copy both the structure and data into a new DataTable, use must DataTable.Copy.
However, you shouldn't clone nor copy the DataTable at all. It just creates unnecessary overhead.
Simply bind the DataTable to the DataGrid.ItemsSource. Because the ItemsControl in general works on collection views, all you need to do is to sort/filter/group the view. You don't have to refresh anything or reset the ItemsSource property.
Because collection view and collection are different layers, modifying the collection view does not modify the underlying collection (or DataTable). You therefore shouldn't clone/copy your DataTable before or after sorting/filtering/grouping it.
public Constructor()
{
InitializeComponent();
// Prefer data binding instead by simply binding to the DataTable.
// Binding will automatically get the default view of the DataTable.
DbMainTable.ItemsSource = dataHandler.KicksTable.DefaultView;
}
private void DbFilterSortByBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;
if (comboBox.SelectedItem is null)
{
return;
}
DataView kicksTableView = dataHandler.KicksTable.DefaultView;
var sortConstraint = comboBox.SelectedItem as string;
SortTable(kicksTableView, sortConstraint);
}
private void SortTable(DataView tableView, string sortConstraint)
{
if (sortConstraint.Equals("Type", StringComparison.OrdinalIgnoreCase))
{
// Setting the Sort property will automatically sort the view and the DataGrid that binds to this view.
tableView.Sort = "Type";
}
else if (sortConstraint.Equals("Size, ascending", StringComparison.OrdinalIgnoreCase))
{
// Setting the Sort property will automatically sort the view and the DataGrid that binds to this view.
tableView.Sort = "Size asc";
}
}

C# - Update databound combo box automatically

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.

Is it wrong to set ListBox DataSource property to null in order to change list Items?

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.

Share ComboBox DataSource

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.

Adding data to WPF DataGrid

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;

Categories