Extending DataList to accept a new InsertCommand from LinkButtons - c#

I'm trying to subclass DataList to accept a new Command from embedded LinkButtons. Here's my abstract class:
public abstract class BaseFieldGroup : DataList
{
public const string InsertCommandName = "Insert";
public event DataListCommandEventHandler InsertCommand
{
add
{
base.Events.AddHandler(EventInsertCommand, value);
}
remove
{
base.Events.RemoveHandler(EventInsertCommand, value);
}
}
private static readonly object EventInsertCommand;
static BaseFieldGroup()
{
EventInsertCommand = new object();
}
protected virtual void OnInsertCommand(DataListCommandEventArgs e)
{
DataListCommandEventHandler handler = (DataListCommandEventHandler)base.Events[EventInsertCommand];
if (handler != null)
{
handler(this, e);
}
}
}
it seems right, but the Event isn't being caught; I'm not sure why. However, I also noticed that LinkButton sends the even up with a RaiseBubble, I don't know if that's an issue or not. Anyone have thoughts?
Oh, this is 2.0

Aha! Found and fixed; I have to override the OnBubbleEvent of the DataList to accomodate (call) the new command. See below:
public abstract class BaseFieldGroup : DataList
{
...
protected override bool OnBubbleEvent(object source, EventArgs e)
{
bool flag = false;
if (e is DataListCommandEventArgs)
{
DataListCommandEventArgs args = (DataListCommandEventArgs)e;
this.OnItemCommand(args);
flag = true;
switch (args.CommandName)
{
case SelectCommandName:
this.SelectedIndex = args.Item.ItemIndex;
this.OnSelectedIndexChanged(EventArgs.Empty);
return flag;
case EditCommandName:
this.OnEditCommand(args);
return flag;
case DeleteCommandName:
this.OnDeleteCommand(args);
return flag;
case UpdateCommandName:
this.OnUpdateCommand(args);
return flag;
case CancelCommandName:
this.OnCancelCommand(args);
return flag; //??
case InsertCommandName:
this.OnInsertCommand(args);
return flag;
}
}
return flag;
}

Related

Why is my property notification not triggering?

I have an hierarchy of classes. The pertinent bits are included below. I was expecting that when FilterCritetionInteger.CriterionValue is changed, via WPF binding, that I would get a notification in my FilterCriterionCollection. But, while the FilterCriterionInteger.CriterionValue setter is called, the FilterCriterionCollection.Criterion_PropertyChanged method is never called.
Apparently I am misunderstanding something, or have something connected incorrectly. I'm just not finding it. Why is my notification not happening? (Please let me know if there is more context needed, anywhere.)
ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler pcHandler = this.PropertyChanged;
if (pcHandler != null)
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
pcHandler(this, args);
}
}
}
FilterCriterionBase
public abstract class FilterCriterionBase : ViewModelBase
{
public abstract bool IsValid { get; }
}
FilterCriterionInteger
public class FilterCriterionInteger : FilterCriterionBase
{
private string _criterionValue = "0";
public string CriterionValue
{
get { return _criterionValue; }
set
{
_criterionValue = value;
OnPropertyChanged("IsValid");
}
}
public int? MaxValue { get; private set; } = null;
public override bool IsValid
{
get
{
if (int.TryParse(_criterionValue, out int i))
{
if (MaxValue.HasValue && (i > MaxValue)) return false;
}
else
{
return false;
}
return true;
}
}
}
FilterCriterionCollection
public class FilterCriteriaCollection : ViewModelBase
{
public FilterCriteriaCollection()
{
Criteria.CollectionChanged += Criteria_CollectionChanged;
}
~FilterCriteriaCollection()
{
Criteria.CollectionChanged -= Criteria_CollectionChanged;
}
public ObservableCollection<FilterCriterionBase> Criteria { get; private set; } = new ObservableCollection<FilterCriterionBase>();
private void Criteria_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (FilterCriterionBase fb in e.NewItems)
{
fb.PropertyChanged += Criterion_PropertyChanged;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (FilterCriterionBase fb in e.NewItems)
{
fb.PropertyChanged -= Criterion_PropertyChanged;
}
break;
}
}
private bool _isValid;
public bool IsValid
{
get { return _isValid; }
private set { _isValid = value; OnPropertyChanged("IsValid"); }
}
private void Criterion_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
using (FilterCriterionBase criterion = sender as FilterCriterionBase)
{
switch (e.PropertyName)
{
case "IsValid":
{
bool isvalid = true;
foreach(FilterCriterionBase fcb in Criteria)
{
isvalid = fcb.IsValid;
if (!isvalid) break;
}
IsValid = isvalid;
break;
}
}
}
}
}
So, as it turns out, I am a moron. The code, above, does need a little tweaking but it is, in general, sound. My primary issue was that I had some leftover code, in my testing sandbox, from testing some JSON serialization/deserialization. I realized, after much head banging, that I was sending deserialized objects that had lost their event notification context and, therefore, were no longer in the loop.
Would it be proper protocol to just delete this post, since it doesn't really require an answer beyond double checking your inputs...?

Using SortableBindingList<T> - DataGridView doesn't automatically sort on changes

I'm building a Windows Forms Application that displays a custom class Record objects and sorts them by how long they've been in my SortableBindingList<Record> record_list. When I start my program, I have some "dummy" records loaded into this list already for the sake of testing.
The SortableBindingList<T> has been taken from here.
public partial class Form1 : Form
{
public SortableBindingList<Record> record_list = new SortableBindingList<Record> { };
public static DataGridViewCellStyle style = new DataGridViewCellStyle();
public Form1()
{
InitializeComponent();
dataGridView.DataSource = record_list;
FillData(); //Temporary function to insert dummy data for demo.
dataGridView.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.cell_formatting);
this.Controls.Add(dataGridView);
this.dataGridView.RowHeadersVisible = false;
this.dataGridView.Sort(this.dataGridView.Columns["UserName"], ListSortDirection.Ascending);
start_timer();
}
Result before "new" data is added (note: this was alphabetized automatically, specifically entered into the list out of alphabetical order):
Result after data is added:
Finally, result after I click the "UserName" header:
So, must I force a sort every time my DataSource is updated? If that's the case, how do I call a sort in such a manner?
Thank you for your assistance in advance!
You need to apply sort when the list changes.
The SortableBindingList<T> needs some changes to keep the the list sorted when some changes made in list. Here is the full code with changes which I made.
Pay attention The OnListChanged method of BindingList will be called automatically after adding and removing items. But if you need to OnListChanged also runs after changing properties of items, you should implement INotifyPropertyChanged for your model class.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
public class SortableBindingList<T> : BindingList<T>
{
private bool isSortedValue;
ListSortDirection sortDirectionValue;
PropertyDescriptor sortPropertyValue;
public SortableBindingList() : base() { }
public SortableBindingList(IList<T> list) : base(list) { }
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
Type interfaceType = prop.PropertyType.GetInterface("IComparable");
if (interfaceType == null && prop.PropertyType.IsValueType)
{
Type underlyingType = Nullable.GetUnderlyingType(prop.PropertyType);
if (underlyingType != null)
{
interfaceType = underlyingType.GetInterface("IComparable");
}
}
if (interfaceType != null)
{
sortPropertyValue = prop;
sortDirectionValue = direction;
IEnumerable<T> query = base.Items;
if (direction == ListSortDirection.Ascending)
query = query.OrderBy(i => prop.GetValue(i));
else
query = query.OrderByDescending(i => prop.GetValue(i));
int newIndex = 0;
foreach (object item in query)
{
this.Items[newIndex] = (T)item;
newIndex++;
}
isSortedValue = true;
sorting = true;
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
sorting = false;
}
else
{
throw new NotSupportedException("Cannot sort by " + prop.Name +
". This" + prop.PropertyType.ToString() +
" does not implement IComparable");
}
}
bool sorting = false;
protected override PropertyDescriptor SortPropertyCore
{
get { return sortPropertyValue; }
}
protected override ListSortDirection SortDirectionCore
{
get { return sortDirectionValue; }
}
protected override bool SupportsSortingCore
{
get { return true; }
}
protected override bool IsSortedCore
{
get { return isSortedValue; }
}
protected override void RemoveSortCore()
{
isSortedValue = false;
sortPropertyValue = null;
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (!sorting && sortPropertyValue != null)
ApplySortCore(sortPropertyValue, sortDirectionValue);
else
base.OnListChanged(e);
}
}

WPF/C#5.0 Publish/Subscribe to an array of static fields?

Using C# 5.0, I'm creating a publish/subscribe relationship on a static field, so that I can access it from multiple pages. In the host window, I have
public enum PLCStates
{
Good,
Bad,
Disabled
};
public static class PLCSafeStates
{
public static event EventHandler testStates1Changed;
private static PLCStates _testStates1;
public static PLCStates testStates1
{
get { return _testStates1; }
set
{
if (value != _testStates1)
{
_testStates1 = value;
if (testStates1Changed != null)
testStates1Changed(null, EventArgs.Empty);
}
}
}
}
And then in the pages hosted by the window, I have things like:
public FB1()
{
InitializeComponent();
SafteyFaults.PLCSafeStates.testStates1Changed += PLCSafeStates_testStates1Changed;
}
private void PLCSafeStates_testStates1Changed(object sender, EventArgs e)
{
var test2 = SafteyFaults.PLCSafeStates.testStates1;
if (test2 == SafteyFaults.PLCStates.Bad)
{
VisualStateManager.GoToState(btnFB, "PLCBad", true);
}
if (test2 == SafteyFaults.PLCStates.Good)
{
VisualStateManager.GoToState(btnFB, "PLCGood", false);
}
}
private void btnFB_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(btnOut1, "PLCBad",false);
if (SafteyFaults.PLCSafeStates.testStates1 == SafteyFaults.PLCStates.Good)
SafteyFaults.PLCSafeStates.testStates1=SafteyFaults.PLCStates.Bad;
else
SafteyFaults.PLCSafeStates.testStates1 = SafteyFaults.PLCStates.Good;
}
(right now, I don't have any business logic wired up yet- once I get this working, I'll link to actual data).
Anyhow, all of this works to create a single field I can subscribe to, modify, etc. But I need 20+ of these fields. I want to make 'testStates1' an array, but I've not been able to get it to work.
If I make the following edits to the code shown so far, it compiles and runs, but throws an error when I actually try to access the field (e.g. click on the button to change it):
//window
public static class PLCSafeStates
{
public static event EventHandler testStates1Changed;
private static PLCStates[] _testStates1;
public static PLCStates[] testStates1
{
get { return _testStates1; }
set
{
if (value != _testStates1)
{
_testStates1 = value;
if (testStates1Changed != null)
testStates1Changed(null, EventArgs.Empty);
}
}
}
}
//page
public FB1()
{
InitializeComponent();
SafteyFaults.PLCSafeStates.testStates1Changed += PLCSafeStates_testStates1Changed;
}
private void PLCSafeStates_testStates1Changed(object sender, EventArgs e)
{
var test2 = SafteyFaults.PLCSafeStates.testStates1[0];
if (test2 == SafteyFaults.PLCStates.Bad)
{
VisualStateManager.GoToState(btnFB, "PLCBad", true);
}
if (test2 == SafteyFaults.PLCStates.Good)
{
VisualStateManager.GoToState(btnFB, "PLCGood", false);
}
}
private void btnFB_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(btnOut1, "PLCBad",false);
if (SafteyFaults.PLCSafeStates.testStates1[0] == SafteyFaults.PLCStates.Good)
SafteyFaults.PLCSafeStates.testStates1[0]=SafteyFaults.PLCStates.Bad;
else
SafteyFaults.PLCSafeStates.testStates1[0] = SafteyFaults.PLCStates.Good;
}

Event when boolean is true

I'm programming in c#(WPF). I have some Boolean variables in my class like isConnected or isBusy. I want to define a event and event handler for them to when my boolean variables are changed, I run a method.
I searched and find some things but I can't understand them.
could you help me to write it?
Update 1:
finally I write it, but I get StackOverFlowExeception which may be caused by recursion.
what is wrong?
public event EventHandler IsConnectedChanged;
public bool IsConnected
{
get { return IsConnected; }
set
{
IsConnected = value;
CheckAndCallHandlers();
}
}
private void CheckAndCallHandlers()
{
EventHandler handler = IsConnectedChanged;
if (IsConnected)
handler(this, EventArgs.Empty);
}
Wrap the variable in Properties, and then in the setter for the properties you can call a method that checks to see if both are true. When that condition is met, you can then do the extra work:
public class SomeClass
{
private bool _isConnected;
private bool _isBusy;
public event EventHandler SomeCustomEvent;
public bool IsConnected
{
get { return _isConnected; }
set
{
_isConnected = value;
CheckAndCallHandlers();
}
}
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
CheckAndCallHandlers();
}
}
private void CheckAndCallHandlers()
{
var handler = SomeCustomEvent;
if(IsConnected && IsBusy && handler != null)
handler(this, EventArgs.Empty);
}
}
make it a property
bool _isConnected;
bool isConnected
{
get { return _isConnected; }
set {
if (value != _isConnected) //it's changing!
{
doSomething();
}
_isConnected = value; //Could do this inside the if but I prefer it outside because some types care about assignment even with the same value.
}
}

SortableBindingList, Index was out of range Error, how to make it Thread-Safe?

using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace MyApplication
{
public class SortableBindingList<T> : BindingList<T>
{
private static object syncLock = new object();
private readonly Dictionary<Type, PropertyComparer<T>> comparers;
private bool isSorted;
private ListSortDirection listSortDirection;
private PropertyDescriptor propertyDescriptor;
private System.ComponentModel.ISynchronizeInvoke _SyncObject;
private System.Action<System.ComponentModel.ListChangedEventArgs> _FireEventAction;
public SortableBindingList()
: base(new List<T>())
{
this.comparers = new Dictionary<Type, PropertyComparer<T>>();
}
public SortableBindingList(IEnumerable<T> enumeration, System.ComponentModel.ISynchronizeInvoke syncObject)
: base(new List<T>(enumeration))
{
_SyncObject = syncObject;
_FireEventAction = FireEvent;
this.comparers = new Dictionary<Type, PropertyComparer<T>>();
}
protected override bool SupportsSortingCore
{
get { return true; }
}
protected override bool IsSortedCore
{
get { return this.isSorted; }
}
protected override PropertyDescriptor SortPropertyCore
{
get { return this.propertyDescriptor; }
}
protected override ListSortDirection SortDirectionCore
{
get { return this.listSortDirection; }
}
protected override bool SupportsSearchingCore
{
get { return true; }
}
protected override void InsertItem(int index, T item)
{
lock (syncLock)
{
base.InsertItem(index, item);
}
}
protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
{
List<T> itemsList = (List<T>)this.Items;
Type propertyType = property.PropertyType;
PropertyComparer<T> comparer;
if (!this.comparers.TryGetValue(propertyType, out comparer))
{
comparer = new PropertyComparer<T>(property, direction);
this.comparers.Add(propertyType, comparer);
}
comparer.SetPropertyAndDirection(property, direction);
itemsList.Sort(comparer);
this.propertyDescriptor = property;
this.listSortDirection = direction;
this.isSorted = true;
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
protected override void RemoveSortCore()
{
this.isSorted = false;
this.propertyDescriptor = base.SortPropertyCore;
this.listSortDirection = base.SortDirectionCore;
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
protected override int FindCore(PropertyDescriptor property, object key)
{
int count = this.Count;
for (int i = 0; i < count; ++i)
{
T element = this[i];
if (property.GetValue(element).Equals(key))
{
return i;
}
}
return -1;
}
protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs args)
{
lock (syncLock)
{
if (_SyncObject == null)
{
FireEvent(args);
}
else
{
_SyncObject.Invoke(_FireEventAction, new object[] { args });
}
}
}
private void FireEvent(System.ComponentModel.ListChangedEventArgs args)
{
base.OnListChanged(args);
}
}
}
I am getting following error:
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
The SortableBindingList is bound to DataGridView, Virtual mode
Multiple threads fires event which adds data to SortableBindingList
private void ProxyScraper_OnProxyFound(Proxy Proxy, int Count)
{
ProxyProcessedCount.Text = Count.ToString();
_ProxyList.Add(Proxy);
}
I have tried to lock the SortableBindingList but still getting error, searched a lot but unable to find a solution.
Ultimately I suspect it is a false hope to make a truly thread-safe binding list, as there will be cases where multiple operations are performed - regardless of whether that is a "check the Count then iterate rows to Count-1", or "enumerate with foreach" - and there is no easy way to lock over the duration of those, since the calling code is outside of your control.
Even for a half-working version, you'd need to add your syncLock code to every access, via overriding all the available methods - however, I can't see a virtual method for the get on this[index], which might render that futile - it is only synchronized if all callers agree to use the lock.
Ultimately, I suspect that trying to use threading with tight UI coupling is fairly doomed. IMO, you might have more success separating the two things, and having the UI worry about handling an event and calling .Invoke(...) to update the binding on the UI thread.

Categories

Resources