I have a listview that pulls in a bunch of information from a database. There are known values assigned to these that would be useful to sort by, but I only seem to be able to sort by one item at a time. Here is that working code:
private bool prodAreaFilter(object item)
{
return (item as PCdata).ProductionArea.IndexOf(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private void prodArea_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = prodAreaFilter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString());
}
private bool typeFilter(object item)
{
return (item as PCdata).Type.IndexOf(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private void type_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewType = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewType.Filter = typeFilter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
Instead of filtering together, they overwrite the other one. I think this is caused by them each having their own filtering logic, so I tried to combine it all into one filter:
private bool Filter(object item)
{
return (item as PCdata).Type.IndexOf(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
(item as PCdata).ProductionArea.IndexOf(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private void prodArea_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = Filter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString());
}
//private bool typeFilter(object item)
//{
// return (item as PCdata).Type.IndexOf(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
//}
private void type_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewType = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewType.Filter = Filter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
This solution worked for me, but it was necessary to add logic to handle one combobox or the other being empty and switching the criteria accordingly.
private void prodArea_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(typeComboBox.Text)) //If other combo is empty, only use one criteria for box that will have value
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).ProductionArea.Contains(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString());
}
else
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).ProductionArea.Contains(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString()) &&
((PCdata)obj).Type.Contains(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
}
private void type_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(prodAreaComboBox.Text))
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).Type.Contains(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
else
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).ProductionArea.Contains(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString()) &&
((PCdata)obj).Type.Contains(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
}
One example of filtering. Maybe this will help you.
If you have a button for filtering by "Name" and "Age"
private void btnFilterName_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj => ((MyDataModel)obj).Name.Contains(txtFilterName.Text);
}
private void btnFilterAge_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj => ((MyDataModel)obj).Age == Convert.ToInt32(txtFilterAge.Text);
}
To filter by multiple columns, you can use the "&&" operator to combine multiple filter conditions in the same event.
private void btnFilterNameAge_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj => ((MyDataModel)obj).Name.Contains(txtFilterName.Text) && ((MyDataModel)obj).Age == Convert.ToInt32(txtFilterAge.Text);
}
To clear the filter, set the filter property to null in the button click event.
private void btnClearFilter_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = null;
}
You can use if statements to apply the filter criteria
private void ApplyFilter()
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj =>
{
if (ComboBoxName.SelectedItem != null && ComboBoxAge.SelectedItem != null)
{
return ((MyDataModel)obj).Name.Contains(ComboBoxName.SelectedItem.ToString()) && ((MyDataModel)obj).Age == Convert.ToInt32(ComboBoxAge.SelectedItem);
}
else if (ComboBoxName.SelectedItem != null)
{
return ((MyDataModel)obj).Name.Contains(ComboBoxName.SelectedItem.ToString());
}
else if (ComboBoxAge.SelectedItem != null)
{
return ((MyDataModel)obj).Age == Convert.ToInt32(ComboBoxAge.SelectedItem);
}
else
{
return true;
}
};
}
You can then call the ApplyFilter method in the SelectionChanged event of the comboboxes, so that the filter will be applied whenever a new item is selected. Call the ApplyFilter method if the selected item is not null, to prevent errors when the combobox is empty.
private void ComboBoxName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ComboBoxName.SelectedItem != null)
{
ApplyFilter();
}
}
private void ComboBoxAge_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ComboBoxAge.SelectedItem != null)
{
ApplyFilter();
}
}
If you want to clear the filter when both comboboxes are empty, you need to add a ClearFilter method and call it when both comboboxes are empty.
private void ClearFilter()
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = null;
}
if (ComboBoxName.SelectedItem == null && ComboBoxAge.SelectedItem == null)
{
ClearFilter();
}
A filter is a predicate which is applied for every item in your collection.
If it returns true, you get to see that item. If it returns false then you do not.
That filtering logic is run every time you add or remove an item or force refresh() on the collection. Unless you make it use live filtering, in which case when anything changes in the collection the whole thing is checked again.
All of which means that collectionview filtering is quite an expensive thing to use. You should carefully consider just how many items you have in your base collection. If there are a million rows and you have a user insert or delete rows out that then each time the whole collection is traversed and checked.
I wrote this article on filtering some years back:
https://social.technet.microsoft.com/wiki/contents/articles/26673.wpf-collectionview-tips.aspx
Microsoft have ditched the technet gallery the source was on.
If you wanted flexible filtering based on more than one property then you could use the technique that demonstrates where the predicate itself checks a list of predicates. Want two columns to match, you add 2 predicates to the list. Want three, you add three. Below, criteria is a List. Trueforall returns true if they're all true.
private bool dynamic_Filter(object item)
{
Person p = item as Person;
bool isIn = true;
if (criteria.Count() == 0)
return isIn;
isIn = criteria.TrueForAll(x => x(p));
return isIn;
}
In that sample you can pick from any or all of several criteria. The code that adds the predicates to that list looks like:
criteria.Clear();
if (yearsChosen > 0)
{
criteria.Add(new Predicate<Person>(x => yearsChosen < (
(_zeroDay + (_now - x.BirthDate)).Year - 1)
));
}
if (letterChosen != "Any")
{
criteria.Add(new Predicate<Person>(x => x.LastName.StartsWith(letterChosen)));
}
if (genderChosen != "Any")
{
criteria.Add(new Predicate<Person>(x => x.Gender.Equals(genderChosen.Substring(0, 1))));
}
PeopleView.Filter = dynamic_Filter;
RaisePropertyChanged("PeopleView");
Related
While my UI is displayed, data is being passed in the back end and added to a List<string> that I would in turn like to display on my UI.
I've seen several examples using background workers however I don't have access to the actual component due to how I layout my User Controls and programmatically build them.
Question: How can I run this method repeatedly behind my UI without locking up my UI in a loop?
public void UpdatePanel()
{
foreach (var item in list)
{
AddMethod(item);
}
}
Instead of using a loop or time intervals to monitor a list, as an option when possible, you can use a BindingList<T> or ObservableCollection<T> and receive notification when list changes.
Then you can update user interface in the event handler which you attaced to ListChanged event ofBindingList<T> or CollectionChanged event of ObservableCOllection<T>.
Example
Here is an example based on ObservableCollection<string>.
ObservableCollection<string> list;
private void Form1_Load(object sender, EventArgs e)
{
list = new ObservableCollection<string>();
list.CollectionChanged += list_CollectionChanged;
list.Add("Item 1");
list.Add("Item 2");
list.RemoveAt(0);
list[0] = "New Item";
}
void list_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var items = string.Join(",", e.NewItems.Cast<String>());
MessageBox.Show(string.Format("'{0}' Added", items));
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
var items = string.Join(",", e.OldItems.Cast<String>());
MessageBox.Show(string.Format("'{0}' Removed", items));
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
var oldItems = string.Join(",", e.OldItems.Cast<String>());
var newItems = string.Join(",", e.NewItems.Cast<String>());
MessageBox.Show(string.Format("'{0}' replaced by '{1}'", oldItems, newItems));
}
else
{
MessageBox.Show("Reset or Move");
}
}
You can use Task, Async and await, where is some code which insert an element in a listbox each second without blocking UI.
In your case, you have to return the data from backend asynchronously.
public async void LoadItemsAsync()
{
for (int i = 0; i < 10; i++)
listBox1.Items.Add(await GetItem());
}
public Task<string> GetItem()
{
return Task<string>.Factory.StartNew(() =>
{
Thread.Sleep(1000);
return "Item";
});
}
I am really confused on thinking which control to use for my purpose.
I am having list of items say item1 to item10. User can select 4 or 5 items in any order.
Now user selected items has to be separated in same order.
For example, if the user selected the items in following order, item4, item8, item3 and item2.
I want it in the same order. item4,item8,item3,item2.
How do I achieve this in winforms control?
It is not a very nice solution but I wrote it as you asked.
Set the SelectionMode of your ListBox to MultiExtended or MultiSimple as you need.
Then write this code in SelectedIndexChanged event of your ListBox:
List<string> orderedSelection = new List<string>();
bool flag = true;
private void listBox3_SelectedIndexChanged(object sender, EventArgs e)
{
if (flag)
{
flag = false;
var list1 = listBox3.SelectedItems.Cast<string>().ToList();
if (listBox3.SelectedItems.Count > orderedSelection.Count)
{
orderedSelection.Add(list1.Except(orderedSelection).First());
}
else if (listBox3.SelectedItems.Count < orderedSelection.Count)
{
orderedSelection.Remove(orderedSelection.Except(list1).First());
}
var list2 = listBox3.Items.Cast<string>().Except(list1).ToList();
listBox3.Items.Clear();
for (int i = 0; i < list1.Count; i++)
{
listBox3.Items.Add(list1[i]);
listBox3.SelectedIndex = i;
}
foreach (string s in list2)
{
listBox3.Items.Add(s);
}
flag = true;
}
}
When user selects an item, It comes to first of the list and the rest of the items comes next.
Also, there is an alternative way. You can use a CheckedListBox with two extra button for moving selected items up and down. So user can change the order of selected items.
This solution uses CheckListBox's ItemCheck event along with a private List to keep track of the click items order.
protected List<string> clickOrderList = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
// Populate the checked ListBox
this.checkedListBox1.Items.Add("Row1");
this.checkedListBox1.Items.Add("Row2");
this.checkedListBox1.Items.Add("Row3");
this.checkedListBox1.Items.Add("Row4");
}
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (sender != null && e != null)
{
// Get the checkListBox selected time and it's CheckState
CheckedListBox checkListBox = (CheckedListBox)sender;
string selectedItem = checkListBox.SelectedItem.ToString();
// If curent value was checked, then remove from list
if (e.CurrentValue == CheckState.Checked &&
clickOrderList.Contains(selectedItem))
{
clickOrderList.Remove(selectedItem);
}
// else if new value is checked, then add to list
else if (e.NewValue == CheckState.Checked &&
!clickOrderList.Contains(selectedItem))
{
clickOrderList.Insert(0, selectedItem);
}
}
}
private void ShowClickOrderButton_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (string s in clickOrderList)
{
sb.AppendLine(s);
}
MessageBox.Show(sb.ToString());
}
I have WPF application where in the Ui I have a header check box and when it is checked all the body checkboxes are checked. When any checkbox is checked, Im doing some work that I need by calling the Background worker which works perfect. If I select any checkboxes individually without checking header then Background worker works fine.
Problem is when I check the header all the body checkboxes are checked and in this case the Background worker 'Run Worker completed' event is being executed even before 'do work' is executed which is breaking my code. Please help. Here is my code. RunJob is from command for button in front end.
public void RunJob(object obj)
{
obforSave = obj;
workerforRun = new BackgroundWorker();
workerforRun.WorkerReportsProgress = true;
workerforRun.DoWork += workerforRun_DoWork;
workerforRun.ProgressChanged += workerforRun_ProgressChanged;
workerforRun.RunWorkerCompleted += workerforRun_RunWorkerCompleted;
if (!workerforRun.IsBusy)
{
workerforRun.RunWorkerAsync();
}
}
//Special case header is checked Runwroker completed is executing before do work.
void workerforRun_DoWork(object sender, DoWorkEventArgs e)
{
IsValidationsComplete = false;
int count = 0;
//var obj = e.Argument;
if (IsRunJobAlreadyExecuted == false)
{
ResultExtract = JobEntities.Any(s => s.ExtractIsSelected != null && (bool)s.ExtractIsSelected);
var resultTransform = JobEntities.Any(x => x.TransformIsSelected != null && (bool)x.TransformIsSelected);
var resultLoad = JobEntities.Any(b => b.LoadIsSelected != null && (bool)b.LoadIsSelected);
//Check if any of the entities are either Extracted, Transformed or Loaded.
if (ResultExtract || resultTransform || resultLoad)
{
SaveJobConfigurationChanges(obforSave);
var jobEngine = new JobEngine();
var jobId = JobEntities[0].JobId;
jobEngine.ProcessJob(jobId);
MessageBox.Show("Job Execution Complete", "Run Job");
AllResults = GetJobConfigurationResults();
foreach (var item in AllResults)
{
if (item.ExtractIsSelected == true && item.ExtractStatus == "Completed Successfully")
{
count += Convert.ToInt32(item.ExtractRowsSelected);
}
if (item.TransformIsSelected == true && item.TransformStatus == "Completed Successfully")
{
count += Convert.ToInt32(item.TransformRowsSelected);
}
if (item.LoadIsSelected == true && item.LoadStatus == "Completed Successfully")
{
count += Convert.ToInt32(item.LoadRowsSelected);
}
}
workerforRun.ReportProgress(count);
//MessageBox.Show(count.ToString());
}
else
{
//When No Entity is Selected and the Run Button is pressed.
MessageBox.Show("Select an Entity First");
}
}
IsRunJobAlreadyExecuted = false;
}
void workerforRun_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress = e.ProgressPercentage;
}
void workerforRun_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
/*When Header is checked and when the Run is clicked, you want to Uncheck the header back after completion.
This property is set in the CS file.*/
AllResults = GetJobConfigurationResults();
foreach (var item in AllResults)
{
item.ExtractIsSelected = false;
item.TransformIsSelected = false;
item.LoadIsSelected = false;
}
SaveTaskSelection();
JobEntitiesCollectionViewSource.Source = AllResults;
JobEntitiesCollectionViewSource.View.Refresh();
ExecuteFilteredEntitiesStoredProcedure();
IsValidateEnabled = AllResults.Any(p => p.ExtractStatus == "Completed Successfully");
}), DispatcherPriority.Background, null);
MessageBox.Show(progress.ToString());
IsValidationsComplete = true;
}
CS file:
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
// If there is a child found and if the child is of the T type.
//Dont remove null check . If no check i
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private IEnumerable<CheckBox> GetAllCheckBoxs()
{
var allCheckBoxes = FindVisualChildren<CheckBox>(UserControlJob);
return allCheckBoxes;
}
private void ChkHeaderExtract_OnChecked(object sender, RoutedEventArgs e)
{
var extractCheckBoxes = GetAllCheckBoxs();
var i = 0;
foreach (var chk in extractCheckBoxes)
{
if (i%3 == 0) // Since I have many sets of checkboxes and I want only the first set of checkboxes checked when header is clicked Im doing this.
{
chk.IsChecked = true;
}
i++;
}
}
Is there some event I can use to tell when the SelectedIndices property changes for a listbox? I want to deselect items in a listbox based on a certain property value of the item. I've hooked up an event that works for when a SelectedIndex is changed, but not sure how to do it for when the SelectedIndices property changes for multiselection.
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Curve curItem = (Curve)listBox1.SelectedItem;
int index = listBox1.Items.IndexOf(curItem);
if (curItem.newName == null)
{
listBox1.SetSelected(index, false);
}
}
You could use ListBox.SelectedItems and LINQ to find all Curves with newName==null to deselect them:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var nullNameCurves = listBox1.SelectedItems
.Cast<Curve>()
.Where(c => c.newName == null)
.ToList();
listBox1.SelectedIndexChanged -= listBox1_SelectedIndexChanged;
foreach (Curve curve in nullNameCurves)
listBox1.SetSelected(listBox1.Items.IndexOf(curve), false);
listBox1.SelectedIndexChanged += listBox1_SelectedIndexChanged;
}
According to MSDN, this event will be fired every time the selection changes:
If the SelectionMode property is set to SelectionMode.MultiSimple or SelectionMode.MultiExtended, any change to the SelectedIndices collection, including removing an item from the selection, will raise this event.
So basically, you can use it the same way as using it with single selection.
Sample:
For example if you want to deselect all items with null as newName:
foreach (var item in listBox1.SelectedItems)
{
if ((item as Curve).newName == null)
{
int index = listBox1.SelectedItems.IndexOf(item);
listBox1.SetSelected(index, false);
}
}
(I'm not sure if you can deselect items inside a foreach loop since it changes the SelectedItems object itself. If it does not work, you can still make a temporary list of those items and deselect them after the loop.)
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Curve curItem = null;
for (int i = 0; i < listBox1.SelectedItems.Count; i++)
{
curItem = (Curve)listBox1.SelectedItems[i];
if (curItem != null)
{
int index = listBox1.Items.IndexOf(curItem);
if (curItem.newName == null)
{
listBox1.SetSelected(index, false);
}
}
}
}
I'm trying to understand what's happening here. I have a CheckedListBox which contains some ticked and some un-ticked items. I'm trying to find a way of determining the delta in the selection of controls. I've tried some cumbersome like this - but only works part of the time, I'm sure there's a more elegant solution. A maybe related problem is the myCheckBox_ItemCheck event fires on form load - before I have a chance to perform an ItemCheck. Here's what I have so far:
void clbProgs_ItemCheck(object sender, ItemCheckEventArgs e)
{
// i know its awful
System.Windows.Forms.CheckedListBox cb = (System.Windows.Forms.CheckedListBox)sender;
string sCurrent = e.CurrentValue.ToString();
int sIndex = e.Index;
AbstractLink lk = (AbstractLink)cb.Items[sIndex];
List<ILink> _links = clbProgs.DataSource as List<ILink>;
foreach (AbstractLink lkCurrent in _links)
{
if (!lkCurrent.IsActive)
{
if (!_groupValues.ContainsKey(lkCurrent.Linkid))
{
_groupValues.Add(lkCurrent.Linkid, lkCurrent);
}
}
}
if (_groupValues.ContainsKey(lk.Linkid))
{
AbstractLink lkDirty = (AbstractLink)lk.Clone();
CheckState newValue = (CheckState)e.NewValue;
if (newValue == CheckState.Checked)
{
lkDirty.IsActive = true;
}
else if (newValue == CheckState.Unchecked)
{
lkDirty.IsActive = false;
}
if (_dirtyGroups.ContainsKey(lk.Linkid))
{
_dirtyGroups[lk.Linkid] = lkDirty;
}
else
{
CheckState oldValue = (CheckState)e.NewValue;
if (oldValue == CheckState.Checked)
{
lkDirty.IsActive = true;
}
else if (oldValue == CheckState.Unchecked)
{
lkDirty.IsActive = false;
}
_dirtyGroups.Add(lk.Linkid, lk);
}
}
else
{
if (!lk.IsActive)
{
_dirtyGroups.Add(lk.Linkid, lk);
}
else
{
_groupValues.Add(lk.Linkid, lk);
}
}
}
Then onclick of a save button - I check whats changed before sending to database:
private void btSave_Click(object sender, EventArgs e)
{
List<AbstractLink> originalList = new List<AbstractLink>(_groupValues.Values);
List<AbstractLink> changedList = new List<AbstractLink>(_dirtyGroups.Values);
IEnumerable<AbstractLink> dupes = originalList.ToArray<AbstractLink>().Intersect(changedList.ToArray<AbstractLink>());
foreach (ILink t in dupes)
{
MessageBox.Show("Changed");
}
if (dupes.Count() == 0)
{
MessageBox.Show("No Change");
}
}
For further info. The definition of type AbstractLink uses:
public bool Equals(ILink other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return IsActive.Equals(other.IsActive) && Linkid.Equals(other.Linkid);
}
There's little point that I see to do this in the ItemCheck event. Just calculate the delta when you save. Cuts out a bunch of code and trouble with spurious events.