I have an Item class in C# and I want to have two methods, OnRightClick and OnLeftClick, but I want every item to do different things upon using them (I am on Unity and this class is not a monoBehavior).
I heard about virtual methods, but from what I realized they can be overridden only from other classes that inherit them. However, I want to do without making a separate class for every item I make. How can I make those 2 methods vary?
I thought of using delegate, but it doesn't work the way I expected it to either. Is it even possible?
EDIT
(Ik the following line are not a thing but this is the best way I can somehow explain what I want to do)
Imagine having the following simple class Item
public class Item
{
private string name;
private float weight;
private int price;
private bool dropable;
public Item(string name, float weight, int price, bool dropable)
{
this.name = name;
this.weight = weight;
this.price = price;
this.dropable = dropable;
}
public Item(string name, float weight, int price)
{
this.name = name;
this.weight = weight;
this.price = price;
this.dropable = true;
}
public string GetName()
{
return this.name;
}
public float GetWeight()
{
return this.weight;
}
public int GetPrice()
{
return this.price;
}
public bool GetDropable()
{
return this.dropable;
}
public void SetDropable(bool dropable)
{
this.dropable = dropable;
}
}
I want to be able to make an OnRightClick and OnLeftClick that would vary from every item I create if I could do something like(As I said I know it's not valid but this is the best way I can explain it, also didn't mention it in the class above, this is the original class I currently have)
Item item1 = new Item(*all the stuff here*);
Item item2 = new Item(*other stuff*);
item1.OnRightClick = delegate {*what I want on right click*};
item1.OnRightClick(); //executes the function for item1
item2.OnRightClick = delegate {*other stuff on right click*};
item2.OnRightClick(); //executes a different function for item2
Again, this is not a valid code but I just used this to try and explain what I want to try and do, and to ask if there is any solutions to this that exist. In the worst case, if there aren't, I could just use virtual but I'd like that to be my last case of no choice.
You can have a parent Item class then different children item classes that inherit from it the syntax it
public class MyItem : MonoBehaviour {
public enum ItemType {HEALING, OFFENSIVE, CONSUMABLE, EQUIPMENT}
public ItemType itemType;
public float potency;
public MyItem(ItemType _it, float _potency) {
itemType = _it;
potency = _potency;
}
public void OnRightClick() {
switch (itemType) {
case ItemType.HEALING:
HealCharacter(character, potency);
break;
case ItemType.OFFENSIVE:
break;
// more cases
}
}
public void OnLeftClick() {
//fill in like onrightclick
}
}
You can use Action and Func to do what you are asking.
https://learn.microsoft.com/en-us/dotnet/api/system.action-1?view=netcore-3.1
https://learn.microsoft.com/en-us/dotnet/api/system.func-2?view=netcore-3.1
public enum ItemType
{
Sword,
Shield,
Potion
}
public class Item
{
private readonly Action leftClickHandler;
private readonly Action<int> leftClickHandlerWithParam;
private readonly Action rightClickHandler;
private readonly Action<int> rightClickHandlerWithParam;
public Item(ItemType itemType)
{
switch (itemType)
{
case ItemType.Potion:
this.rightClickHandler = this.HandleClickWithFlash;
this.leftClickHandler = this.HandleClickWithSound;
this.leftClickHandlerWithParam = this.HandleClickWithFlash;
this.rightClickHandlerWithParam = this.HandleClickWithSound;
break;
}
}
public void HandleLeftClick()
{
this.leftClickHandler();
}
public void HandleRightClick()
{
this.rightClickHandler();
}
private void HandleClickWithFlash()
{
// Logic here.
}
private void HandleClickWithFlash(int parameter)
{
// Logic here.
}
private void HandleClickWithSound()
{
// Logic here.
}
private void HandleClickWithSound(int parameter)
{
// Logic here.
}
}
Here it is with the items exposed, if say you wanted a item factory concept.
public class ItemSettableHandlers
{
public ItemSettableHandlers()
{
}
public Action LeftClickHandler { get; set; }
public Action RightClickHandler { get; set; }
public void HandleLeftClick()
{
this.LeftClickHandler?.Invoke();
}
public void HandleRightClick()
{
this.RightClickHandler?.Invoke();
}
}
public class ItemCreator
{
public void CreateItems()
{
var itemSword = new ItemSettableHandlers();
itemSword.LeftClickHandler = () =>
{
// Do sword left click here.
};
itemSword.RightClickHandler = () =>
{
// Do sword right click here.
};
var itemShield = new ItemSettableHandlers();
itemShield.LeftClickHandler = () =>
{
// Do shield left click here.
};
itemShield.RightClickHandler = () =>
{
// Do shield right click here.
};
}
}
You could use EventHandler:
in you class, you define:
public event EventHandler RightClick;
public void OnRightClick()
{
EventHandler handler = RightClick;
if (null != handler) handler(this, EventArgs.Empty);
}
You use like that:
// Enable Event
RightClick += new EventHandler(OnRightClick);
OnRightClick();
public void OnRightClick(object s, EventArgs e)
{
}
Related
Well, my issue here is basically what it says in the title. I'm trying to call my bool value of my Player2 class for my Tic Tac Toe-project we have in school. I think it's worth mentioning that I use "Player Player1, Player2;" in the beginning of Form1.cs to create two instances of my class, Player. I've read multiple posts on the internet about this but all of them are people trying to call in more parameters than that they are providing. I don't see how a bool value of true or false is more than one.
Thanks in advance.
One of my buttons where this problem appears.
public void Btn1_Click(object sender, EventArgs e) >{
{
if (click1 == 0)
{
if (Player2.GetActive(true))//(turn == 0)
{
Btn1.Text = "X";
}
else
{
>Btn1.Text = "O";
}
//turn++;
click1++;
}
else
{
Btn1.Text = Btn1.Text;
}
display();
checkit();
}
}
This is my player class.
` public class Player
{
//Characteristics
string name;
int points;
bool Active;
//Constructor
public Player() { points = 0; Active = true; }
//Methods
public void SetName(string n) { name = n; }
public string GetName() { return name; }
public void SetPoints(int p) { points = p; }
public int GetPoints() { return points; }
public void SetActive(bool a) { Active = a; }
public bool GetActive() { return Active; }`
You have the code:
Player2.GetActive(true)
But you define get active as
public bool GetActive() { return Active; }`
So it is correct you have not defined a GetActive with a parameter.
Here,
if(Player2.GetActive(true))
you are passing an extra argument (true) to the method GetActive. As we can see from the method declaration of GetActive, it takes no parameters:
public bool GetActive() { return Active; }
// ↑
// empty parentheses
I think what you mean here is "if Player2.GetActive() is true..." right? You don't need to specify the value you want if it is true, just doing this is fine:
if (Player2.GetActive())
If you want to check if it is false, add ! before the call to negate the result:
if (!Player2.GetActive())
Like BugFinder said, you are using the method to get the Active-Value instead of using the method to set the value.
So change
Player2.GetActive(true)
to
Player2.SetActive(true)
Just as an addition:
Since we are working with C# here and many of your methods are called set and get, I suggest you change those methods to be properties:
public string Name
{
get { return name; }
set { name = value; }
}
public int Points
{
get { return points; }
set { points = value; }
}
public bool Active
{
get { return active; }
set { active = value; }
}
Now when you want to set the value, you can type
Player2.IsActive = true;
and to check the value simple type code like
If(Player2.IsActive)
//Do Stuff
I have trying to build a simulator of Ingenico POS terminal (iWL220).
The main screen I have a combo-box. Once user enter id and password the combo-box load 6 menus. If User click btn1 then combo-box clear the menu and add another set of menu. If user click btn1 for new loaded menu then again combo-box cleared and load another set of menu so on.
My problem is for each button click (btn1, btn2, btn3, btn4, btn5) I have to code lot of if else statement. Example;
First menu (on combo-box) has 6 sector.
1.SectorA
2.SectorB
3.SectorC
4.SectorD
5.SectorE
6.SectorF
If user choose 1.SectorA then user click btn1. Then btn1 clear the combo-box and loads another set of menu. This time menu (on combo-box) has 3 companies.
1.CompanyA
2.CompanyB
3.CompanyC
This time user choose 1.CompanyA then user click again btn1. Then btn1 clear the combo-box and loads another set of menu. This time menu (on combo-box) has 2 payment option.
1.FullPayment
2.ParitalPayment
Now this time if user click btn1 or btn2 the combo-box visible become false and in main screen there is a label and text box. The text box allows user to enter the subscriber number and press enter (green button).
I already load Ingenico terminal picture as jpeg and top of it I set my buttons.
I gave only small version of my simulation. In my app there are 114 probability that user can choose.
In my app btn1 has 92 probability to be clicked, btn2 has 53 probability to be clicked and so on. After user enters the subscriber number and click green button the my app use wfc services to format the data and send to sql server.
But before user click each combination of button some where in my app I store the btn number as 422. This 422 means, user chose SectorD + CompanyB + ParitalPayment option. So my wfc will know what it is mean 422.
My question is what is the shortest way to construct my button events for this 114 probability case?
I have 4 buttons. Btn1, Btn2, Btn3 and Btn4. Also I have some arrays as it shown below and 1 combo-box.
1.ArrayMain() = {“1.Water”,”2.Air”,”3.Soil”,”4.Fire”}
1.1. ArrayWater() = {“1.Salty”,”2.Fresh”, “3.Contaminated”}
1.1.1.ArraySalty() = {1.”AA”, 2.”BB”, 3.”CC”}
1.1.2.ArrayFresh() = {1.”DD”, 2.”EE”, 3.”FF”}
1.1.3.ArrayContaminated() = {1.”XX”, 2.”YY”, 3.”ZZ”}
1.2 ArrayAir() = {“1.Fresh”, “2.Contaminated”}
1.3 ArraySoil() = {“1.Normal”, “2.Contaminated”}
1.4 ArrayFire() = {“1.Low”,”2.Mid”,”3.High”}
When my app starts, first array values 1.(ArrayMain) fills the comboBox. This comboBox will have 4 values as, “1.Water”, ”2.Air”, ”3.Soil”, ”4.Fire” in it. If user choose “1.Water” than user clicks Btn1. Than btn1 events clears the comboBox and loads 1.1ArrayWater() values into comboBox.
Second time if user chooses “1.Salty” than user clicks again btn1 and this time btn1 events clears the comboBox and loads 1.1.1ArraySalty() values into comboBox.
Third time if user chooses “2.BB” than user clicks Btn2 and sends the information “BB” for calculation.
First you have 5 (more or less) menu item and each time you press any (number) buttons (1 to 9 lilke in pos terminal) pressed than new menu appears on the screen.
Each button at any specific time shall execute some specific action depending on the state of the system. Obviously, if you try to decide the specific action depending on the multitude of different variables, you will create a lot of branching code. Such code is very difficult to write correctly and even more difficult to debug and maintain.
So, what if we encapsulate current action for each possible state(sequence of state) in some specific class (interface):
/// <summary>
/// Represents internal terminal presenter that is used inside IGlobalTerminalPresenter.
/// </summary>
public interface ITerminalPresenter
{
void UpdateUI();
ITerminalPresenter this[Int32 index]
{
get;
}
ITerminalPresenter Do1();
ITerminalPresenter Do2();
ITerminalPresenter Parent
{
get;
set;
}
void Reset();
}
Inside the form we will use field of a similar interface that will encapsulate all changes of the presenter.
/// <summary>
/// Represents terminal presenter that UI can operate upon.
/// </summary>
public interface IGlobalTerminalPresenter
{
void UpdateUI();
void Do1();
void Do2();
Int32 SelectedIndex
{
get;
set;
}
void Reset();
}
Our event handlers will become:
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
var senderComboBox = (ComboBox)sender;
this.globalTerminalPresenter.SelectedIndex = senderComboBox.SelectedIndex;
}
private void button1_Click(object sender, EventArgs e)
{
this.globalTerminalPresenter.Do1();
}
private void button2_Click(object sender, EventArgs e)
{
this.globalTerminalPresenter.Do2();
}
To allow our concrete TerminalPresenters to interoperate with form we will force our form to implement the following interface:
/// <summary>
/// This represents your UI in technology-independent manner
/// </summary>
public interface ITerminalView
{
String Title { get; set; }
String Input { get; set; }
String Output { get; set; }
String Button1_Text { get; set; }
String Button2_Text { get; set; }
IEnumerable<String> SelectionItems { get; set; }
void Clear();
}
public partial class MainForm : Form,
ITerminalView
{
...
#region ITerminalView implementation
public string Title
{
get { return this.Text; }
set { this.Text = value; }
}
public String Button1_Text
{
get { return this.button1.Text; }
set { this.button1.Text = value; }
}
public String Button2_Text
{
get { return this.button2.Text; }
set { this.button2.Text = value; }
}
public string Input
{
get { return this.textBox_Input.Text; }
set { this.textBox_Input.Text = value; }
}
public string Output
{
get { return this.textBox_Output.Text; }
set { this.textBox_Output.Text = value; }
}
public IEnumerable<string> SelectionItems
{
get { return this.comboBox.Items.Cast<String>(); }
set
{
this.comboBox.Items.Clear();
if (value == null)
return;
foreach (var item in value)
{
this.comboBox.Items.Add(item);
}
}
}
public void Clear()
{
this.comboBox.SelectedIndex = -1;
this.Title = String.Empty;
this.Input = String.Empty;
this.Output = String.Empty;
this.SelectionItems = null;
}
#endregion
For now we will create two TerminalPresenters - one to just allow selection of next option through combobox, one that calculates sum of two numbers. Both of them use the same base class.
/// <summary>
/// Base class for all presenters
/// </summary>
public abstract class TerminalPresenterBase : ITerminalPresenter
{
protected ITerminalView view;
public TerminalPresenterBase(ITerminalView view)
{
if (view == null)
throw new ArgumentNullException("view");
this.view = view;
this.Parent = this;
}
public abstract void UpdateUI();
public abstract ITerminalPresenter this[int index]
{
get;
}
public abstract ITerminalPresenter Do1();
public abstract ITerminalPresenter Do2();
public virtual ITerminalPresenter Parent
{
get;
set;
}
public virtual void Reset()
{
this.UpdateUI();
}
}
/// <summary>
/// Presenter whose sole goal is to allow user to select some other option and press next
/// </summary>
public class SelectOptionPresenter : TerminalPresenterBase
{
private IList<KeyValuePair<String, ITerminalPresenter>> options;
private ITerminalPresenter selected;
private String title;
public SelectOptionPresenter(ITerminalView view,
String title,
IList<KeyValuePair<String, ITerminalPresenter>> options)
: base(view)
{
if (options == null)
throw new ArgumentNullException("options");
this.title = title;
this.options = options;
foreach (var item in options)
{
item.Value.Parent = this;
}
}
public override void UpdateUI()
{
this.view.Clear();
this.view.Button1_Text = "Confirm selection";
this.view.Button2_Text = "Go back";
this.view.Title = title;
this.view.SelectionItems = options
.Select(opt => opt.Key);
}
public override ITerminalPresenter this[int index]
{
get
{
this.selected = this.options[index].Value;
return this;
}
}
public override ITerminalPresenter Do1()
{
return this.ConfirmSelection();
}
public override ITerminalPresenter Do2()
{
return this.GoBack();
}
public ITerminalPresenter ConfirmSelection()
{
this.selected.UpdateUI();
return this.selected;
}
public ITerminalPresenter GoBack()
{
this.Parent.UpdateUI();
return this.Parent;
}
}
public enum APlusBState
{
EnterA,
EnterB,
Result
}
public class StepActions
{
public Action UpdateUI { get; set; }
public Func<ITerminalPresenter> Do1 { get; set; }
public Func<ITerminalPresenter> Do2 { get; set; }
}
public class APlusBPresenter : TerminalPresenterBase
{
private Int32 a, b;
private APlusBState state;
private String error = null;
private Dictionary<APlusBState, StepActions> stateActions;
private void InitializeStateActions()
{
this.stateActions = new Dictionary<APlusBState, StepActions>();
this.stateActions.Add(APlusBState.EnterA,
new StepActions()
{
UpdateUI = () =>
{
this.view.Title = this.error ?? "Enter A";
this.view.Input = this.a.ToString();
this.view.Button1_Text = "Confirm A";
this.view.Button2_Text = "Exit";
},
Do1 = () => // Confirm A
{
if (!Int32.TryParse(this.view.Input, out this.a))
{
this.error = "A is in incorrect format. Enter A again";
return this;
}
this.error = null;
this.state = APlusBState.EnterB;
return this;
},
Do2 = () => // Exit
{
this.Reset();
return this.Parent;
}
});
this.stateActions.Add(APlusBState.EnterB,
new StepActions()
{
UpdateUI = () =>
{
this.view.Title = this.error ?? "Enter B";
this.view.Input = this.b.ToString();
this.view.Button1_Text = "Confirm B";
this.view.Button2_Text = "Back to A";
},
Do1 = () => // Confirm B
{
if (!Int32.TryParse(this.view.Input, out this.b))
{
this.error = "B is in incorrect format. Enter B again";
return this;
}
this.error = null;
this.state = APlusBState.Result;
return this;
},
Do2 = () => // Back to a
{
this.state = APlusBState.EnterA;
return this;
}
});
this.stateActions.Add(APlusBState.Result,
new StepActions()
{
UpdateUI = () =>
{
this.view.Title = String.Format("The result of {0} + {1}", this.a, this.b);
this.view.Output = (this.a + this.b).ToString();
this.view.Button1_Text = "Exit";
this.view.Button2_Text = "Back";
},
Do1 = () => // Exit
{
this.Reset();
return this.Parent;
},
Do2 = () => // Back to B
{
this.state = APlusBState.EnterB;
return this;
}
});
}
public APlusBPresenter(ITerminalView view) : base(view)
{
this.InitializeStateActions();
this.Reset();
}
public override void UpdateUI()
{
this.view.Clear();
this.stateActions[this.state].UpdateUI();
}
public override ITerminalPresenter this[int index]
{
get { throw new NotImplementedException(); }
}
public override ITerminalPresenter Do1()
{
var nextPresenter = this.stateActions[this.state].Do1();
nextPresenter.UpdateUI();
return nextPresenter;
}
public override ITerminalPresenter Do2()
{
var nextPresenter = this.stateActions[this.state].Do2();
nextPresenter.UpdateUI();
return nextPresenter;
}
public override void Reset()
{
this.state = APlusBState.EnterA;
this.a = 0;
this.b = 0;
this.error = null;
}
}
/// <summary>
/// Represents terminal presenter to use inside GUI. It handles current ISpecificTerminalPresenter inside itself.
/// </summary>
public class GlobalTerminalPresenter : IGlobalTerminalPresenter
{
#region Fields
private ITerminalPresenter current;
private Int32 selectedIndex;
#endregion
#region Constructors
public GlobalTerminalPresenter(ITerminalPresenter mainPresenter)
{
if (mainPresenter == null)
throw new ArgumentNullException("mainPresenter");
this.current = mainPresenter;
this.UpdateUI();
}
#endregion
public void UpdateUI()
{
this.current.UpdateUI();
}
public void Do1()
{
this.current = this.current.Do1();
}
public void Do2()
{
this.current = this.current.Do2();
}
public Int32 SelectedIndex
{
get
{
return this.selectedIndex;
}
set
{
this.selectedIndex = value;
if (value == -1)
return;
this.current = this.current[value];
}
}
public void Reset()
{
this.current.Reset();
}
}
Then we initialize them in the constructor of our form:
public partial class MainForm : Form,
ITerminalView
{
private IGlobalTerminalPresenter globalTerminalPresenter;
public MainForm()
{
InitializeComponent();
var nextLevelPresenters = new KeyValuePair<String, ITerminalPresenter>[]
{
new KeyValuePair<String, ITerminalPresenter>(
"A plus B",
new APlusBPresenter(this)),
new KeyValuePair<String, ITerminalPresenter>(
"Just empty selector",
new SelectOptionPresenter(this,
"Selector with no selection choices",
Enumerable
.Empty<KeyValuePair<String, ITerminalPresenter>>()
.ToArray()))
};
var topPresenter = new SelectOptionPresenter(this, "Select the option and press the confirm button", nextLevelPresenters);
this.globalTerminalPresenter = new GlobalTerminalPresenter(topPresenter);
}
P.S.1: These code snippets assume that you have form named MainForm that has two buttons - button1, button2, one combobox, two textBoxes - textBox_Input, textBox_Output.
P.S.2: The pattern used is close enough to Model-View-Presenter, just without DataBindings.
P.S.3 You can create more or less generic state machine Presenters if you modify APlusBPresenter code. Or try to shape up ChainXxxx... classes and interfaces.
P.S.4: And sorry for these walls of code. That's probably too much for [SO] format, so I've put ad-hoc proof of concept at GitHub - https://github.com/Podskal/StackOverflow_29870164.git. It is ugly in many aspects, but as it is, it can at least give few ideas about how to implement your own system.
P.S.5: There are a lot of problematic places in this code, so you should very carefully consider how you will build your own system from it.
I have two classes that inherit from the same abstract class. I want both of them or at least one to be aware of changes in a specific property of the other. Is there any simple method for doing this? I've been trying to move the variable to the parent class, but that just creates 2 of the same variable, and when I create a reference to the other class inside the first one the same thing happens. thanks.
This is what my code looks like:
public abstract class Animal
{
public int MovementSpeed;
public bool Death;
public string Feedback;
public bool DeerCaught;
public int tiredRate;
public virtual int Movement()
{
MovementSpeed = MovementSpeed - tiredRate;
return MovementSpeed;
}
public virtual string Print()
{
return Feedback;
}
}
public class Deer : Animal
{
public string hidden;
public string Foraging;
public int DeerCount;
public Deer()
{
this.DeerCount = 10;
this.DeerCaught = false;
this.MovementSpeed = 10;
this.tiredRate = 2;
}
public void Hide()
{
if (Hunting)
{
Feedback = "The deer is hiding.";
if (DeerCount > 0)
{
Print();
}
}
else
{
//Forage();
}
}
public void Forage()
{
if (!Hunting)
{
Feedback = "The deer is searching for food.";
if (DeerCount > 0)
{
Print();
}
}
else
{
//Hide();
}
}
}
public class Wolf : Animal
{
public int Hunger;
public bool Hunting;
public Wolf()
{
this.Hunting = false;
this.Hunger = 10;
this.MovementSpeed = 10;
this.tiredRate = 1;
}
public bool Hunt()
{
if (Hunger < 5)
{
Hunting = true;
Feedback = "The wolf is searching for his next meal.";
if (DeerCaught == true)
{
Hunger++;
}
else
{
Hunger--;
}
return Hunting;
}
else
{
Hunting = false;
Feedback = "The wolf decides to rest.";
Hunger--;
return Hunting;
}
}
public void Die()
{
if (Hunger < 0)
{
Death = true;
Feedback = "The wolf has lost the hunt.";
}
}
}
I've tried setting Hunting as static in the base class, but I just end up getting two different versions of 'Hunting' when I run the methods of each class.
If this is intended as a simulation, then Deer isn't told when a wolf is hunting, it has to find out. The analogue here is to have some way that the Deer can query about the presence of wolves (something like Deer.LookForWolves(), then to check the value of the Hunting property on each wolf. This will require some sort of controller class, representing the world.
class World
{
public static List<Animal> Animals = new List<Animal>();
//...
}
class Deer : Animal
{
//...
bool IsSafe()
{
return LookForWolves().All(wolf => !wolf.Hunting);
}
List<Wolf> LookForWolves()
{
return World.Animals.OfType<Wolf>();
}
//...
Alternatively, you could reference World as a member of each Animal, passed in via the constructor. It's up to you, and will depend on whether you need to have multiple World objects, each with a different list of Animals.
Something like implementing INotifyPropertyChanged could help:
First, declare some classes that implement INotifyPropertyChanged:
abstract class Base {
}
class ClassA : Base, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string ClassAProperty {
get {
return _property;
}
set {
_property = value;
PropertyChanged(this, new PropertyChangedEventArgs("ClassAProperty"));
}
}
}
class ClassB : Base, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string ClassBProperty {
get {
return _property;
}
set {
_property = value;
PropertyChanged(this, new PropertyChangedEventArgs("ClassBProperty"));
}
}
}
Then, wire up new instances to subscribe to the PropertyChanged event:
using System.ComponentModel;
static void Main(string[] args) {
ClassA a = new ClassA();
a.PropertyChanged += PropertyChanged;
a.ClassAProperty = "Default value";
ClassB b = new ClassB();
b.PropertyChanged += PropertyChanged;
b.ClassBProperty = "Default value";
b.ClassBProperty = "new value in B";
a.ClassAProperty = "new value in A";
Console.Read();
}
static void PropertyChanged(object sender, PropertyChangedEventArgs e) {
Console.WriteLine("Property {0} on object {1} was changed, the value is \"{2}\"", e.PropertyName, sender.GetType().Name, sender.GetType().GetProperty(e.PropertyName).GetValue(sender));
}
Output of this is:
Property ClassAProperty on object ClassA was changed, the value is "Default value"
Property ClassBProperty on object ClassB was changed, the value is "Default value"
Property ClassBProperty on object ClassB was changed, the value is "new value in B"
Property ClassAProperty on object ClassA was changed, the value is "new value in A"
Each time either property is set, PropertyChanged is called, which in the above example writes the details to the console.
In your use case, you would have the event call a method in the other class (if I understand you correctly).
A very basic way to notify property changed with your own delegate definition. Since you do not provide any code I made up some classes myself. Use this as an example to modify your own code:
public delegate void PropertyChangedEventHandler();
public abstract class Base
{
}
public class A : Base
{
public event PropertyChangedEventHandler PropertyChanged;
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
if (PropertyChanged != null)
{
PropertyChanged();
}
}
}
public class B : Base
{
private A _a;
public B(A a)
{
_a = a;
a.PropertyChanged += new PropertyChangedEventHandler(a_PropertyChanged);
}
private void a_PropertyChanged()
{
Console.WriteLine(_a.Value);
}
}
public class Application()
{
public void DoStuff()
{
var a = new A();
var b = new B(a);
}
}
The basic idea is to pass a reference of one object to the other. For example tell the deer it is being hunted by the wolf:
public class Wolf : Animal
{
public void Hunt(Deer deer)
{
deer.SetHunter(this);
}
}
Now the deer can check whether a wolf is hunting it:
public class Deer : Animal
{
Wolf _hunter;
public void SetHunter(Wolf wolf)
{
_hunter = wolf;
}
public void Hide()
{
if (_hunter != null)
{
Feedback = "The deer is hiding.";
}
else
{
//Forage();
}
}
}
This can be improved to be more generic, but it's the basic idea of passing a reference of one object to the other.
Don't use public fields for the properties of your classes. This way you will never be aware of changes and therefore can not notify others. Put the public fields into properties and always use these properties to change the value even from inside the Animal class. The property setter can then be used to notify others of changes.
public abstract class Animal
{
private int _movementSpeed;
public int MovementSpeed
{
get
{
return _movementSpeed;
}
set
{
if (_movementSpeed != value)
{
_movementSpeed = value;
OnMovementSpeedChanged();
}
}
}
protected virtual void OnMovementSpeedChanged()
{
// Derived classes can override this method.
// It will be called each time MovementSpeed changes.
}
public virtual int Movement()
{
// always use the property to change the value
// otherwise OnMovementSpeedChanged would never be called
MovementSpeed -= tiredRate;
return MovementSpeed;
}
}
Like others already mentioned you can also implement INotifyPropertyChanged in your base class. Since this uses events for notification not only derived classes can use that but also any other object that has a reference to an animal. The approach is basically the same. Each time the property value changes you call a method that fires the event. Any other object can then handle that event.
public abstract class Animal : INotifyPropertyChanged
{
private int _movementSpeed;
public int MovementSpeed
{
get
{
return _movementSpeed;
}
set
{
if (_movementSpeed != value)
{
_movementSpeed = value;
// call this method each time a property changes
OnPropertyChanged(new PropertyChangedEventArgs("MovementSpeed"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
// always implement events like this
// -> check if the event handler is not null, then fire it
if (PropertyChanged != null)
{
PropertyChanged(this, args);
}
}
}
A class that wants to handle the event can do it like so:
public class AnyClass
{
public AnyClass(Animal anAnimal)
{
TheAnimal = anAnimal;
anAnimal += Animal_PropertyChanged;
}
public Animal TheAnimal { get; private set; }
private void Animal_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "MovementSpeed")
{
Console.WriteLine("MovementSpeed changed");
}
}
}
Derived classes however don't need to handle the event. Since the OnPropertyChanged method is declared as protected virtual they can just override it.
public class Deer : Animal
{
protected override void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == "MovementSpeed")
{
Console.WriteLine("MovementSpeed changed");
}
// don't forget to call the base class otherwise the event will never get fired
base.OnPropertyChanged(args);
}
}
I've been looking all over the web for example implementations of an MVC setup in .NET. I have found many examples but they all seem to differ in certain aspects. I have a book on Design Patterns that described that MVC was originated in Smalltalk so I read over several people discussing its implementation in that language. What follows is an example project I wrote utilizing what I gathered was a proper implementation but I've been confused by a few details.
One problem I run into is the proper order of the construction of the objects. Here is the impl in my Program.cs
Model mdl = new Model();
Controller ctrl = new Controller(mdl);
Application.Run(new Form1(ctrl, mdl));
The View:
Immediately I have a couple of issues I'm unsure of. First, if the view is supposed to only read data from the Model for updating, but contains a reference to it, what is stopping me from making the calls the controller does to the model from the view? Should a programmer just ignore the fact that they are exposed to the models member functions? Another thought I had, was perhaps the event that informs the view the model is updated, would send some sort of state object for the view to update itself with.
public interface IView
{
double TopSpeed { get; }
double ZeroTo60 { get; }
int VehicleID { get; }
string VehicleName { get; }
}
/// <summary>
/// Assume the form has the following controls
/// A button with a click event OnSaveClicked
/// A combobox with a selected index changed event OnSelectedIndexChanged
/// A textbox that displays the vehicles top speed named mTextTopSpeed
/// A textbox that displays the vehicles zero to 60 time named mTextZeroTo60
/// </summary>
public partial class Form1 : Form, IView
{
private IController mController;
private IModel mModel;
public Form1(IController controller, IModel model)
{
InitializeComponent();
mController = controller;
mController.SetListener(this);
mModel = model;
mModel.ModelChanged += new ModelUpdated(mModel_ModelChanged);
}
void mModel_ModelChanged(object sender, EventArgs e)
{
mTextTopSpeed.Text = mModel.TopSpeed.ToString();
mTextZeroTo60.Text = mModel.ZeroTo60.ToString();
}
public double TopSpeed { get { return Double.Parse(mTextTopSpeed.Text); } }
public double ZeroTo60 { get { return Double.Parse(mTextZeroTo60.Text); } }
public int VehicleID { get { return (int)mComboVehicles.SelectedValue; } }
public string VehicleName { get { return mComboVehicles.SelectedText; } }
#region Form Events
private void OnFormLoad(object sender, EventArgs e)
{
mComboVehicles.ValueMember = "Key";
mComboVehicles.DisplayMember = "Value";
mComboVehicles.DataSource = new BindingSource(mModel.VehicleList, null);
}
private void OnSelectedIndexChanged(object sender, EventArgs e)
{
mController.OnSelectedVehicleChanged();
}
private void OnSaveClicked(object sender, EventArgs e)
{
mController.OnUpdateVehicle();
}
#endregion
}
The Controller:
My only real issue with the way I have implemented the controller is that it seems a bit odd to me that is possible to construct the controller without definitely having a view assigned to it. I could ignore the view entirely but that would mean I would pass parameters to the controller's functions for updating the model which seems to miss the point entirely.
public interface IController
{
void OnUpdateVehicle();
void OnSelectedVehicleChanged();
void SetListener(IView view);
}
class Controller : IController
{
private IModel mModel;
private IView mView = null;
public Controller(IModel model)
{
mModel = model;
}
public void OnUpdateVehicle()
{
if(mView == null)
return;
mModel.UpdateVehicle(mView.VehicleID, mView.TopSpeed, mView.ZeroTo60);
}
public void SetListener(IView view)
{
mView = view;
}
public void OnSelectedVehicleChanged()
{
if (mView == null)
return;
mModel.SelectVehicle(mView.VehicleID);
}
}
The Model:
In my form, I have a combobox that is a list of the vehicles given in my pseudo database. I feel as though my form should actually implement multiple Views / Models because of this. A view specific to listing the possible vehicles with a corresponding controller / model, and a view for displaying information about the selected vehicle with its own controller / model.
public delegate void ModelUpdated(object sender, EventArgs e);
public interface IModel
{
event ModelUpdated ModelChanged;
void UpdateVehicle(int id, double topSpeed, double zeroTo60);
void SelectVehicle(int id);
double TopSpeed { get; }
double ZeroTo60 { get; }
IDictionary<int, string> VehicleList { get; }
}
// class for the sake of a pseudo database object
class Vehicle
{
public int ID { get; set; }
public string Name { get; set; }
public double TopSpeed { get; set; }
public double ZeroTo60 { get; set; }
public Vehicle(int id, string name, double topSpeed, double zeroTo60)
{
ID = id;
Name = name;
TopSpeed = topSpeed;
ZeroTo60 = zeroTo60;
}
}
class Model : IModel
{
private List<Vehicle> mVehicles = new List<Vehicle>()
{
new Vehicle(1, "Civic", 120.0, 5.0),
new Vehicle(2, "Batmobile", 9000.0, 1.0),
new Vehicle(3, "Tricycle", 5.0, 0.0)
};
private Vehicle mCurrentVehicle;
public Model()
{
mCurrentVehicle = mVehicles[0];
}
public event ModelUpdated ModelChanged;
public void OnModelChanged()
{
if (ModelChanged != null)
{
ModelChanged(this, new EventArgs());
}
}
public double TopSpeed { get { return mCurrentVehicle.TopSpeed; } }
public double ZeroTo60 { get { return mCurrentVehicle.ZeroTo60; } }
public IDictionary<int, string> VehicleList
{
get
{
Dictionary<int, string> vDict = new Dictionary<int, string>();
foreach (Vehicle v in mVehicles)
{
vDict.Add(v.ID, v.Name);
}
return vDict as IDictionary<int, string>;
}
}
#region Pseudo Database Calls
public void SelectVehicle(int id)
{
foreach (Vehicle v in mVehicles)
{
if (v.ID == id)
{
mCurrentVehicle = v;
OnModelChanged(); // send notification to registered views
break;
}
}
}
public void UpdateVehicle(int id, double topSpeed, double zeroTo60)
{
foreach (Vehicle v in mVehicles)
{
if (v.ID == id)
{
mCurrentVehicle.TopSpeed = topSpeed;
mCurrentVehicle.ZeroTo60 = zeroTo60;
OnModelChanged(); // send notification to registered views
break;
}
}
}
#endregion
}
In Conclusion of this tl;dr, I guess what I'm looking for, is some guidance on whether or not what I'm doing here represents a true MVC implementation and maybe for someone to shed some light on the aforementioned concerns. Any advice would be greatly appreciated.
We'll it depends on what you want to do. You currently have an implementation of the Supervising Controller. If you wish to remove the model from the view (and any databinding), you can implement a Passive View pattern instead. See this article for more differences.
(source: microsoft.com)
And Martin Fowler is king (GUI Architectures).
Ok so I'm just making a mock MVC app to get a handle on how things work
my model has some string in it (and more, but don't care at the moment)
my GUI has a bunch of radiobuttons and here is what I do
private class radioButtonEvent : Model.EventHandling.XObjectEvent{
private List<RadioButton> myList;
public radioButtonEvent() { }
public override void execute(){
foreach (RadioButton a in myList){
if (a.Text == ((Model.InfoTree)myObj).Info){
a.Checked = true;
((TabControl)a.Parent.Parent).SelectTab(a.Parent.Name);
}
}
}
public void setRadioList(List<RadioButton> a){
myList = a;
}
}
I create one of these and add a list of all my radiobuttons then make it listen to a certain string, like so:
radioButtonEvent locationEvent = new radioButtonEvent();
List<RadioButton> radioList = new List<RadioButton>();
radioList.Add(Location_Logged_Arena_radioButton);
radioList.Add(Location_Logged_Market_radioButton);
radioList.Add(Location_Logged_Mute_radioButton);
radioList.Add(Location_Logged_Town_radioButton);
radioList.Add(Location_Logged_Vault_radioButton);
radioList.Add(Location_Logging_Home_radioButton);
radioList.Add(Location_Logging_Select_radioButton);
locationEvent.setRadioList(radioList);
myModel.InformationTree.addInfoEvent(locationEvent);
one thing I should point out is that in XObjectEvent there is a method that gets inherited that handles the setting of myobj
now the two ways I've currently thought of going about having listenable strings are these:
Way 1:
private ListenableString lstring = new ListenableString();
public string Info{
get
{ return lstring.Text;}
set
{ lstring.Text = value;}
}
public void addInfoEvent(XEvent ev){
lstring.addEvent(ev);
}
and this uses the following
class ListenableString
{
public ListenableString() { }
private string me = "";
private List<XEvent> Events = new List<XEvent>();
public void addEvent(XEvent ev){
Events.Add(ev);
}
public string Text{
get
{ return me;}
set{
me = value;
foreach (EventHandling.XObjectEvent x in Events){
x.setObject(this);
x.execute();
}
}
}
}
Way 2:
private List<XEvent> infoEvents = new List<XEvent>();
public void addInfoEvent(XEvent ev)
{
infoEvents.Add(ev);
}
private string _Info = "";
public string Info
{
get
{
return _Info;
}
set
{
_Info = value;
foreach (EventHandling.XObjectEvent x in infoEvents)
{
x.setObject(this);
x.execute();
}
}
}
Way 3
got a better way? :)
Ye Olde New Answer
Here are some Stack Overflow questions that deal with implementing MVC in a Windows Form Application:
Implementing MVC with Windows Forms.
How would you implement MVC in a Windows Form Application?
Ye Olde Olde Answer
Use ASP.NET MVC 1.0.