So I was trying to create my own event for the initialization of a class called Car, which inherits from an Automobile object. Below is the same in C# code:
`
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Abc.Training.Artifacts;
namespace Abc.Training.Objects
{
public abstract class Automobile
{
public string Model { get; set; }
public string Manufacturer { get; set; }
public string YoM { get; set; }
}
public class Car : Automobile
{
public static event Delegates.ObjectInitHandler OnInit;
public string MarketSegment { get; set; }
public int BootSpace { get; set; }
public Car(string model, string manufacturer, string yom)
{
Model = model ;
Manufacturer = manufacturer;
YoM = yom;
ObjectInitEventArgs eArgs = new ObjectInitEventArgs();
eArgs.IsResidentObject = true;
eArgs.ObjectType = this.GetType();
if (OnInit != null) OnInit(this, eArgs);
}
}
}
`
The ObjectInitHandler and its args (the delegate type used here) is also created by me as:
`
public delegate void ObjectInitHandler(object sender, ObjectInitEventArgs e);
public class ObjectInitEventArgs:EventArgs
{
public Type ObjectType { get; set; }
public bool IsResidentObject { get; set; }
}
`
I am subscribing to the event as below:
`
Car.OnInit += new Delegates.ObjectInitHandler(Car_OnInit);//able to do this as event is static
Car c = new Car("Maruti", "Maruti", "2004");
void Car_OnInit(object sender, ObjectInitEventArgs e)
{
Console.WriteLine("Car object initialized");
}
`
I wanted to create an event OnInit for this class. However, if I put an instance event OnInit in the publisher (my Car class), I will have to initialize the class first before I can subscribe to this event. Since I would like to fire this event on initialization, this becomes a chicken and egg problem for me.
I solved it by creating a static event Object and doing the subscription before the object initialization as shown below (this is a snippet from the code above itself):
public static event Delegates.ObjectInitHandler OnInit;
However, in an ASP.NET application, this would mean if multiple users access this application, I will have the same delegate object that will have duplicate subscriptions of events (because its static), which is obviously not cool.
Is there a design pattern which I can follow to have the event also as an instance member but still I can subscribe to the event before instantiation?
I think you have to pass that function as a callback:
public class Car : Automobile
{
// public static event Delegates.ObjectInitHandler OnInit; remove this
public string MarketSegment { get; set; }
public int BootSpace { get; set; }
public Car(string model, string manufacturer, string yom,ObjectInitHandler OnInit) //add the callback as parameter.
{
Model = model ;
Manufacturer = manufacturer;
YoM = yom;
ObjectInitEventArgs eArgs = new ObjectInitEventArgs();
eArgs.IsResidentObject = true;
eArgs.ObjectType = this.GetType();
if (OnInit != null) OnInit(this, eArgs);
}
}
Pass a callback to the constructor when initializing an object:
Car c = new Car("Maruti", "Maruti", "2004",new Delegates.ObjectInitHandler(Car_OnInit));
void Car_OnInit(object sender, ObjectInitEventArgs e)
{
Console.WriteLine("Car object initialized");
}
Actually, I don't see a need for an initialization event in your code unless there are asynchronous operations inside your constructor.
here is a way to do it, it don't use statics, and i used Actions instead of event arguments. (you can use it your way!)
note that i passed the callback function to the object when creating!
class Program
{
static void Main(string[] args)
{
Car c = new Car("Maruti", "Maruti", "2004", Car_OnInit);
Console.WriteLine("Press a key to exit...");
Console.ReadKey();
}
static void Car_OnInit()
{
Console.WriteLine("Car object initialized");
}
}
public abstract class Automobile
{
public string Model { get; set; }
public string Manufacturer { get; set; }
public string YoM { get; set; }
}
public class Car : Automobile
{
public event Action OnInit;
public string MarketSegment { get; set; }
public int BootSpace { get; set; }
public Car(string model, string manufacturer, string yom, Action callBack)
{
this.OnInit += callBack;
Model = model;
Manufacturer = manufacturer;
YoM = yom;
if (OnInit != null) OnInit();
}
}
Also you can pass any argumenst if you want, just use Action<T> like Action<string> instead of Action. then your callback will be Car_OnInit(string)
There is no need (even if it were possible). You have everything you need.
Firstly, you wouldn't subscribe to the event each page load/per user. You would do it once.. in Application_Start for example.
Second.. you have everything you need in the event. Notice this line of code:
if (OnInit != null) OnInit(this, eArgs);
You pass this as the sender argument. this is an instance of Car. So, in your event.. you have the instance you care about:
void Car_OnInit(object sender, ObjectInitEventArgs e) {
var instance = sender as Car;
// use instance here.
}
Related
I have currently a problem
I have 1 Interface with two types of argument like this
ITestInterface<ArgumentA>
ITestInterface<ArgumentB>
this interface has only the argument as different
I would like to pass this interface to an constructor of a class. sth like this
public class MyClass
{
public ITestInterface<object> MyInterface {get; set;}
public MyClass(ITestInterface<ArgumentA> testInterfaceA){
this.MyInterface = testInterfaceA as ITestInterface<object>;
this.MyTestInterface.SomeEvent += this.OnSubcribe;
}
public MyClass(ITestInterface<ArgumentB> testInterfaceB){
this.MyInterface = testInterfaceB as ITestInterface<object>;
this.MyTestInterface.SomeEvent += this.OnSubcribe;
}
public void OnSubcribe(){
//Work to do here, dont care about what argument the interface has.
}
}
and to call the MyClass constructor I have sth like this:
public List<MyClass> ClassList = new();
public void testMethod(){
var interA = getInterfaceWithArgumentA();
var myClassA = new MyClass(interA);
var interB = getInterfaceWithArgumentB();
var myClassB = new MyClass(interB);
}
So the problem is i am not able to cast the interface argument to object. I dont need to differenciate the argument either. I just want to avoid to have 2 properties of MyInterface like (MyInterfaceA, MyInterfaceB).
I need also to consider that maybe in the future I will have more type of Argument so maybe to have multiple properties like MyInterfaceA, MyInterfaceB, MyInterfaceC and also multiple constructor for each Interfaceargument type would be a mess.
I just thought about have a Baseclass and the ArgumentA and ArgumentB class derived from it so the cast would work but its not like that.
How would I solve this problem ?
Many Thanks
I think you have not provided what getInterfaceWithArgumentB() and getInterfaceWithArgumentA() method doing. I am making few assumption.
To solve your problem Generic will help.
Following is the example of it.
public class MyClass<T>
{
public ITestInterface<T> MyInterface { get; set; }
public MyClass(ITestInterface<T> testInterfaceA)
{
this.MyInterface = testInterfaceA;
this.MyInterface.SomeEvent += MyInterface_SomeEvent;
}
private void MyInterface_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine(sender);
}
}
public class ArgumentA
{
public string Name { get; set; }
}
public class ArgumentB
{
public int Id { get; set; }
}
public interface ITestInterface<T>
{
T Data { get; set; }
event EventHandler SomeEvent;
void OnSomeEvent();
}
public class TestInterface<T> : ITestInterface<T>
{
public T Data { get ; set; }
public event EventHandler SomeEvent;
public void OnSomeEvent()
{
if(SomeEvent != null)
SomeEvent(Data, EventArgs.Empty);
}
}
You can use it like following.
MyClass<ArgumentA> myClass = new MyClass<ArgumentA>(
new TestInterface<ArgumentA>()
{
Data = new ArgumentA() { Name = "test" }
});
MyClass<ArgumentB> myClas2 = new MyClass<ArgumentB>(
new TestInterface<ArgumentB>()
{
Data = new ArgumentB() { Id = 10 }
});
myClas2.MyInterface.OnSomeEvent();
myClass.MyInterface.OnSomeEvent();
UPDATE
class Program
{
static void Main(string[] args)
{
ObservableCollection<MyClass> items = new ObservableCollection<MyClass>();
MyClass<ArgumentA> myClass = new MyClass<ArgumentA>(
new TestInterface<ArgumentA>()
{
Data = new ArgumentA() { Name = "test" }
});
MyClass<ArgumentB> myClas2 = new MyClass<ArgumentB>(
new TestInterface<ArgumentB>()
{
Data = new ArgumentB() { Id = 10 }
});
items.Add(myClass);
items.Add(myClas2);
myClas2.MyInterface.OnSomeEvent();
myClass.MyInterface.OnSomeEvent();
}
}
public class MyClass
{
public ITestInterface MyInterface { get; set; }
}
public class MyClass<T> : MyClass
{
public MyClass(ITestInterface<T> testInterfaceA)
{
this.MyInterface = testInterfaceA;
this.MyInterface.SomeEvent += MyInterface_SomeEvent;
}
private void MyInterface_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine(sender);
}
}
public class ArgumentA
{
public string Name { get; set; }
}
public class ArgumentB
{
public int Id { get; set; }
}
public interface ITestInterface
{
event EventHandler SomeEvent;
void OnSomeEvent();
}
public interface ITestInterface<T> : ITestInterface
{
T Data { get; }
}
public class TestInterface<T> : ITestInterface<T>
{
public T Data { get; set; }
public event EventHandler SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
SomeEvent(Data, EventArgs.Empty);
}
}
I have an application that loads a list of client/matter numbers from an input file and displays them in a UI. These numbers are simple zero-padded numerical strings, like "02240/00106". Here is the ClientMatter class:
public class ClientMatter
{
public string ClientNumber { get; set; }
public string MatterNumber { get; set; }
}
I'm using MVVM, and it uses dependency injection with the composition root contained in the UI. There is an IMatterListLoader service interface where implementations represent mechanisms for loading the lists from different file types. For simplicity, let's say that only one implementation is used with the application, i.e. the application doesn't support more than one file type at present.
public interface IMatterListLoader
{
IReadOnlyCollection<string> MatterListFileExtensions { get; }
IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile);
}
Let's say in my initial version, I've chosen an MS Excel implementation to load the list of matters, like this:
I'd like to allow the user to configure at runtime the row and column numbers where the list starts, so the view might look like this:
And here's the MS Excel implementation of IMatterListLoader:
public sealed class ExcelMatterListLoader : IMatterListLoader
{
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load using StartRowNum and StartColNum
}
}
The row and column numbers are an implementation detail specific to MS Excel implementations, and the view model doesn't know about it. Nevertheless, MVVM dictates that I control view properties in the view model, so if I were to do that, it would be like this:
public sealed class MainViewModel
{
public string InputFilePath { get; set; }
// These two properties really don't belong
// here because they're implementation details
// specific to an MS Excel implementation of IMatterListLoader.
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public ICommandExecutor LoadClientMatterListCommand { get; }
public MainViewModel(IMatterListLoader matterListLoader)
{
// blah blah
}
}
Just for comparison, here's an ASCII text file based implementation that I might consider for the next version of the application:
public sealed class TextFileMatterListLoader : IMatterListLoader
{
public bool HasHeaderLine { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load tab-delimited client/matters from each line
// optionally skipping the header line.
}
}
Now I don't have the row and column numbers that the MS Excel implementation needed, but I have a Boolean flag indicating whether the client/matter numbers start on the first row (i.e. no header row) or start on the second row (i.e. with a header row).
I believe the view model should be unaware of the change between implementations of IMatterListLoader. How do I let the view model do its job controlling presentation concerns, but still keep certain implementation details unknown to it?
Here's the dependency diagram:
You'd need a seperate viewmodel for each type of file you intend to load.
Each viewmodel does the setup for its particular loader.
These viewmodels can then be passed in as dependencies to the main viewmodel, which calls load on each viewmodel when needed;
public interface ILoaderViewModel
{
IReadOnlyCollection<ClientMatter> Load();
}
public class ExcelMatterListLoaderViewModel : ILoaderViewModel
{
private readonly ExcelMatterListLoader loader;
public string InputFilePath { get; set; }
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public ExcelMatterListLoaderViewModel(ExcelMatterListLoader loader)
{
this.loader = loader;
}
IReadOnlyCollection<ClientMatter> Load()
{
// Stuff
loader.Load(fromFile);
}
}
public sealed class MainViewModel
{
private ExcelMatterListLoaderViewModel matterListLoaderViewModel;
public ObservableCollection<ClientMatter> ClientMatters
= new ObservableCollection<ClientMatter>();
public MainViewModel(ExcelMatterListLoaderViewModel matterListLoaderViewModel)
{
this.matterListLoaderViewModel = matterListLoaderViewModel;
}
public void LoadCommand()
{
var clientMatters = matterListLoaderViewModel.Load();
foreach (var matter in clientMatters)
{
ClientMatters.Add(matter)
}
}
}
As you add more types to the application, you'd create new view models and add those as dependencies.
You could have a function that constructs the UI elements based on the specific type of the interface.
public static void ConstructUI(IMatterListLoader loader) {
Type loaderType = loader.GetType();
// Do logic based on type
}
You could have classes for each of the IMatterListLoader implementations, which contains logic concerning the presentation. (You don't want to mix the UI presentation logic in with the IMatterListLoader implementations).
Based on the type of the loader, you use the correct class to generate the UI elements.
I would add a Draw() method to the IMatterListLoader interface. Your MainViewModel would then just call Draw() and the the actual IMatterListLoader will add whatever parameters are needed to the UI.
This is a bit conceptual as I'm not too familiar with WPF, so you might need to change the code to use UserControl's or something but the logic is the same.
For example, lets say you have AsciiMatterListLoader which requires no input from the client, then nothing will be shown in the MainViewModel. But if the ExcelMatterListLoader is loaded, the MainViewModel should add the necessary user inputs.
public sealed class AsciiMatterListLoader : IMatterListLoader
{
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load data with no parameters
}
public Panel Draw()
{
// Nothing needs to be drawn
return null;
}
}
public sealed class ExcelMatterListLoader : IMatterListLoader
{
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load using StartRowNum and StartColNum
}
public Panel Draw()
{
Panel panelForUserParams = new Panel();
panelForUserParams.Height = 400;
panelForUserParams.Width = 200;
TextBox startRowTextBox = new TextBox();
startRowTextBox.Name = "startRowTextBox";
TextBox startColumnTextBox = new TextBox();
startColumnTextBox.Name = "startColumnTextBox";
panelForUserParams.Children().Add(startRowTextBox);
panelForUserParams.Children().Add(startColumnTextBox);
return panelForUserParams;
}
}
public sealed class MainViewModel
{
public string InputFilePath { get; set; }
public ICommandExecutor LoadClientMatterListCommand { get; }
public MainViewModel(IMatterListLoader matterListLoader)
{
var panel = matterListLoader.Draw();
if (panel != null)
{
// Your MainViewModel should have a dummy empty panel called "placeHolderPanelForChildPanel"
var parent = this.placeHolderPanelForChildPanel.Parent;
parent.Children.Remove(this.placeHolderPanelForChildPanel); // Remove the dummy panel
parent.Children.Add(panel); // Replace with new panel
}
}
}
You might need to use event handlers to pass the user input changes to the IMatterListLoader or maybe make IMatterListLoader a UserControl.
Edit
#rory.ap is right, the service layer shouldn't know about the UI components. Here is my adjusted answer where the IMatterListLoader just exposes the properties it needs by using a dictionary as a PropertyBag instead of telling the UI what to draw. This way the UI layer does all the UI work:
public interface IMatterListLoader
{
IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile);
IDictionary<string, object> Properties { get; }
void SetProperties(IDictionary<string, object> properties);
}
public sealed class AsciiMatterListLoader : IMatterListLoader
{
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IDictionary<string, object> Properties
{
get
{
return new Dictionary<string, object>(); // Don't need any parameters for ascii files
}
}
public void SetProperties(IDictionary<string, object> properties)
{
// Nothing to do
}
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// Load without using any additional params
return null;
}
}
public sealed class ExcelMatterListLoader : IMatterListLoader
{
private const string StartRowNumParam = "StartRowNum";
private const string StartColNumParam = "StartColNum";
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
private bool havePropertiesBeenSet = false;
public IDictionary<string, object> Properties
{
get
{
var properties = new Dictionary<string, object>();
properties.Add(StartRowNumParam, (uint)0); // Give default UINT value so UI knows what type this property is
properties.Add(StartColNumParam, (uint)0); // Give default UINT value so UI knows what type this property is
return properties;
}
}
public void SetProperties(IDictionary<string, object> properties)
{
if (properties != null)
{
foreach(var property in properties)
{
switch(property.Key)
{
case StartRowNumParam:
this.StartRowNum = (uint)property.Value;
break;
case StartColNumParam:
this.StartColNum = (uint)property.Value;
break;
default:
break;
}
}
this.havePropertiesBeenSet = true;
}
else
throw new ArgumentNullException("properties");
}
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
if (this.havePropertiesBeenSet)
{
// Load using StartRowNum and StartColNum
return null;
}
else
throw new Exception("Must call SetProperties() before calling Load()");
}
}
public sealed class MainViewModel
{
public string InputFilePath { get; set; }
public ICommandExecutor LoadClientMatterListCommand { get; }
private IMatterListLoader matterListLoader;
public MainViewModel(IMatterListLoader matterListLoader)
{
this.matterListLoader = matterListLoader;
if (matterListLoader != null && matterListLoader.Properties != null)
{
foreach(var prop in matterListLoader.Properties)
{
if (typeof(prop.Value) == typeof(DateTime))
{
// Draw DateTime picker for datetime value
this.placeHolderPanelForParams.Add(new DateTimePicker() { Name = prop.Key });
}
else
{
// Draw textbox for everything else
this.placeHolderPanelForParams.Add(new TextBox() { Name = prop.Key });
// You can also add validations to the input here (E.g. Dont allow negative numbers of prop is unsigned)
// ...
}
}
}
}
public void LoadFileButtonClick(object sender, EventArgs e)
{
//Get input params from UI
Dictionary<string, object> properties = new Dictionary<string, object>();
foreach(Control propertyControl in this.placeHolderPanelForParams().Children())
{
if (propertyControl is TextBox)
properties.Add(propertyControl.Name, ((TextBox)propertyControl).Text);
else if (propertyControl is DateTimePicker)
properties.Add(propertyControl.Name, ((DateTimePicker)propertyControl).Value);
}
this.matterListLoader.SetProperties(properties);
this.matterListLoader.Load(null); //Ready to load
}
}
Not sure why nobody suggested property attributes and reflection
Just create a new Attribute, eg:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ExposeToViewAttribute : Attribute
{
public string Name { get; set; }
public ExposeToViewAttribute([System.Runtime.CompilerServices.CallerMemberName]string name = "")
{
this.Name = name;
}
}
and make sure it gets added in your view
var t = matterListLoader.GetType();
var props = t.GetProperties().Where((p) => p.GetCustomAttributes(typeof(ExposeToViewAttribute), false).Any());
foreach(var prop in props)
{
var att = prop.GetCustomAttributes(typeof(ExposeToViewAttribute), true).First() as ExposeToViewAttribute;
//Add to view
}
approach will not get much cleaner.
Usage then would be as simple as:
[ExposeToView]
public int Something { get; set; }
[ExposeToView("some name")]
public int OtherFieldWithCustomNameThen { get; set; }
If you use something like WPF however, there are other solutions that may work better for you.
This question already has answers here:
How to check if an object has changed?
(7 answers)
Closed 4 years ago.
Is it possible to know when an property is modified within the entity itself?
E.g.:
public class StudentEntity{
public string studentId { get; set; }
public string studentStatus { get; set; }
public string getStatusChangeDate{
get
{
//if studentStatus change then return date
}
}
}
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed.
For change notification to occur in a binding between a bound client and a data source, your bound type should either:
Implement the INotifyPropertyChanged interface (preferred).
Provide a change event for each property of the bound type.
Rewrite your code:
public class StudentEntity : INotifyPropertyChanged
{
private string studentIdValue;
public string StudentId
{
get { return this.studentIdValue; }
set
{
if(value != this.studentIdValue)
{
this.studentIdValue = value;
this.OnPropertyChanged(nameof(this.StudentId));
}
}
}
private string studentStatusValue;
public string StudentStatus
{
get { return this.studentStatusValue; }
set
{
if(value != this.studentStatusValue)
{
this.studentStatusValue= value;
this.OnPropertyChanged(nameof(this.StudentStatus));
}
}
}
public string StatusChangeDate { get; set; }
public StudentEntity(string studentId, string studentStatus)
{
// Don't invoke property-setters from the ctor to avoid raising the event prematurely), instead set the backing fields directly:
this.studentIdValue = studentId;
this.studentStatusValue = studentStatus;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
class Program
{
static void Main(string[] args)
{
var person = new StudentEntity("101", "Accept");
person.PropertyChanged += Person_PropertyChanged;
person.StudentStatus = "Reject";
Console.ReadLine();
}
private static void Person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
StudentEntity studentEntity = sender as StudentEntity;
if (e.PropertyName == "StudentStatus")
{
studentEntity.getStatusChangeDate = DateTime.Now.ToString();
}
}
}
You can use a method to set the value. This means each time EF loads the record, it won't get overwritten: but of course you have to remember to call the method and not set the property directly.
public class StudentEntity {
public string studentId { get; set; }
public string studentStatus { get; set; }
public DateTime studentStatusChanged { get; set; }
public void SetStudentStatus(string status) {
studentStatus = status;
studentStatusChanged = DateTime.Now;
}
}
Example: https://dotnetfiddle.net/kF8VZR
public class Activity
{
public games _Games {get;set;}
public sports _Sports {get;set;}
}
public class games : PropertyChangedBase
{
public int player
{
get;
set; //have if- else statement
}
}
public class sports : PropertyChangedBase
{
public int sub{get;set;}
}
Aim: when the games player is more than 2, I would like to update sports sub variable to 10.
Question: How can I access the parent class and update the sports class variable?
You could use an event that would signal to the Activity class that it is time to update.
public class games
{
public event UpdatePlayerSubDelegate UpdatePlayerSub;
public delegate void UpdatePlayerSubDelegate();
private int _player;
public int player
{
get { return _player; }
set
{
_player = value;
if (_player > 2)
{
// Fire the Event that it is time to update
UpdatePlayerSub();
}
}
}
}
In the Activity class you can register the event in the constructor and write in to the event handler the necessary update. In your case sub to 10:
public class Activity
{
public games _Games { get; set; }
public sports _Sports { get; set; }
public Activity()
{
this._Games = new games();
this._Games.UpdatePlayerSub += _Games_UpdatePlayerSub;
this._Sports = new sports();
}
private void _Games_UpdatePlayerSub()
{
if (_Sports != null)
{
_Sports.sub = 10;
}
}
}
EDIT
I just saw the tag INotifyPropertyChanged. Of course you could also use this interface and the provided event. Implement the interface as the following:
public class games : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _player;
public int player
{
get { return _player; }
set
{
_player = value;
if (_player > 2)
{
// Fire the Event that it is time to update
PropertyChanged(this, new PropertyChangedEventArgs("player"));
}
}
}
}
And in the Activity class register again to the event in the constructor:
public Activity()
{
this._Games = new games();
this._Games.PropertyChanged += _Games_PropertyChanged;
this._Sports = new sports();
}
and declare the body of the event handler:
private void _Games_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_Sports != null)
{
_Sports.sub = 10;
}
}
And _Sports.sub will get updated automatically. Hope it helps. There are of course other ways to accomplish this update. It is just the first the came to my mind
You need to create an instance of Activity. You also need to initialize _Sports in it
Activity activity = new Activity();
activity._Sports = new sports();
activity._Sports.sub = 10;
Or using object tantalizer
Activity activity = new Activity
{
_Sports = new sports()
};
activity._Sports.sub = 10;
By the way, Activity is not parent class of sports. Activity holds sports object as a member. In your example PropertyChangedBase is parent class of games.
Is it possbible to get the Instance of an item, within the CollectionChanged event?
For Example:
public class Foo
{
public string Name { get; set; }
public ObservableCollection<Bar> Bars { get; set; }
public Foo()
{
Bars += HelperFoo.Bars_CollectionChanged;
}
}
public class Bar
{
public string Name { get; set; }
}
public static class HelperFoo
{
public static voic Bars_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//sender is the collection Foo.Bars
//can I get the Instance of Foo?
}
}
(I wouldnt mind using reflection)
If this isnt possible is there a way to get the Instance of object that intializes an other object?
For Example:
public class Foo
{
public string Name { get; set; }
public Foo()
{
var bar = new Bar(); //yes I know, I could write new Bar(this) and provide an overload for this
}
}
public class Bar
{
public string Name { get; set; }
public Bar()
{
//Get the Foo, since the constructor is called within Foo, is this possible?
//without providing an overload that takes an object, and just change it to `(new Bar(this))`
}
}
I agree with #Gaz but if you are really wanting to do what you describe then add a Dictionary to your HelperFoo class. Then in your Foo class add this as the creator as follows.
public static class HelperFoo
{
private static Dictionary<ObservableCollection<Bar>, object> lookupCreators = new Dictionary<ObservableCollection<Bar>, object>();
public static void AddBarsCreator(ObservableCollection<Bar> bars, object creator)
{
lookupCreators.Add(bars, creator);
}
public static void Bars_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ObservableCollection<Bar> bars = (ObservableCollection<Bar>)sender;
if (lookupCreators.ContainsKey(bars))
{
}
}
}
public class Foo
{
public ObservableCollection<Bar> Bars { get; set; }
public Foo()
{
Bars = new ObservableCollection<Bar>();
HelperFoo.AddBarsCreator(Bars, this);
Bars.CollectionChanged += HelperFoo.Bars_CollectionChanged;
}
}
Your code seems rather oddly structured.
If your HelperFoo class needs to do something with Foo, then pass it Foo, and let it subscribe to Bar events itself.
If HelperFoo shouldn't know anything about Bars, then expose an event on Foo instead and subscribe to that. You can raise then event inside Foo when Bars changes.
Have a read up on the Law Of Demeter.