Workaround for generic event handler in Windows Forms - c#

Quite some time ago, I noticed that the Windows Forms editor of Visual Studio does not support events which contain generic type parameters. For example, an event like
public event EventHandler<ListEventArgs<int>> MyStrangeEvent { add { ... } remove { ... } }
where
public class ListEventArgs<T> : EventArgs { List<T> args; }
does not even show up in the event list in the property manager of Visual Studio. Now, this is a somewhat artificial example that could easily be modified to work in Visual Studio by rewriting the classes and their events. However, I am currently working on a project where I cannot change some classes for compatibility reasons. The only thing I can do is to change the events of my user control. The events of this control currently look like this:
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
Note that the underlying Plane class (represented by the _Plane instance which is a protected field) cannot be changed. Its DrawingError event and its EventArgs type are declared in the Plane class like this:
public class Plane<T> where T : ISurface
{
...
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
...
public class DrawingErrorEventArgs : EventArgs { ... /* Uses T */ ... }
}
Of course, the Windows Forms editor of Visual Studio does not show any of the events of my user control. I have been looking for a number of workarounds to get them shown again, but have not been able to find a workaround that actually works. Here are some things that I tried:
Created a MyPlane class which inherits from Plane and used that instead: public event EventHandler<MyPlane.DrawingErrorEventArgs> DrawingError .... For reasons unknown to me, the events still don't show up in the editor. Perhaps this is due to the parameters of the event, some of which still are generic. Find a minimal working example below.
Created a helper class which defines implicit conversion operators between EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> and EventHandler<GDIPlane.DrawingErrorEventArgs> where GDIPlane is just a dummy class which inherits from Plane<GDISurface>. This does work to some extent, but duplicates event calls since the conversion creates new event handlers which are passed down to _Plane which cannot be removed/unregistered properly.
Tried to inherit from EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>, which obviously does not work since EventHandler<T> is sealed.
Are there any other ways to make my events visible again in the Windows Forms editor?
Best regards
Andreas
EDIT: Minimal working example for 1:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
public class GDIPlane : Plane<GDISurface> { }
GDIPlane _Plane = null;
public event EventHandler<GDIPlane.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
DrawingError does not show up in the list of events in the property manager when clicking on a TestControl instance.
EDIT2: This is the original problem (without any workarounds) where the DrawingError event does of TestControl does not show up either:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
Plane<GDISurface> _Plane = null;
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}

This is behavior specific to Visual Studio, and the cause is rooted in the fact that EventHandler<> does not specify covariance on its 'TEventArgs' (it would impose seemingly silly restrictions) and the tools do not perform enough introspection of your code to suss out an appropriate type (even though you've left a trail of type data in constructing the control.) Thus, it seems as though VS does not support generic event properties. You may consider filing a feature request on Microsoft Connect, I wouldn't suggest filing it as a bug as they may label it "by design" and close it.
As a general rule, if you need generic type parameters on your events and you need design time support for them (which are different implementation concerns), you're looking at wrapping them in a presentation-specific facade (e.g. "extra layer of code to facilitate design-time needs".)
Personally, I would reduce the generic typing you have in play now, it seems a bit excessive and if you don't understand covariance/contravariance in generic types it might put you in a tight spot at some point, such as now.
However, to work around your problem:
Consider using a custom event args class which could transport data in a non-generic property, and also use a non-generic EventHandler event/property. Understanding the 'type' of the event is then shifted away from generic type parameters and made the responsibility of your non-generic event args instead. If the 'class' of the event args is insufficient, you can add a property to convey the event type (or data type) so that receiving code can properly interpret it (assuming, of course, that it does not already know by some other means.):
public class DataEventArgs : EventArgs
{
//public string EventTypeOrPurpose { get; set; }
public object Data { get; set; }
}
This is most often only used to ferry data through an event chain, and it is usually implemented as follows:
public class DataEventArgs<T> : EventArgs
{
public T Data { get; set; }
}
Unfortunately, this also has a covariance problem, to resolve it you would actually want something more like this:
public interface IDataArgs<out T>
{
T Data { get; }
}
public class DataEventArgs<T> : EventArgs, IDataArgs<T>
{
public DataEventArgs<T>(T data)
{
_data = data;
}
private T _data;
public T Data { get { return _data; } }
}
Even so, these generic versions still don't work around Visual Studio's limitations, this is merely more proper alternative forms of what you already have shown us.
UPDATE: As requested, here is what a "purpose built facade" might look like in the most basic sense. Note that the usercontrol functions as a facade layer in this case as the eventhandler it exposes delegates to the underlying object model. There is no direct access to underlying object model from the user control (from consumer/designer perspective.)
Please note the reference tracking for event handlers is not necessary unless you dispose of these user controls throughout the lifetime of the app (it is only done to ensure proper delegate removal based on the delegate provided, which is wrapped in a closure/delegate, as you see below.)
Also worth noting I did not test-run this code beyond verifying that the designer shows DrawingError in the property grid when dropped onto a form.
namespace SampleCase3
{
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
private Plane<GDISurface> _Plane = new Plane<GDISurface>(); // requires initialization for my own testing
public TestControl()
{
}
// i am adding this map *only* so that the removal of an event handler can be done properly
private Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>> _cleanupMap = new Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>>();
public event EventHandler DrawingError
{
add
{
var nonGenericHandler = value;
var genericHandler = (EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>)delegate(object sender, Plane<GDISurface>.DrawingErrorEventArgs e)
{
nonGenericHandler(sender, e);
};
_Plane.DrawingError += genericHandler;
_cleanupMap[nonGenericHandler] = genericHandler;
}
remove
{
var nonGenericHandler = value;
var genericHandler = default(EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>);
if (_cleanupMap.TryGetValue(nonGenericHandler, out genericHandler))
{
_Plane.DrawingError -= genericHandler;
_cleanupMap.Remove(nonGenericHandler);
}
}
}
}
}
To complement the above, here is what a non-generic event handler would now look like:
private void testControl1_DrawingError(object sender, EventArgs e)
{
var genericDrawingErrorEventArgs = e as Plane<GDISurface>.DrawingErrorEventArgs;
if (genericDrawingErrorEventArgs != null)
{
// TODO:
}
}
Note that the consumer here has to have knowledge of the type for e to perform conversion. The use of the as operator will bypass ancestry checks under the assumption that the conversion should succeed.
Something like this is as close as you're going to get. Yes it is ugly by most of our standards, but if you absolutely 'need' design-time support on top of these components and you cannot change Plane<T> (which would be more appropriate) then this, or something close to this, is the only viable workaround.
HTH

Related

Alternative to Template Pattern where the implementation can have an attribute

I'm using a series of Template Pattern classes that represent different types of events.
internal abstract class DayEndingEvent : Event
{
internal void OnDayEnding(object? sender, DayEndingEventArgs e)
{
if (IsHooked) OnDayEndingImpl(sender, e);
}
protected abstract void OnDayEndingImpl(object? sender, DayEndingEventArgs e);
}
This pattern ensures that the implementation only runs if the event is "hooked", which allows other parts of the application to activate/deactivate the event by calling Hook and Unhook methods from the base Event class.
internal abstract class Event
{
public bool IsHooked {get; private set;}
public bool Hook() => !IsHooked && (IsHooked = true);
public bool Unhook() => IsHooked && !(IsHooked = false);
}
(Event is obviously more complex than this, but this is enough to get the picture).
My EventManager can instantiate one of every implementation of this pattern and hook their OnDayEnding to the appropriate handler in an external API.
This has worked fine for a while, but now I have a new requirement to add prioritization to these classes. The only way to do so (and this is a limitation of the external API) is by adding attribute [EventPriority] to the event callback. But obviously I can't annotate OnDayEnding with a priority since that would set the priority of all implementations, which defeats the whole purpose.
The attribute will have no effect anywhere else but on the callback. The only other solution I can see is to remove the Impl and just make the callback itself abstract. But that means I'd have to manually check the IsHooked flag on every implementation, which is what I want to avoid.
So question is, can anybody sugest an alternative to this pattern that would both 1) allow me to have different implementations of the callback, to which I can add priority attributes, and 2) enforce the check for IsHooked?
There are two possibilities I have come across recently when I encountered a similar problem:
Option one, have an entry method that has the required attributes:
public class SpecificImplementationClass1 : BaseClass, IInitializer
{
[SomeAttribute]
public void CallMeToInitiate(SomeType input)
{
ExecuteCommonCode(input);
}
protected override void ExecuteSpecificCode(object input)
{
var typedInput = (SomeType) input;
// ...execute whatever implementation-specific code here
}
}
public class BaseClass
{
protected void ExecuteCommonCode(object input)
{
// DoSomethingBefore(input);
ExecuteSpecificCode(input);
// DoSomethingAfter(input);
}
protected abstract void ExecuteSpecificCode(object input);
}
public interface IInitializer
{
void CallMeToInitialize(SomeType input);
}
// Get all IInitializers through dependency injection and call "CallMeToInitialize(new SomeType())" on each
Option two, use the template delegate pattern

Save references to generic ObservableCollections with common BaseClass

This problem has been keeping me busy for half a day now and I start to lose my sanity:
I'm using Items for UI Logic stuff. There are "parent" Items, that can contain ObservableCollections of other Items. (Both inherit from the same ItemBase, picture nodes with nodes, sort of recursive)
For not having to recreate Observer logic on each "parent" item class, I wanted to add the functionality to the common baseclass, called ItemBase. The idea is, that the parent can just register its ObservableCollections and the baseclass takes care of the event routing and all. The problem is, that I can't seem to find a way to save a reference to these ObservableCollections (of different types with the same baseclass) for the way that generics work.
Here's the code:
public abstract class ItemBase : ViewModelBase
{
private List<ObservableItemCollection<ItemBase>> _trackedChildItemsList = new List<ObservableItemCollection<ItemBase>>();
public event EventHandler<ItemPropertyChangedEventArgs> ChildItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ChildItemIsDirtyChanged;
public override bool IsDirty
{
get { return base.IsDirty || AreAnyChildItemsDirty; }
set { base.IsDirty = value; }
}
private bool AreAnyChildItemsDirty
{
get
{
return _trackedChildItemsList.Any(i => i.Any(l => l.IsDirty));
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection); // intellisense underlines 'collection'; cannot convert from 'ObservableItemCollection<T>' to ObservableItemCollection<ItemBase>:
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (ObservableItemCollection<ItemBase> collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
OnChildItemPropertyChanged(e);
}
protected virtual void OnChildItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ChildItemPropertyChanged?.Invoke(this, e);
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
OnItemIsDirtyChanged(e);
}
protected virtual void OnItemIsDirtyChanged(IsDirtyChangedEventArgs e)
{
ChildItemIsDirtyChanged?.Invoke(this, e);
}
}
As you can see, I'm using a derived, custom type of the ObservableCollection, namely ObservableItemCollection, which takes care of the ItemPropertyChanged and ItemIsDirtyChanged invokation for the collection itself. This allows one to catch those events from the outside.
Now, instead of having that 'catching the events' logic in each parent item itself (duplicated), I wanted it to be in a centralized spot, namely the baseclass.
Now the main problem is, that upon registering the ObservableItemCollections, I cannot possibly keep a reference to them since there's no common base. ObservableItemCollection<CustomItem> does not inherit from ObservableItemCollection<ItemBase>, since its a collection. I tried solving the whole thing with generics, however, the above is as far as I got. It fails to compile where i wrote the 'cannot convert from 'ObservableItemCollection' to ObservableItemCollection' comment.
I understand why it fails to compile, however, I can't seem to find a workaround/working solution.
I absolutely need a direct reference to the collections (casted as my custom type ObservableItemCollection), else the whole thingy won't work. You can see in the code that I'm accessing both the events of the collection itself, as well as properties of the ItemBase.
Either way, I can't seem to find a common base for the collections. I tried using dynamics and reflection based casting, Interfaces, a Custom generic ParentItem type, neither worked (i might have overlooked something) and even if it did, it would be rather ugly.
Is it really not possible to achieve what I want with a limited amount of hacking things together? I can't believe that I didn't find a good solution after all the time I've invested in this.
Additional info:
In the parent item i have the following ObservableCollections:
public ObservableItemCollection<SomeItem1> Collection1 { get; set; } = new ObservableItemCollection<SomeItem1>();
public ObservableItemCollection<SomeItem2> Collection2 { get; set; } = new ObservableItemCollection<SomeItem2>();
Where both item types inherit from ItemBase. Then i call the base method RegisterItemCollection in the parent item constructor like so:
RegisterItemCollection(Collection1);
RegisterItemCollection(Collection2);
WPF collection controls have the same problem: How do you define a property which can hold a reference to any kind of generic collection? Answer: Make the property a reference to a non-generic interface that all the collections implement. This is a very general question, and it's the reason why non-generic System.Collections.IEnumerable and System.Collections.IList are still in heavy use throughout the .NET framework, all these years after generics were introduced.
Nothing you're doing in RegisterItemCollection(), IsDirty, or Dispose() needs to care about the type of item in the collection. So take whatever methods and properties you need that code to interact with, and put it all in a non-generic interface or base class. Your base class is already generic (ObservableCollection<T>, I presume), so use an interface.
public interface IObservableItemCollection
{
event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
bool IsDirty { get; }
}
public interface IDirtyable
{
// I'm pretty sure you'll want this event here, and I think you'll want your collection to
// implement IDirtyable too.
//event EventHandler<IsDirtyChangedEventArgs> IsDirtyChanged;
bool IsDirty { get; }
}
public class ObservableItemCollection<T>
: ObservableCollection<T>, IObservableItemCollection
where T : IDirtyable
{
public bool IsDirty => this.Any(item => item.IsDirty);
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
}
public class ViewModelBase : IDisposable, IDirtyable
{
public virtual bool IsDirty => true;
public virtual void Dispose()
{
}
}
public class ItemBase : ViewModelBase
{
private List<IObservableItemCollection> _trackedChildItemsList = new List<IObservableItemCollection>();
public override bool IsDirty
{
get
{
return base.IsDirty || _trackedChildItemsList.Any(coll => coll.IsDirty);
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection);
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (IObservableItemCollection collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
}
}
public class ItemPropertyChangedEventArgs : EventArgs
{
}
public class IsDirtyChangedEventArgs : EventArgs
{
}
You could also do this by making _trackedChildItemsList a collection of IDisposable, and have the collections clear their own event handlers, but a class clearing its own event handlers is pretty gruesome. Shun reflection when conventional OOP can be used to do the job in a readable and maintainable way. And you'd still have to think of something for IsDirty.
You can not do this since if you could you could do something like
class A {}
class B : A { }
class C : A { }
var list = new List<List<A>>();
var sublist_b = new List<B>();
sublist_b.Add(new B());
list.Add(sublist_b);
var sublist = list.Single();
sublist.Add(new C()); // <- now a List<B> contains an object that ist not if type B or derived B
I would suggest that you only use ObservableItemCollection<ItemBase> to hold your objects.

Castle Windsor interceptor blocking PropertyChanged events

I have created a test project as a POC for this problem.
I have a WPF app, that when we use interceptors around the view models, it's stopping the propagation of events. If I disable all interceptors, it works fine.
Here is the code:
MyInterceptor.cs
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
}
}
IoCTestViewModel.cs
public interface IIoCTestViewModel : INotifyPropertyChanged
{
int Number { get; }
}
public class IoCTestViewModel : IIoCTestViewModel
{
public IoCTestViewModel()
{
var timer = new Timer(200);
timer.Elapsed += (a, b) => {
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Number"));
}
};
timer.Start();
}
public int Number
{
get
{
return new Random().Next(1, 100);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
IoCTest.xaml.cs
public partial class IoCTest : UserControl
{
public IIoCTestViewModel ViewModel { get; set; }
public IoCTest(IIoCTestViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
App.xaml (fragment)
Container = new WindsorContainer();
Container.Register(Component.For<MyInterceptor>().ImplementedBy<MyInterceptor>());
Container.Register(Component.For<IIoCTestViewModel>().ImplementedBy<IoCTestViewModel>().Interceptors<MyInterceptor>());
Container.Register(Component.For<IoCPage>().ImplementedBy<IoCTest>()); //IoCTest is a usercontrol
OK. So once I get an instance of IoCTest and add it to a page, I don't see any changes, even though I am sending PropertyChanged every 200ms. If I remove the interceptor, everything works fine.
So how do I fix this?
The issue here is that because you declare your service to be IIoCTestViewModel, when you add an interceptor Windsor simply creates a dynamic proxy that delegates all calls to your implementation type. However, the interception is done using composition - one object delegating to another. Hence, when you raise your property changed event with a sender of this, it is a different object to the one that WPF thinks it is watching.
You should instead register your view model like this:
Container.Register(Component.For<IIoCTestViewModel,IoCTestViewModel>().Implemen‌​tedBy<IoCTestViewModel>().Interceptors<MyInterceptor>())
By specifying multiple services, one of which is actually your implementation class, Windsor will instead generate a class proxy - i.e. the interception will be done using inheritance, with the generated proxy inheriting from IoCTestViewModel. (This is known as type forwarding in Windsor). Now when you raise your event with a sender of this it correctly refers to the same instance that WPF is watching.
See here for a more detailed explanation of type forwarding and its implications for proxies

Percolate data through properties

I've got a class with a list of properties, and the properties themselves have a list of properties. I need the "grandchild"- property to be able to request data from it's parent's parent (sorry for the confusing terms - there's no inheritance here). To clarify:
class ClassA
{
list<ClassB> Children
var SomeOtherProperty
}
class ClassB
{
list<ClassC> Grandchildren
}
class ClassC
{
var GetSomeOtherProperty()
{
...
}
}
The data may change during run time so I can't just pass it once and be done with it, I gotta be able to get it dynamically.
I could always pass the parent all the way down to the grandchild in the ctors, but I was taught it's a bad practice, so I'd rather avoid it.
I've been reading about passing data back/downwards via events using mutable EventArguments- I'm wondering if that'd be an OK solution for what I've got (I'll need to do it twice each time- sort of chase the tail of the first eventarg). Are there any pitfalls I need to be aware of?
Do I have any other options for this situation?
Thanks!
If possible, you can use the design pattern composite. First, create a base class 'note' with a list of notes for the children and a virtual method 'DoAction'. Then derive all other classes from this class and override the method with own implementation.
Now you can build up a tree of notes and do a traversal on all of it childs. For each of them, call 'DoAction'.
Hope you get the idea...
Since classes A, B & C are not liked via inheritance, therefore, I would like to avoid any direct wiring between these classes as it will make the design a little complicated as the system would evolve.
Moreover, since class B has nothing to do with the data, I dont want it to get effected by it.
Therefore, I would like to take out the communicate via a separate route to keep the classes loosely coupled -
here is a sample code to explain my idea -
public interface IPublisher
{
event EventHandler OperationOccurred;
}
class ClassA : IPublisher
{
List<ClassB> Children;
event EventHandler OperationOccurred;
public ClassA()
{
BroadCaster.Instance.RegisterPublisher(this);
}
protected virtual void OnOperationOccurred()
{
if (OperationOccurred != null)
OperationOccurred(this, new EventArgs());
}
}
class ClassB
{
List<ClassC> Grandchildren;
}
class ClassC
{
public ClassC()
{
BroadCaster.Instance.BroadCastNotificaiton += Instance_OperationOccurred;
}
void Instance_OperationOccurred(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}
/// <summary>
/// A singleton class ... Like a single braodcast tower just one in the city
/// </summary>
public sealed class BroadCaster
{
public static BroadCaster Instance { get; private set; }
//Static constructor
static BroadCaster()
{
Instance = new BroadCaster();
}
// private constructor
private BroadCaster(){}
public event EventHandler BroadCastNotificaiton;
public void RegisterPublisher(IPublisher publisher)
{
publisher.OperationOccurred += Publisher_OperationOccurred;
}
void Publisher_OperationOccurred(object sender, EventArgs e)
{
if (this.BroadCastNotificaiton != null)
this.BroadCastNotificaiton(sender, e);
}
}
I have implemented broadcaster a singleton object, but all I want to convey is a single point of communication.
Hope it helps.

Change value of control on a form from class (C#)

This should be quite simple really - not sure what the problem is.
I have a C# Class (Public.cs) and a windows form (Form1.cs). Through a function in Public.cs, I want to get the value of a control on Form1 (without having to use object parameters).
// This code appears in Public.cs
public string MyFunction(int num_val)
{
if (chk_num.checked == true)
{
// Something here...
}
}
The issue is that my class cannot find the control on my form. Is there some way that I must reference it in C#?
Thank you.
I would strongly suggest exposing the Checked property via a specific property on Form1 (perhaps with a more meaningful name). This will help to hide the implementation details (i.e. control structure) of the Form1 from it's caller and instead expose only the logic that is required for other consumers to do their job
For example:
public bool IsNumberRequested
{
get { return chk_num.Checked; }
}
Or alternatively, if you still really want to access the control directly, from the designer you can select the control and change it's Modifier property to public (or something else) enabling you to access the control object using the code you originally wrote above.
EDIT: (Response based on comment)
Public.cs will still need a reference to Form1 and then will call the IsNumberRequested property of that object.
// Public.cs
public class Public
{
private Form1 _ui;
public Public(Form1 ui) { _ui = ui };
public string MyFunction(int num_val)
{
if (_ui.IsNumberRequested)
{
// Stuff
}
// Else, default Stuff
}
}
Alternatively, you could pass the form as a parameter to the MyFunction too rather than using it as an instance variable.
I would have the set up the other way around
public class Public
{
public bool CheckNumber {get;set;}
public string MyFunction(int val)
{
if(CheckNumber)
{
//do that thing
}
return ...
}
}
public partial class Form1 : Form
{
Public myinstance = new Public();
public Form1()
{
InitializeComponent();
}
private void CheckBoxChanged(object sender, EventArgs e)
{
myinstance.CheckNumber = chk_num.checked;
}
}
You'll need to assign CheckBoxChanged to the OnChanged event handler for your check box (which I'm assuming is chk_num.
This way your class Public doesn't rely on a form, which it shouldn't.
As Reddog says, use better names, although I half suspect you've just given example names in your question.

Categories