ListBox deletion - c#

I have a listbox, with 2 buttons, new and delete. new adds an item into the list box, and the delete button should delete the item out of the list box. The list box items are tied to a class that stores user entered data from text boxes below.
private void AddListBox()
{
lstCondition.BeginUpdate();
Condition cond = new Condition("");
cond.Name = string.Format("Condition {0}", _selection.NetConditions.Count + 1);
_selection.NetConditions.Add(cond);
lstCondition.EndUpdate();
lstCondition.SelectedItem = cond;
cboNetCondition.Properties.Items.Clear();
cboNetCondition.Properties.Items.AddRange(NetCondition);
cboControlType.Properties.Items.Clear();
cboControlType.Properties.Items.AddRange(ControlType);
cboFlowRate.Properties.Items.Clear();
cboFlowRate.Properties.Items.AddRange(FlowRate);
}
private void btnNew_Click(object sender, EventArgs e)
{
AddListBox();
}
the cbo items are comboboxes, whose data gets tied in the condition class to each instance of the list box.
public frmNetConditions(Condition condo, Selection selection)
{
InitializeComponent();
_selection = selection;
lstCondition.DataSource = _selection.NetConditions;
condition = _selection.NetConditions.Count;
}
private void btnDelete_Click(object sender, EventArgs e)
{
selectedCondition = (Condition)lstCondition.SelectedItem;
cboControlType.SelectedIndex = -1;
cboNetCondition.SelectedIndex = -1;
cboFlowRate.SelectedIndex = -1;
txtFlowRate.Text = string.Empty;
txtStatPressure.Text = string.Empty;
txtDampOpening.Text = string.Empty;
txtDensity.Text = string.Empty;
cboDensity.SelectedIndex = -1;
lstCondition.Items.Remove(lstCondition.SelectedItem);
lstCondition.Refresh();
}
After pressing this delete button, the listbox, still contains the item i wish to delete, im unsure why thats the case?
Update with datasource
public List<Condition> NetConditions { get { return _netconditions; } }

As already suggested, you should bind to a BindingList<Condition> instead of a List<Condition>. This allows you to change the datasource and the control (ListBox) to get notified by your changes. The code should look like this:
lstCondition.ValueMember = "ConditionId";
lstCondition.DisplayMember = "Name";
lstCondition.DataSource = NetConditions;
After defining the binding, the correct way of operating on the ListBox items is to remove from the datasource, not the ListBox itself:
// SelectedItem should be checked for null (no selection is an option)
NetCondition.Remove((Condition)lstCondition.SelectedItem);
However, if you plan to change properties from an element (so, not the list itself), the control is notified only if your element (Condition) implements INotifyPropertyChanged interface.

Related

Get data from DataGrid WPF

How can I get data from DataGrid when ComboBox' text is equal with the record of the DataGrid:
combobox1.ItemsSource = database.Mahs.ToList();
combobox1.DisplayMemberPath = "MahName";
and
private void datagrid_customer_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var data = datagrid_customer.SelectedItem;
string id = (datagrid_customer.SelectedCells[0].Column.GetCellContent(data) as TextBlock).Text;
txt_f1.Text = id;
}
It shows me the id, but when i selected item but i want show me id when combobox.Text = name of the row in the DataGrid, then show the id of that row.
I would suggest changing your approach slightly, and retrieving the entire object from both controls for the comparison. This way you have full access to the properties of each selected object.
If the objects you're retrieving from your database override .Equals and .GetHashCode you could do away with some of the if statements below. But to get you started here's a quick example of your change listener
private void datagrid_customer_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Cast the objects from your DataGrid and your ComboBox.
YourDataObject dataItem = (YourDataObject)dataGrid1.SelectedItem;
YourDataObject comboItem = (YourDataObject)combo.SelectedItem;
// Compare your objects and decide if they're the same. Ideally you would
// have Equals and HashCode overridden so you could improve this
if (dataItem == null || comboItem == null)
text.Text = "Not Matching";
else
{
if (dataItem.MahName == comboItem.MahName)
// You've got full access to the object and all it's properties
text.Text = dataItem.Id.ToString();
else
text.Text = "Not Matching";
}
}

Moving Item In Observable Collection resets moved item to default values

I have a Listview that contains Comboboxes, a textbox, and a check box. I can add multiple rows, and each time a row is added you get a new row in the list view that contains all 5 of those items. I have a default prepended to the beginning of each of the Comboboxes to prompt the user to select a value from the dropdownlist that it is bound to. The problem is when I try to reorder the rows the move event resets all the values to their default values, clears out the textbox and unchecks the check box.
public ObservableCollection<RemovePunctuationRules> PunctuationRules
{
get
{
return _punctuationRules ?? (_punctuationRules = new ObservableCollection<RemovePunctuationRules>());
}
set
{
_punctuationRules = value;
}
}
private void MenuItemMoveUp_OnClick(object sender, RoutedEventArgs e)
{
var selectedIndex = ListView.SelectedIndex;
if (ListView.SelectedIndex > 0)
{
var itemToMoveUp = PunctuationRules[selectedIndex];
PunctuationRules.RemoveAt(selectedIndex);
PunctuationRules.Insert(selectedIndex - 1, itemToMoveUp);
ListView.SelectedIndex = selectedIndex - 1;
}
}

Changing SelectedItem property of a ListView programatically

I have a 2 ListViews with same items in both of them. What I want to do is that when a selection is made in one ListView, the same selection should be reflected in the other ListView also. The two ListViews are bound to two different ViewModels but both the ViewModels implement the same interface.
I've overridden the Equals methods in both ViewModels.
The two ListViews are on different XAML pages. The first ListView say LV1 is in Page1.xaml and LV2 is in Page2.xaml. What I want is that when I am changing the selection in LV2 the selection in LV1 should also change( one way only ). I've set x:FieldModifier="public" on LV1 and exposing through a static property of Page1 like this:
public sealed partial class Page1 : Page
{
public static Page1 page1 { get; private set; }
}
And on Page2, I have this :
private async void LV2_ItemClick(object sender, ItemClickEventArgs e)
{
var selected = e.ClickedItem as ISomeCommonInterface;
//Comparision is successful --> Contains() always returns corect value;
if (Page1.page1.LV1.Items.ToList().Contains(selected))
{
Page1.page1.LV1.SelectedItem = null; // this works
Page1.page1.LV1.SelectedItem = selected; // this doesn't work
}
}
I've found that inside the if condition, assignment to null changes the SelectedItem of LV1 to null but the next line doesn't change it to selected ( it remains null ).
add after assignment:
Page1.page1.LV1.Select();
This works for me:
private void LV1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = (sender as ListView).SelectedItem as string;
int index = -1;
for (int i = 0; i < LV2.Items.Count(); i++)
{
if (LV2.Items[i] as string == selected){
index = i;
break;
}
}
// The if becomes obsolete here, it could be replaced by
// if(index >= 0)
if (LV2.Items.ToList().Contains(selected))
{
LV2.SelectedIndex = index;
}
}
There is probably an easier way of getting the index of LV1's SelectedItem in LV2, but it should be enough to get you on the right track.
You can check out the minimal testing app I created that shows that SelectedItem works too.
Method 1 - SelectionMode="Multiple" - both ListViews in sync
You should subscribe the SelectionChanged event on both ListViews - item may not get selected only by click - and there (when selection is changed) you should sync the selection.
private void SyncSelection(object sender, SelectionChangedEventArgs e)
{
ListView listViewToAdd = ReferenceEquals(sender, firstListView) ? secondListView : firstListView;
foreach (var item in e.AddedItems)
{
if (!listViewToAdd.SelectedItems.Contains(item))
{
listViewToAdd.SelectedItems.Add(item);
}
}
foreach (var item in e.RemovedItems)
{
listViewToAdd.SelectedItems.Remove(item);
}
}
Method 2 - SelectionMode="Multiple" - update one after selecting in the other
You should subscribe the SelectionChanged event only on the ListView where items could be selected.
private void SyncSelection(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems)
{
secondListView.SelectedItems.Add(item);
}
foreach (var item in e.RemovedItems)
{
secondListView.SelectedItems.Remove(item);
}
}
Method 3 - SelectionMode="Single"
Subscribe the SelectionChanged event on both if you want to make them be in sync or only on the selectable one if you only want to update the second based on the first.
private void SyncSelection(object sender, SelectionChangedEventArgs e)
{
ListView senderListView = (ListView)sender;
ListView listViewToAdd = ReferenceEquals(sender, firstListView) ? secondListView : firstListView;
listViewToAdd.SelectedItem = senderListView.SelectedItem;
}
You may need to replace var with your interface to make it work.

Selected index combobox winforms

Ok, very dumb question but i haven't really found an answer on internet.
I have multiple comboboxes on a form. The binding of each combobox is on form_load.
When the form loads, the first item is selected on the form. This is obvious, but I don't want this. So i used in the form_load the following code:
private void InvoiceView_Load(object sender, EventArgs e)
{
// Bind list of customers to combobox
CustomerComboBox.DataSource = invoicePresenter.getCustomers();
CustomerComboBox.DisplayMember = "CustomerName";
CustomerComboBox.ValueMember = "CustomerId";
// Bind list of products to combobox
productCombobox.DataSource = invoicePresenter.getProducts();
productCombobox.DisplayMember = "ProductName";
productCombobox.ValueMember = "ProductId";
// Bind list of vat codes to combobox
vatComboBox.DataSource = invoicePresenter.getTaxCodes();
vatComboBox.DisplayMember = "taxCodeShortDescr";
vatComboBox.ValueMember = "taxCodeId";
// Set comboboxes empty
CustomerComboBox.SelectedItem = null;
productCombobox.SelectedItem = null;
vatComboBox.SelectedItem = null;
}
This works. But the textboxes are still giving me the data of the first item ? My guess is because its in the selectedIndexChanged. But i have no clue what to use else.
If I put the combobox.selectedIndex = -1; I face the same issue.
the code:
private void CustomerComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
// Bind the selected customer itemvalues to the texboxes
txtCustomerName.Text = ((tbl_customer)CustomerComboBox.SelectedItem).CustomerName.ToString();
txtAddress.Text = ((tbl_customer)CustomerComboBox.SelectedItem).CustomerAddress.ToString();
txtPostalCode.Text = ((tbl_customer)CustomerComboBox.SelectedItem).CustomerPostalCode.ToString();
txtCity.Text = ((tbl_customer)CustomerComboBox.SelectedItem).CustomerCity.ToString();
txtCountry.Text = ((tbl_customer)CustomerComboBox.SelectedItem).CustomerCountry.ToString();
txtVatNumber.Text = ((tbl_customer)CustomerComboBox.SelectedItem).CustomerCountryCode.ToString() + ((tbl_customer)CustomerComboBox.SelectedItem).CustomerVat.ToString();
}
One would think, because im using selecteditem in the textbox binding, it would be null also. But this is not the case.
Interestingly, seems like you've hit one of the WF data binding quirks. The problem is caused by the fact that the CurrencyManager class which maintains every list data source does not allow setting the Position property to -1 (thus Current to null) when the list count is not zero. Since the ComboBox is synchronizing the SelectedIndex with the CurrencyManager.Position, this effectively prevents having an unselected item.
As a workaround, if the data bound mode of the list portion is not essential for you, replace the line
CustomerComboBox.DataSource = invoicePresenter.getCustomers();
with
foreach (var customer in invoicePresenter.getCustomers())
CustomerComboBox.Items.Add(customer);
Do the same for the other comboboxes that need such behavior.
You can remove the handler for the SelectedIndex_Changed event of combobox, bind data, then add the handler back. like this :
private void InvoiceView_Load(object sender, EventArgs e)
{
this.CustomerComboBox.SelectedIndexChanged -= new EventHandler(CustomerComboBox_SelectedIndexChanged);
this.productCombobox.SelectedIndexChanged -= new EventHandler(productCombobox_SelectedIndexChanged);
this.vatComboBox.SelectedIndexChanged -= new EventHandler(vatComboBox_SelectedIndexChanged);
// Bind list of customers to combobox
CustomerComboBox.DataSource = invoicePresenter.getCustomers();
CustomerComboBox.DisplayMember = "CustomerName";
CustomerComboBox.ValueMember = "CustomerId";
// Bind list of products to combobox
productCombobox.DataSource = invoicePresenter.getProducts();
productCombobox.DisplayMember = "ProductName";
productCombobox.ValueMember = "ProductId";
// Bind list of vat codes to combobox
vatComboBox.DataSource = invoicePresenter.getTaxCodes();
vatComboBox.DisplayMember = "taxCodeShortDescr";
vatComboBox.ValueMember = "taxCodeId";
this.CustomerComboBox.SelectedIndexChanged += new EventHandler(CustomerComboBox_SelectedIndexChanged);
this.productCombobox.SelectedIndexChanged += new EventHandler(productCombobox_SelectedIndexChanged);
this.vatComboBox.SelectedIndexChanged += new EventHandler(vatComboBox_SelectedIndexChanged);
}

Autocomplete for DatagridView ComboBox

I have a data grid view which is used to bind values.
I have a ComboBox inside this DatagridView; I want to implement an auto complete property in this ComboBox. It should not only search for the first letter but the whole item...
This can be done by
Grabbing the ComboBox
Manipulating its Items
Let's assume you have only one ComboBoxColumn; then you can grab an instance of the current one like this:
ComboBox editCombo = null; // class level variable
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
editCombo = e.Control as ComboBox;
if (editCombo != null)
{
// here we can set its style..
editCombo.DropDownStyle = ComboBoxStyle.DropDown;
editCombo.AutoCompleteMode = AutoCompleteMode.Suggest;
// sigh..:
editCombo.TextChanged -= editCombo_TextChanged;
editCombo.TextChanged += editCombo_TextChanged;
}
}
Let's assume you have the valid list of values in a List<string>
List<string>() allChoices = new List<string>();
Then we can adapt the Items to be shown in the TextChanged event:
void editCombo_TextChanged(object sender, EventArgs e)
{
List<String> items = allChoices.Select(x=>x)
.Where(x=>x.Contains(editCombo.Text)).ToList();
if (items.Count > 0)
{
editCombo.Items.Clear();
editCombo.Items.AddRange(items.ToArray());
}
editCombo.Select(editCombo.Text.Length, 0); //clear the selection
}
Note that this x=>x.Contains(editCombo.Text) searches for items that contain the full text entered. I hope that is what you mean; searching for items that are identical to the entered text makes no sense, as then you do not need to AutoComplete them anyway..

Categories