Method Invoke() inside click event doesnt change controls values in Page - c#

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.

Related

EventHandler called once without event fired

EventHandler called once without event fired
public class Observable<T>
{
public delegate void ValueChanged(T value);
event ValueChanged Changed;
public T Value { get; private set; }
public Observable(T t)
{
Value = t;
}
/*
public void Set(T value)
{
if (Equals(value, Value))
{
return;
}
Value = value;
Changed?.Invoke(Value);
}
*/
public void Subscribe(ValueChanged action)
{
Changed += action;
}
}
I am currently seeing the issue in the user permission handling of a Xamarin mobile app I am working on
ViewModel reads local storage to get latest saved permission (mobile). It creates the Obervable<Permisson> property passing the saved value in the constructor:
var savedCamera = HistoryService.GetStatus<Permissions.Camera>()
CameraPermissionProperty = new Observable<PermissionStatus>(savedCamera); // <--
Then subscription is made
CameraPermissionProperty.Subscribe(SendPermission<Permissions.Camera>);
The SendPermission<Permissions.Camera> method is triggered once with value set in the Observable despite not having invoked the ValueChanged delegate.
I can't repoduce this in a console app.
What could be the issue, could be related to threading?
I added a number to see which method was called:
// ... Observable
public delegate void ValueChanged(T value, int number); //<--
event ValueChanged Changed;
public void Subscribe(ValueChanged action)
{
Changed += action;
Number = 18;
}
public void SubscribeWithInitialUpdate(ValueChanged action)
{
Changed += action;
Number = 10;
Changed.Invoke(Value, Number);
}
10 is returned even though I am calling the Subscribe method
I could not reproduce your error in Xamarin, so I will try to provide some suggestions.
First here is the code I tested:
public class Observable<T>
{
public delegate void ValueChanged(T v, int n);
public event ValueChanged Changed;
public T Value { get; private set; }
public int Number; //this was missing in your code example
public Observable(T t)
{
Value = t;
}
public void Set(T value)
{
if (Equals(value, Value))
{
return;
}
Value = value; //breakpoint here
Changed?.Invoke(Value, Number);
}
public void Subscribe(ValueChanged action)
{
Changed += action;
Number = 22; //breakpoint here
Console.WriteLine("setting 22");
}
public void SubscribeWithInitialUpdate(ValueChanged action)
{
Changed += action;
Number = 11; //breakpoint here
Console.WriteLine("setting 11 - is never called");
Changed?.Invoke(Value, Number);
}
}
I executed the code from an arbitrary viewmodel's c-tor:
public class SomeViewModel // : INotifyPropertyChanged
{
public SomeViewModel()
{
obs = new Observable<string>("baar");
// subscribe to event with anonymous delegate event handler
obs.Subscribe((v,n) => { //breakpoint here
Console.WriteLine("value=" + v + " number=" + n); //breakpoint here
});
obs.Set("foo"); //breakpoint here
}
}
//console output:
//setting 22
//value=foo number=22
In Xamarin Forms, you can use breakpoints and Console.WriteLine in DEBUG mode with the Android Emulator. For me this was working as expected. So I suggest setting breakpoints at the relevant places, especially the method that should not be called, but somehow is ("SubscribeWithInitialUpdate"), maybe you will catch, when it is called. In my scenario the method was never executed, as expected.
For me this has nothing to do with invoking the event. Number = 10 is set in the method SubscribeWithInitialUpdate(), which is independent from the event. Even if the event would have been invoked from some other method multiple times, the breakpoint in that method should never be reached.
It may be that your SendPermission<Permissions.Camera> method is called from somewhere else, not from the event (though the 10 would still be a mystery). So I suggest to add a breakpoint in there aswell. Also to be sure, don't add a method than can be called as a parameter, try adding an anonymous lambda delegate (at least for debugging), so you can be sure, no other caller exists.
So my last suggestion is to try my code in a new empty Xamarin project and compare that to your project.
I am assuming you don't want to do MVVM here and use custom listeners? Otherwise I would suggest to simply implement INotifyPropertyChanged. Examples:
Link1
Link2
Link3
I hope some of this helps.
If you are using multiple threads, I assume you know what you are doing, otherwise I don't know why or how something would ever call any arbitrary method. You could check it's thread.ManagedThreadId Link
You might think about of the loading of page.
It could be launched on page load

How to access variable value declared inside main function to passing it to button_click method ? C#

I want to access a variable value which is initialized inside main function on the button click. Please check my code first ...
{
public string user { get; private set; }
public Dashboard(String loggedinUser)
{
InitializeComponent();
var user = loggedinUser;
f1(user);
// WORKING WELL WHEN I'M CALLING f1() FROM HERE.
}
private void f1(String usr)
{
Dock_MainPanel.Children.Clear();
Passuc psuc = new Passuc(usr);
Dock_MainPanel.Children.Add(psuc);
}
private void dash_nav_pw_Click(object sender, RoutedEventArgs e)
{
// USER VALUE DROPPING HERE. HENCE NULL VALUE IS PASSED TO FUNCTION
f1(user);
}
I want to access user value which is stored in Dashboard function and to be passed next time when I am calling function f1 inside dash_nav_pw_Click(). How to make it global variable and values should be static once assigned to user variable ?
Change var user = loggedinUser; to just user = loggedinUser;
The first makes a new variable inside the function, the latter writes into the already existing outer variable.
Since user is a public property in your class, it is already accessible. Do not set it to local variable inside of Dashboard, just assign the value to the property. You didn't provide information on how you want to use this value, but you can just get the value from the property directly.
{
public string user { get; private set; }
public Dashboard(String loggedinUser)
{
InitializeComponent();
//set the value of the user property here
user = loggedinUser;
f1(user);
}
private void f1(String usr)
{
Dock_MainPanel.Children.Clear();
Passuc psuc = new Passuc(usr);
Dock_MainPanel.Children.Add(psuc);
}
private void dash_nav_pw_Click(object sender, RoutedEventArgs e)
{
//Once the user property value was set by Dashboard, you can get it here
f1(user);
}

Passing Value on Property Change

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;
}

How to call value from getter and setter method

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.

C#: data binding a single, custom class to form controls (checkbox?)

I'm writing a desktop application in Visual Studio 2008 / C#
I finished (mostly) writing an engine which generates a working schedule for the week for a small company; a form of a course-scheduling problem
Right now I'm designing a form in which the user can determine the initial parameters, or criteria, for the engine to adhere to (as some settings are optional)
I've got a class named EngineParameters, which holds all of those settings.
For the purpose of databinding, I created a bndEngineParameters class, which encapsulates all the relevant fields with getters and setters
public class bndEngineParameters
{
private engineParameters _parameters;
public bndEngineParameters(engineParameters ep)
{
this._parameters = ep;
}
public bool avoidGrouping
{
get { return _parameters.avoidGrouping; }
set { _parameters.avoidGrouping = value; }
}
public bool avoidWeekends
{
get { return _parameters.avoidWeekends; }
set { _parameters.avoidWeekends = value; }
}
public bool keyFlow
{
get { return _parameters.keyFlow; }
set { _parameters.keyFlow = value; }
}
public bool keyFlowAssistants
{
get { return _parameters.keyFlowAssistants; }
set { _parameters.keyFlowAssistants = value; }
}
}
It's not complete - there will be int values (maximum number of hours one can work etc.); I want those bool values to be bound to checkboxes on my form
And it's at that trivial task where I surprisingly ran into problems
Using "Add New DataSource" wizard, I created a binding source
private System.Windows.Forms.BindingSource bndEngineParametersBindingSource;
I then bound the Checked property of my Checkbox to the respective property of my binding source:
I implemented a local variable boundParameters so that I get an access to the parameters set by the user
public partial class formGenerateRota : Form
{
public bndEngineParameters boundParameters;
// (...)
public formGenerateRota()
{
InitializeComponent();
}
private void formGenerateRota_Load(object sender, EventArgs e)
{
boundParameters = new bndEngineParameters(new engineParameters());
bndEngineParametersBindingSource.Add(boundParameters);
}
// (...)
}
And what? Nothing happens. There is an bndEngineParameters object under bndEngineParametersBindingSource.Current (in run-time of course), but the avoidWeekends value never changes (when I check the checkbox on and off), and the bndEngineParametersBindingSource_CurrentItemChanged event is never fired
What's wrong?
SORRY! it does change, but only after the checkbox loses focus (after validation).
I'm stupid sometimes
If I'm doing something wrong anyway (I'm not any good with data binding), I'd very much appreciate if you point it out of course!
Two common issues:
set the DataSourceUpdateMode to OnPropertyChanged
(optional) to receive changes from the object, implement the {name}Changed event pattern or INotifyPropertyChanged
To be honest though, I'm sure most of that isn't necessary; you should just be able to say:
myCheckbox.Bindings.Add("Checked", myEngineParameters, "avoidWeekends",
false, DataSourceUpdateMode.OnPropertyChanged);
Full example:
using System;
using System.Diagnostics;
using System.Windows.Forms;
class EngineParameters {
private bool avoidWeekends;
public bool AvoidWeekends {
get { return avoidWeekends; }
set {
avoidWeekends = value;
Debug.WriteLine("AvoidWeekends => " + value);
}
}
}
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
using(Form form = new Form())
using (CheckBox myCheckbox = new CheckBox()) {
EngineParameters myEngineParameters = new EngineParameters();
myEngineParameters.AvoidWeekends = true;
form.Controls.Add(myCheckbox);
myCheckbox.DataBindings.Add("Checked", myEngineParameters, "AvoidWeekends",
false, DataSourceUpdateMode.OnPropertyChanged);
Application.Run(form);
}
}
}
Instead of this:
bndEngineParametersBindingSource.Add(boundParameters);
do this:
bndEngineParametersBindingSource.DataSource = boundParameters;

Categories