He everybody,
I'm trying to setup a project management class.
In order to see if somthing in the data changed i want to implement events on the lower level of the programming structure. I have some Classes extending the ProjectComponent Class. The base class has an event and event throwing methode, which the childcomponents can use.
Now I have a couple of custom list (nameley eList) in the project object.
Because all the child component have a common parent, ProjectComponent, i would like my custom list object (eList) to subscribe to the event when an object is added and unsubscribe when removed.
However when trying to prog this, i received the following error:
'ProjectComponent' does not contain a
definition for 'itemChanged' and no
extension method 'itemChanged'
accepting a first argument of type
'ProjectComponent'
Which is kind of wierd seeing as the class clearly has that public field.
Here is a the code:
public class ProjectComponent
{
public event ItemChanged itemChanged;
public void throwItemChangedEvent(ItemChangedEventArgs Arguments)
{
if (itemChanged != null)
itemChanged(new Object(), Arguments);
}
}
public class eList<ProjectComponent> : IList<ProjectComponent>
{
List<ProjectComponent> internalList = new List<ProjectComponent>();
public override void Add(ProjectComponent Item)
{
this.internalList.Add(Item);
Item.itemChanged += new ItemChanged(ItemChanged_Handler);
}
private void ItemChanged_Handler(object sender, ItemChangedEventArgs eventArgs)
{
//do stuff here
}
}
An example how it would be called is:
public eList<ChildClass> Children = new eList<ChildClass>();
The idea is that when an object in the list is edited the list object recieve an object like so:
Children.childstring = "anything";
At the moment the field inside the Children object is changed an event could be recieved.
My question is simply what am i doing wrong, why cant i suscribe to the ProjectComponent event inside the eList class?
Or does anyone know a better way to achive the same results?
Thanks in Advance,
Harry
Edit: Definition of ItemChanged delagate:
public delegate void ItemChanged(object sender, ItemChangedEventArgs eventArgs);
public class ItemChangedEventArgs : EventArgs
{
private String p_CallStack;
public String CallStack
{
get { return this.p_CallStack; }
set { this.p_CallStack = value; }
}
public ItemChangedEventArgs()
{
p_CallStack = "";
}
public ItemChangedEventArgs(String StackStart)
{
p_CallStack = StackStart;
}
}
you have 2 errors:
1.
in generic class definition you must use variables not existing classes:
public class eList<ProjectComponent>: ...
--> public class eList<T>: ...
in your case you want to do:
public class eList : IList<ProjectComponent>
2.
Item.itemChanged += new Item.itemChanged(ItemChanged_Handler);
new Item.itemChanged has no meaning, you have to use the underlying delegate type of your event:
Item.itemChanged += new ItemChanged(ItemChanged_Handler);
N.B:
your code does not respect at all design guidelines for c#
More informations here:Naming Guidelines
Shouldn't it be
Item.itemChanged += new ItemChanged(ItemChanged_Handler);
Related
I am trying to subscribe a event but it is showing error on the mentioned line. Can anyone tell me what the problem in my code is?
I am new to programming, so kindly please provide a detailed answer. Thanks in advance
namespace Events
{
delegate void MyEventHandler();
internal class ArrayListExamplePublisher : ArrayList
{
public event MyEventHandler objMyEventHandler;
void OnAdded()
{
if (objMyEventHandler != null)
{
objMyEventHandler();
}
}
public override int Add(object? value)
{
OnAdded();
return base.Add(value);
}
}
public class ArrayListExample
{
static void Main()
{
ArrayList arrayList = new ArrayList();
// Here I get an error
arrayList.objMyEventHandler += () => Console.WriteLine("Object Added");
arrayList.Add(1);
arrayList.Add("4");
}
}
}
You are adding the event to ArrayListExamplePublisher, not ArrayList, so when you try to assign that event on an ArrayList, it fails.
You can't add an event to a class externally in C# - you'd have to modify the definition of ArrayList, which you can't do.
You could do:
ArrayListExamplePublisher arrayList = new ArrayListExamplePublisher();
and set the event on that, but it's not clear if that's what you want.
Side note: The framework already has ObservableCollection<T> that has events for when the collection is modified (and is strongly-typed, unlike ArrayList.
Your lambda expression has to match the delegate type for the event.
Most events take two arguments, so this should probably work:
arrayList.objMyEventHandler += ( s, e ) => Console.WriteLine("Object Added");
So i have figured it out
As i am inheriting the class ArrayList so I should use object of child class but i was using the object of parent class. This works perfectly
ArrayListExamplePublisher objArrayListExamplePublisher = new ArrayListExamplePublisher();
objArrayListExamplePublisher.Added += () => Console.WriteLine("Object Added");
objArrayListExamplePublisher.Add(1);
objArrayListExamplePublisher.Add("4");
I am brand new to C# (I apologise if my question is noobish - I'm teaching myself, so it's a bumpy process). I am trying to develop a winform and since some of the methods are pretty long, I am trying to keep it in a couple classes. This is what I'm kind of hoping to achieve:
public partial class formMainForm : Form
{
public formMainForm()
{
InitializeComponent();
}
private void UpDown1_ValueChanged(object sender, EventArgs e)
{
longCalculations.LongMethod1();
}
}
public class longCalculations
{
private void LongMethod1()
{
// Arbitrarily long code goes here
}
}
I'm doing this in an attempt to keep the formMainForm class tidy and be able to split any calculations into manageable chunks. However, I'm encountering problems with using form controls (e.g. check boxes, numeric up-down controls, etc.) in my non-form classes.
If I leave them as is (e.g. CheckBox1) I get a the name does not exist in the current context error. I searched around and I found that it's because that box is defined in a different class. However, if I change it to formMainForm.CheckBox1, the error is now an object reference is required for the non-static field, method or property. Again, I looked around and it appears that that is due to the form initialization method not being static.
If I change public formMainForm() to static formMainForm(), the error now moves to InitializeComponent(); and I do not know where to go from here. I also tried making an instantiation of the formMainForm() method, but that didn't do anything (the code I attempted to use is below. I found it somewhere on this site as an answer to a similar problem).
private void formLoader(object sender, EventArgs e)
{
shadowrunMainForm runForm = new shadowrunMainForm();
runForm.Show();
}
How can I use the formcontrol names in other classes?
P.S. It is my first post here - I am super sorry if I have missed this question already being asked somewhere. I did search, but I didn't find what I was looking for.
EDIT
It seems I hadn't made myself clear - this was just an example of code and my problem is with the second class, not the first one. I have now simplified the code to:
public partial class formMainForm : Form
{
public formMainForm()
{
InitializeComponent();
}
}
public class longCalculations
{
private void LongMethod1()
{
List<CheckBox> listOfBoxes = new List<CheckBox>();
listOfBoxes.Add(CheckBox1);
// The code displays an "object reference is required for the non-static field, method or property" error at this stage. Changing the "CheckBox1" to formMainForm.CheckBox1 doesn't help
// Arbitrarily long code goes here
}
}
LongMethod1 works perfectly fine when placed in the formMainForm partial class. Moving it to the other form makes it unable to take data from those checkboxes.
I believe this line longCalculations.LongMethod1(); is throwing error cause you are trying to access a instance method as if it's a static method and as well it's defined as private method which won't be accessible outside the class. You need to create an instance of longCalculations class before accessing any of it's member or method(s) and mark the method public like
private void UpDown1_ValueChanged(object sender, EventArgs e)
{
longCalculations ln = new longCalculations();
ln.LongMethod1();
}
public class longCalculations
{
public void LongMethod1()
{
// Arbitrarily long code goes here
}
}
(OR) If you really want it to be a static method then define accordingly with static modifier like
public class longCalculations
{
public static void LongMethod1()
{
// Arbitrarily long code goes here
}
}
Now you can call it like the way you are trying
public static class longCalculations
{
public static void LongMethod1()
{
// Arbitrarily long code goes here
}
}
If you're going to make a call longCalculations.LongMethod1();, then you need to make your class static as such.
Or you leave it as not static method by calling
longCalculations lc = new longCalculations()
lc.LongMethod1();
As for accessing controls in separate classes, you can pass in the form and make the controls public which can be dangerous.
So on your Form.designer.cs, change any control you may have to public modifier. Then you would make a call like this...
private void UpDown1_ValueChanged(object sender, EventArgs e)
{
longCalculations.LongMethod1(this);
}
public void LongMethod1(Form1 form)
{
// Arbitrarily long code goes here
form.label1.Text = someString;
//more settings and whatnot
}
Or do something like this:
public class longCalculations
{
public string LongMethod1()
{
// Arbitrarily long code goes here
return myString;
}
}
longCalculations lc = new longCalculations()
string result = lc.LongMethod1();
this.label1.Text = result;
Ideally, your longCalculations class would not attempt to modify the form directly. Instead it would return an object that the form could use to update its controls.
If you need to access the form directly from the longCalculations class, first change the method to accept an instance of your form
public void LongMethod1(formMainForm myForm)
Then you can pass the form itself as a parameter
var calc = new longCalculations();
calc.LongMethod1(this);
In your other class, you need to have an instance of your formMainForm class:
var myForm = new formMainForm();
Then you can access its members like this:
myForm.CheckBox1.Checked = true;
I can't find a specific problem that matches mine, which I think has to do with the fact that I am creating a subclass of EventArgs with one argument, a string. When I try to compile, it seems to tell me that ScanInfoEventArgs doesn't have one constructor, when it clearly does (clearly to me at least).
I've only included the bit of code that I think applies. It seems like such a simple thing that I am at a loss.
public partial class MainWindow : Window
{
Coffee coffeeOnHand;
SweetTea sweetTeaOnHand;
BlueberryMuffin blueberryMuffinOnHand;
public MainWindow()
{
InitializeComponent();
//The following reads the inventory from file, and assigns each inventory item to the Coffee, SweatTea
//and BlueberryMuffin objects in memory.
using (Stream input = File.OpenRead("inventory.dat"))
{
BinaryFormatter formatter = new BinaryFormatter();
coffeeOnHand = (Coffee)formatter.Deserialize(input);
sweetTeaOnHand = (SweetTea)formatter.Deserialize(input);
blueberryMuffinOnHand = (BlueberryMuffin)formatter.Deserialize(input);
}
//The following adds whatever information is loaded from the objects on file from above
//into the dropdown box in the menu.
SelectedItemDropdown.Items.Add(coffeeOnHand);
SelectedItemDropdown.Items.Add(sweetTeaOnHand);
SelectedItemDropdown.Items.Add(blueberryMuffinOnHand);
}
public class ScanInfoEventArgs : EventArgs
{
ScanInfoEventArgs(string scanType)
{
this.scanType = scanType;
}
public readonly string scanType;
}
public class Scan
{
//Delegate that subscribers must implement
public delegate void ScanHandler (object scan, ScanInfoEventArgs scanInfo);
//The event that will be published
public event ScanHandler onScan;
public void Run()
{
//The ScanInfoEventArgs object that will be passed to the subscriber.
ScanInfoEventArgs scanInformation = new ScanInfoEventArgs("scanType");
// Check to see if anyone is subscribed to this event.
if (onScan != null)
{
onScan(this, scanInformation);
}
}
}
You need to make your constructor public. All class members default to private, which means the outside world can't get at them.
Since the compiler didn't see a matching public constructor (as in, one the code could actually invoke), it threw the error you saw.
Correct code:
public ScanInfoEventArgs(string scanType)
{
this.scanType = scanType;
}
Note that internal would work as well if all code resides in the same assembly.
I have a class which is inherited from context bound object. Class has attribute on some properties. When some property is changed, PostProcess(IMessage msg, IMessage msgReturn) raise an event and from the event again a new property with same attribute is fired. Second change should also call PostProcess, but it is not happening. Probably because, the object where second property is changed is not original .net object but MarshalByRefObject / ContextBoundObject / Proxy Object. My query is how to cast the proxy to original object. I tried casting and SynchonizationAttribute, but it does not help. Just to let you know the events are executing in Async manner so it does not block code execution, and both proxy and original object exist in same app domain.
I tried with two object, one holding reference of second, and when property of first is changed, it try to change property of second, but it did not invoke PostProcess.
Basically I need to make a tree where various objects are depending on property of other objects. And when any one property is changed, it should trigger its watcher, and this could spread like a chain untill no watcher is found. I am trying it with ContextBoundObject.
Sample:
public class PowerSwitch : ObjectBase
{
[Watchable]
public bool IsOn { get; set; }
public bool IsWorking { get; set; }
}
public class Bulb : ObjectBase
{
public Color Color { get; set; }
[Watchable]
public bool IsOn { get; set; }
protected override void Update(object sender, PropertyChangeEventArgs e)
{
((Bulb)this).IsOn = !((Bulb)this).IsOn;
//<-- b1.update should be called after this, but it is not
}
}
[Watchable]
public class ObjectBase : ContextBoundObject
{
public virtual void Watch(ObjectBase watch, string propertyName)
{
watch.Watcher.Add(this, propertyName);
}
protected virtual void Update(object sender,
PropertyChangeEventArgs e) { }
public Dictionary<ObjectBase, string> Watcher
= new Dictionary<ObjectBase, string>();
internal void NotifyWatcher(
PropertyChangeEventArgs propertyChangeEventArgs)
{
Watcher.Where(sk => sk.Value == propertyChangeEventArgs.Name)
.ToList()
.ForEach((item) =>
{
item.Key.Update(this, propertyChangeEventArgs);
});
}
}
Main method
PowerSwitch s1 = new PowerSwitch();
Bulb b1 = new Bulb();
b1.Watch(s1, "IsOn");
s1.IsOn = true; //<-- b1.update is called after this
Please suggest alternate or better way to implement what I want to achieve.
It looks like you're very close to the observer pattern, where Observers subscribe to status notifications on a Subject.
In that pattern, b1.IsOn = true would contain code that cycles through the Observers and notifies them of changes. I guess if you wanted to observe a lot of properties on one object, you could encapsulate the notification code. Then b1.IsOn could just have a single line that says something like:
this.SendNotification("IsOn", Value);
This seems a lot like what you're doing... If you read up on the pattern you might end up feeling a little more confident, and maybe you could make a few changes to standardize your code.
By the way, there is something built into .Net for this -- IObservable(T). I haven't used this, but it looks strong.
I would like to pass the reference of a variable into a class, use it and then get it out later.
Something like this:
// Create the comment Screen
string newCommentText = "";
commentsScreen = new CommentEntry(this, ref newCommentText);
commentScreen.ShowDialog();
...
_dataLayer.SaveOffComment(newCommentText);
And then in the comment class:
public partial class CommentEntry : Form
{
public CommentEntry(Control pControl, ref string commentResult)
{
InitializeComponent();
control = pControl;
// ***** Need a way for this to store the reference not the value. *****
_commentResult = commentResult;
}
private string _commentResult;
private void CommentEntry_Closing(object sender, CancelEventArgs e)
{
_commentResult = tbCommentText.Text.Trim();
}
}
Is there someway that newCommentText can have the value set in _commentResult in the closing method?
NOTE: Clearly it would be easy to just set a variable in my class and access it after the ShowDialog. This example is only a an approximation of my real issue and accessing any variables in the class after ShowDialog is done is not possible.
This will never work with a String as they are immutable and the variable will change to point to a new instance.
You have two basic options. The first is to simply have a getter for the result so it can be accessed when it is needed later. The other option is to have the owner pass in a delegate method that can be called passing in the resulting value. The owner would then receive the value when the CommentEntry is closing.
You generally can't directly store a 'reference to a reference' in C#, but you could do something like this:
public interface ICommented
{
string Comment { get; set; }
}
public class MyClass : ICommented
{
public string Comment { get; set; }
}
public partial class CommentEntry : Form
{
public CommentEntry(Control pControl, ICommented commented)
{
InitializeComponent();
control = pControl;
// ***** Need a way for this to store the reference not the value. *****
_commented = commented;
}
private ICommented _commented;
private void CommentEntry_Closing(object sender, CancelEventArgs e)
{
_commented.Comment = tbCommentText.Text.Trim();
}
}
So now your form can edit the comment of any class that has said it knows how to be commented upon.
As Dan Bryant pointed out, you cannot do that directly. One option is to wrap the reference into a class, but that requires writing a lot of boilerplate code. A simpler option is to use delegate and lambda functions (in C# 3.0) or anonymous delegates (C# 2.0):
string newCommentText = "";
// Using lambda that sets the value of (captured) variable
commentsScreen = new CommentEntry(this, newValue => {
newCommentText = newValue });
commentScreen.ShowDialog();
_dataLayer.SaveOffComment(newCommentText);
A modified version of the CommentEntry form would look like this:
public partial class CommentEntry : Form {
public CommentEntry(Control pControl, Action<string> reportResult) {
InitializeComponent();
control = pControl;
// Store the delegate in a local field (no problem here)
_reportResult = reportResult;
}
private Action<string> _reportResult;
private void CommentEntry_Closing(object sender, CancelEventArgs e) {
// Invoke the delegate to notify the caller about the value
_reportResult(tbCommentText.Text.Trim());
}
}
Make newComment property of CommentEntry class.
Here, try out what this guy is doing.
http://www.c-sharpcorner.com/UploadFile/gregory_popek/WritingUnsafeCode11102005040251AM/WritingUnsafeCode.aspx