I have 2 forms: Form A and Form B. I also have a property field class.
Form A contains the label I want changed when a property is changed. Form B contains code that will change the property field.
Property Class Code:
public class Controller
{
private static string _customerID;
public static string customerID
{
get { return _customerID; }
set
{
_customerID = value;
if (_customerID != "")
{
FormA.ChangeMe();
}
}
}
}
Form B Code:
private void something_Click(object sender, SomethingEventArgs e) {
Controller.customerID = "Cool";
}
Form A Code:
public static void ChangeMe()
{
var frmA = new FormA();
MessageBox.Show("Test: " + Controller.customerID); //This works! Shows Cool
frmA.lb2Change.Text = Controller.customerID; //This kind of works..
MessageBox.Show("Test2: " + frmA.lb2Change.Text); //This shows the correct value. Shows Cool
}
The property field value is passed (which I know from the MessageBox) however it does not update the value on the form label itself. Why is this? What am I doing wrong? I also believe there is a better alternative for achieving what ChangeMe() method is intended to achieve -- if so are there any suggestions?
You can do the following
To define a delegate
To Implement Property Change Notification
Delegate
public delegate void OnCustomerIDChanging(object sender,CancelEventArgs e);
public delegate void OnCustomerIDChanged(object sender,object value);
public class Controller
{
private static string _customerID;
public event OnCustomerIDChanging CustoerIDChanging;
public event OnCustomerIDChanged CustoerIDChanged;
public static string customerID
{
get { return _customerID; }
set
{
// make sure that the value has a `value` and different from `_customerID`
if(!string.IsNullOrEmpty(value) && _customerID!=value)
{
if(CustomerIDChanging!=null)
{
var state = new CancelEventArgs();
// raise the event before changing and your code might reject the changes maybe due to violation of validation rule or something else
CustomerIDChanging(this,state);
// check if the code was not cancelled by the event from the from A
if(!state.Cancel)
{
// change the value and raise the event Changed
_customerID = value;
if(CustomerIDChanged!=null)
CustomerIDChanged(this,value);
}
}
}
}
}
}
in your Form and when you are initiating the Controller Object
var controller = new Controller();
controller.CustomerIDChanging +=(sd,args) =>{
// here you can test if you want really to change the value or not
// in case you want to reject the changes you can apply
args.Cancel = true;
};
controller.CustomerIDChanged +=(sd,args) =>{
// here you implement the code **Changed already**
}
The above code will give you a great control over your code, also will make your controller code reusable and clean. Same
result you can get by implementing INotifyPropertyChanged interface
INotifyPropertyChanged
you might have a look on this article to get more information
In your static method ChangeMe you are creating a new Form every time, you want to Change the value. Instead of that you want to change the value of an existing form. Therefor your Controller needs an instance of this FormA. Try it like this:
public class Controller
{
//You can pass the form throught the constructor,
//create it in constructor, ...
private FormA frmA;
private string _customerID;
public string customerID
{
get { return _customerID; }
set
{
_customerID = value;
if (_customerID != "")
{
frmA.ChangeMe();
}
}
}
}
Now you donĀ“t need to be static in your FormA:
public void ChangeMe()
{
MessageBox.Show("Test: " + Controller.customerID);
this.lb2Change.Text = Controller.customerID;
}
Related
I have a column class with 2 properties Name and Value, and another class(ColumnList) which inherit it as a collection, I want to raise an event from parent class to child class means change in column value should be triggered in ColumnList class.
below is my code
public class Column
{
private string colName;
private string colValue;
public string Name
{
get
{
return colName;
}
set
{
colName = value;
if (ColumnValueChangedEvent != null)
ColumnValueChangedEvent(null, null);
}
}
public string Value
{
get
{
return colValue;
}
set
{
colValue = value;
if (ColumnValueChangedEvent != null)
ColumnValueChangedEvent(null, null);
}
}
public event ColumnValueChangedDelegate ColumnValueChangedEvent;
public delegate void ColumnValueChangedDelegate(object sender, EventArgs e);
}
public class AddColumn : List<Column>
{
public AddColumn()
: base()
{
}
}
What You Need To do is:
Change The List To ObservableCollection like follows :
public class AddColumn : ObservableCollection<Column>
{
public AddColumn()
: base()
{
}
}
then in your code u can Call it like Follows:
var addColumn = new AddColumn();
addColumn.CollectionChanged += Test_CollectionChanged;
press Tab Vs will generate a function Like Follows:
private static void Test_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// put what ever u need here
}
****this doesn't have to be static ****
Update:
If You Want To change An filed in an item:
For example Lets say you want to change the name field at addColumn[0].
You do it as follows:
var item = addColumn[0];
item.Name = "New VAlue";
addColumn[0] = item.Name;
this code will fire the event.
Note its better to warp it in a function
If I understood you correctly, you want to subscribe to ColumnValueChangedEvent for each added Column item to your AddColumn class?
If yes, it is not possible because List<T> does not provide any mechanism to tell inherited class when item is added to list.
You should create a new type which not inherits from List<T>. It should implement IList<T> or something else depends on your requirements. Then in implementation in the Add(Column column) method just subscribe to events.
We have a click Event that inside calls a previously set Action session variable. The problem is that the changes done in the click event shows fine in the page but the changes done inside the Action method called in the invoke doesn't get shown in the page...
Debugging it seems like the Invoked method is in another context/viewstate for the page controls.
Simplified code example:
public static Action OKFunction {
get => (Action) HttpContext.Current.Session["OKFunction"];
set => HttpContext.Current.Session["OKFunction"] = value;
}
protected void FunctionPrepareCall() {
//in the long version we prepare a javascript dialog with _doPostback and set the desired target function depending on various conditions, here we show only the problematic part, setting the target function
OKFunction = DialogDeleteItemAcepted;
}
protected void ConfirmationDialogOk_Click(object sender, EventArgs e) {
lbMsg.Text = "TestConfirmDialog"; //this value is what it is shown after the page refreshes
OKFunction ? .Invoke(); //calling the target function if its assigned
string currentMsgValue = lbMsg.Text; //right here in debug the value of lbMsg.Text is the one we assigned in this method "TestConfirmDialog";
}
public void DialogDeleteItemAcepted() {
//right here in debug the value of lbMsg.Text appears empty, like it would be in another thread context/viewstate
lbMsg.Text = "TestDialogDeleteItemAcepted"; //in the real case the message text would depend on the result of the delete item operation for example
//right here in debug the value of lbMsg.Text is the one we assigned "TestDialogDeleteItemAcepted";
}
I've created a console application that demonstrates a similar issue:
using System;
namespace PlayAreaCSCon
{
internal class Program
{
public static void Main(string[] args)
{
var s = new Session();
var c1 = new Page(1, s);
c1.SetAction();
c1 = null;
var c2 = new Page(2, s);
c2.InvokeAction();
Console.ReadLine();
}
}
public class Session
{
public object Thingy;
}
public class Page
{
int _id;
Session _session;
public Page(int id, Session session)
{
_id = id;
_session = session;
}
public Action OKFunction
{
get { return (Action)_session.Thingy; }
set { _session.Thingy = value; }
}
public void SetAction()
{
OKFunction = DelegatedMethod;
}
public void DelegatedMethod()
{
Console.WriteLine($"Delegated method called on page {_id}");
}
public void InvokeAction()
{
OKFunction.Invoke();
}
}
}
Run this and it prints "Delegated method called on page 1", even though, of course, we accessed it through page "2".
So, if we change the getter of OKFunction to:
public Action OKFunction
{
get { return (Action)Delegate.CreateDelegate(typeof(Action), this,
((Action)_session.Thingy).Method); }
set { _session.Thingy = value; }
}
And now we get the output as "Delegated method called on page 2". You should be able to apply a similar transformation in your OKFunction getter to return a new Action that targets the current page. OKFunction has to become an instance method (non-static) so that it can access the this member.
This will be broken if whatever was passed to the setter of OKFunction wasn't a delegate to an instance method or was bound to an instance of some other class. You may wish to apply further validation within the setter and throw an ArgumentException of some kind if what's being set won't work.
I find myself quite often in the following situation:
I have a user control which is bound to some data. Whenever the control is updated, the underlying data is updated. Whenever the underlying data is updated, the control is updated. So it's quite easy to get stuck in a never ending loop of updates (control updates data, data updates control, control updates data, etc.).
Usually I get around this by having a bool (e.g. updatedByUser) so I know whether a control has been updated programmatically or by the user, then I can decide whether or not to fire off the event to update the underlying data. This doesn't seem very neat.
Are there some best practices for dealing with such scenarios?
EDIT: I've added the following code example, but I think I have answered my own question...?
public partial class View : UserControl
{
private Model model = new Model();
public View()
{
InitializeComponent();
}
public event EventHandler<Model> DataUpdated;
public Model Model
{
get
{
return model;
}
set
{
if (value != null)
{
model = value;
UpdateTextBoxes();
}
}
}
private void UpdateTextBoxes()
{
if (InvokeRequired)
{
Invoke(new Action(() => UpdateTextBoxes()));
}
else
{
textBox1.Text = model.Text1;
textBox2.Text = model.Text2;
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
model.Text1 = ((TextBox)sender).Text;
OnModelUpdated();
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
model.Text2 = ((TextBox)sender).Text;
OnModelUpdated();
}
private void OnModelUpdated()
{
DataUpdated?.Invoke(this, model);
}
}
public class Model
{
public string Text1 { get; set; }
public string Text2 { get; set; }
}
public class Presenter
{
private Model model;
private View view;
public Presenter(Model model, View view)
{
this.model = model;
this.view = view;
view.DataUpdated += View_DataUpdated;
}
public Model Model
{
get
{
return model;
}
set
{
model = value;
view.Model = model;
}
}
private void View_DataUpdated(object sender, Model e)
{
//This is fine.
model = e;
//This causes the circular dependency.
Model = e;
}
}
One option would be to stop the update in case the data didn't change since the last time. For example if the data were in form of a class, you could check if the data is the same instance as the last time the event was triggered and if that is the case, stop the propagation.
This is what many MVVM frameworks do to prevent raising PropertyChanged event in case the property didn't actually change:
private string _someProperty = "";
public string SomeProperty
{
get
{
return _someProperty;
}
set
{
if ( _someProperty != value )
{
_someProperty = value;
RaisePropertyChanged();
}
}
}
You can implement this concept similarly for Windows Forms.
What you're looking for is called Data Binding. It allows you to connect two or more properties, so that when one property changes others will be updated auto-magically.
In WinForms it's a little bit ugly, but works like a charm in cases such as yours. First you need a class which represents your data and implements INotifyPropertyChanged to notify the controls when data changes.
public class ViewModel : INotifyPropertyChanged
{
private string _textFieldValue;
public string TextFieldValue {
get
{
return _textFieldValue;
}
set
{
_textFieldValue = value;
NotifyChanged();
}
}
public void NotifyChanged()
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(null));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Than in your Form/Control you bind the value of ViewModel.TextFieldValue to textBox.Text. This means whenever value of TextFieldValue changes the Text property will be updated and whenever Text property changes TextFieldValue will be updated. In other words the values of those two properties will be the same. That solves the circular loops issue you're encountering.
public partial class Form1 : Form
{
public ViewModel ViewModel = new ViewModel();
public Form1()
{
InitializeComponent();
// Connect: textBox1.Text <-> viewModel.TextFieldValue
textBox1.DataBindings.Add("Text", ViewModel , "TextFieldValue");
}
}
If you need to modify the values from outside of the Form/Control, simply set values of the ViewModel
form.ViewModel.TextFieldValue = "new value";
The control will be updated automatically.
You should look into MVP - it is the preferred design pattern for Winforms UI.
http://www.codeproject.com/Articles/14660/WinForms-Model-View-Presenter
using that design pattern gives you a more readable code in addition to allowing you to avoid circular events.
in order to actually avoid circular events, your view should only export a property which once it is set it would make sure the txtChanged_Event would not be called.
something like this:
public string UserName
{
get
{
return txtUserName.Text;
}
set
{
txtUserName.TextChanged -= txtUserName_TextChanged;
txtUserName.Text = value;
txtUserName.TextChanged += txtUserName_TextChanged;
}
}
or you can use a MZetko's answer with a private property
I have a class called nyoba, i tried to enter value of textBox1.Text to eek.konsentrasi.
And I don't have any idea to call value of eek.konsentrasi from another class. Anybody knows? please help me.
public class nyoba
{
private string Konsentrasi;
public string konsentrasi
{
get
{
return Konsentrasi;
}
set
{
Konsentrasi = value;
}
}
public void njajal(string hehe)
{
}
}
private void button1_Click(object sender, EventArgs e)
{
nyoba eek = new nyoba();
eek.konsentrasi = textBox1.Text;
}
public class caller
{
//how to get eek.konsentrasi variable?
}
As first, your class names should always be pascal case (first letter uppercase). Also your public property should be pascal case.
Then your Nyoba class and its property Konsentrasi are not static, means you have to initiate the class as object before you can access it's non static property.
Nyoba n = new Nyoba();
string s = n.Konsentrasi;
To access the same instance you should not create the instance inside of the button click event. Place your Nyoba instance somewhere you can access to in the form and in the Caller class.
I have made a Base Form which is inherited by most Forms in the application. Base form contains a Status Bar Control that displays user name which is internally a static string. User can Switch User at any point in the application by pressing a button on status bar. At this point the user name in the status bar should also change, as if now it only changes in code and UI has no idea about the change. I have googled around and found that i need to bind the label with that static string by implementing a INotifyProperty Interface. I have implemented many example code without success.
Appreciate any help
use BindableAttribute for the property you want to bind a control to it.
[Bindable(true)]
public int Username {
get {
// Insert code here.
return 0;
}
set {
// Insert code here.
}
}
You must implement a class to notify prop changed and therefore the prop can not be static. Combine with a singleton pattern and you have yout solution.
public class Global : INotifyPropertyChanged
{
private string _userName;
public string UserName
{
get
{
return this._userName;
}
set
{
if (this._userName == value)
{
return;
}
this._userName = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("UserName"));
}
{
}
public event PropertyChangedEventHandler PropertyChanged;
private Global() {}
public static readonly Global Get = new Global();
}
Usage:
var currUserName = Global.Get.UserName;
Global.Get.PropertyChanged += (s, e) => Console.WriteLine(e.PropertyName);
Global.Get.UserName = "John";
And bind to Global.Get to property UserName.
I would:
1- Add a timer to the base form to update the status bar. (the timer resolution is uo to your requirement).
the timer Tick handler would be something like this:
private void timerStatusUpdate_Tick(object sender, EventArgs e)
{
toolStripStatusLabelMessage.Text = StatusMessage();
}
2 - Add a virtual StatusMessage method to your base class:
class BaseForm : Form
{
.......
public virtual string StatusMessage()
{
return "override me!";
}
}
3- override StatusMessage in all your derived classes
class XXXForm : BaseForm
{
........
public override string StatusMessage()
{
return "XXXForm status message";
}
}
I use Reactive Extensions for these things
For example if you have a Context class with a property UserName
you could do this
public static class Context
{
public static Subject<string> UserChanged = new Subject<string>();
private static string user;
public static string User
{
get { return user; }
set
{
if (user != value)
{
user = value;
UserChanged.OnNext(user);
}
}
}
}
And then on your forms just do
Context.UserChanged.ObserveOn(SynchronizationContext.Current)
.Subscribe(user => label.Text = user);
The ObserveOn(SynchronizationContext.Current) makes it safe for cross thread operation calls