Is there something like SESSION in Windows application? I want to store a few values to be persistent between forms.
For example: First form has some check boxes and third form process them accordingly. So I need to store the checked checkboxes somewhere.
If you're talking about different Forms within the same Application, then just create some static members on a class, it will be persisted for the lifetime of the executable.
You could only expose your CheckBoxes Checked state through properties of this form where you put your CheckBoxes on, and access these properties from your third or Process form.
public partial class MainForm : Form {
// We assume we have let's say three CheckBoxes named chkFirst, chkSecond and chkThird
public bool IsFirstChecked { get { return chkFirst.Checked; } }
public bool IsSecondChecked { get { return chkSecond.Checked; } }
public bool IsThirdChecked { get { return chkThird.Checked; } }
// Calling this form from where these checked states will be processed...
// Let's suppose we have to click a button to launch the process, for instance...
private void btnLaunchProcess(object sender, EventArgs e) {
ProcessForm f = new ProcessForm();
f.Parent = this;
if (DialogResult.OK == f.ShowDialog()) {
// Process accordingly if desired, otherwise let it blank...
}
}
}
public partial class ProcessForm : Form {
// Accessing the checked state of CheckBoxes
private void Process() {
if ((this.Parent as MainForm).FirstChecked)
// Process according to first CheckBox.Checked state.
else if ((this.Parent as MainForm).SecondChecked)
// Process according to second CheckBox.Checked state.
else if ((this.Parent as MainForm).ThirdChecked)
// Process according to third CheckBox.Checked state.
}
}
Please consider that I picked this code up the top of my head, so it might happen not to compile. Anyway, I hope that this gives you an idea of how to pass your values throughout your forms.
The biggest difference between Web and WinForm programming is that Web is stateless. SESSION and VIEWSTATE are workarounds to allow one to preserve values.
WinForms are stateful, so you don't need to go through SESSION and VIEWSTATE-like variables. A value is preserved as long as the object exists.
You can use app.config (or Settings section in Project's Properties) if you use Visual Studio, or just serialize your values and store them in some file.
If you want to persist data between independent execution of the same app (as in concurrent request serving in a HTTP farm) then just write out some XML or use a mashalling/serializing system with your runtime/plaform (dunno what it would be for C#).
Then import it again. Just watch your concurrency control.
If this is just a regular single-user windows application, create a class to model the state you want to pass around and require it in your form constructors:
internal class ApplicationState
{
// Store the selected checkbox values here, for example
public List<int> SelectedProductIds { get; }
// ... additional state ...
}
internal class EditOrderForm: Form
{
private ApplicationState applicationState;
public EditCustomerForm(ApplicationState applicationState) {
this.applicationState = applicationState;
}
// Rest of the code
}
You could use static variables instead of instances - but those are just global variables that make your code harder to read and maintain.
If you are looking to store data on a per user basis between execution sessions, you should consider Isolated Storage.
Doesn't clutter install directory
Doesn't cause issues with AnitVirus software
Part of the OS including .Net objects, don't need to install anything else
Already works with the Windows security model
Exists on a per user basis, so saved settings are separated for each user
Can serialize/deserialize obects directly into it
Related
I have a c# application that launches during startup with the next code below.
On the first windows form I have two textboxes that should be filled with data from Properties.Settings.Default, but they are empty.
If I close the app and start it again, the textboxes are filled with the correct values.
What can be the issue?
RegistryKey rk = Registry.CurrentUser.OpenSubKey
("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
rk.SetValue("", Application.ExecutablePath);
I set the values in the constructor of the form with the following code:
if (Properties.Settings.Default.dbusername != string.Empty)
{
textBoxLoginUsername.Text = Properties.Settings.Default.dbusername;
string readable = EncryptionHelper.Decrypt(Properties.Settings.Default.dbpassword);
textBoxLoginPassword.Text = readable;
}
Your code shows a registry key assignment that is potentially problematic.
First, it attempts to set the default ("") value of
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
It should be making named key for your startup app e.g.
Second, is it being set in the startup app itself? This is contrary to Microsoft documentation.
Run and RunOnce Registry Keys -
A program that is run from any of these keys should not write to the key during its execution because this will interfere with the execution of other programs registered under the key.
Instead, we'll "do the right thing" by establishing this key in the installer project for the app:
Moving on...
Settings
Showing a "mystery line" like:
string readable = EncryptionHelper.Decrypt(Properties.Settings.Default.dbpassword)
makes it more difficult to diagnose the question that you actually asked. The points made by Panagiotis Kanavos are excellent, but notice that we're talking about that now instead of your original ask. I suggest you solve the main issue first using a Minimal Reproducible Example leaving out the authentication scheme. Then we take the straightforward case of two startup settings:
The textboxes are initialized here:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
textBoxLoginUsername.Text = Properties.Settings.Default.dbusername;
textBoxLoginPassword.Text = Properties.Settings.Default.dbpassword;
}
}
And after installing the app and restarting the PC we see that things are "so far so good".
Things to check
Without seeing more code, I can only make general suggestions.
Consider writing a startup log file to your app's AppData folder tracing values at various execution points.
Look for uncaught or "swallowed" exceptions that might skip your textbox initialization code.
Check for race condition where Properties.Settings.Default.Save() might be being called before textboxes are initialized.
Since there's "probably" a mechanism for saving login changes, make sure any event handlers are attached after the InitializeComponents has run. For example:
Initializing a Persist scheme
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
textBoxLoginUsername.Text = Properties.Settings.Default.dbusername;
textBoxLoginPassword.Text = Properties.Settings.Default.dbpassword;
buttonLogin.Click += onClickLogin;
}
private void onClickLogin(object? sender, EventArgs e)
{
if(tryLoginWithCredentials())
{
Properties.Settings.Default.dbusername = textBoxLoginUsername.Text;
Properties.Settings.Default.dbpassword = textBoxLoginUsername.Text;
Properties.Settings.Default.Save();
Text = $"Logged in as {Properties.Settings.Default.dbusername}";
}
}
private bool tryLoginWithCredentials() => true; // Succeeded (for testing purposes).
}
I am creating an ASP.NET MVC web application. It has service classes to execute business logic and it access data through Entity Framework.
I want to change some business logic based on application variable. These variables are global variables and load from app config and don't change after the initial loading.
public class BroadcastService : IBroadcastService
{
private static readonly ILog Logger = LogProvider.GetCurrentLogger();
private readonly IUnitOfWork _worker;
private readonly IGlobalService _globalService;
public BroadcastService(IUnitOfWork worker, IGlobalService globalService)
{
_worker = worker;
_globalService = globalService;
}
public IEnumerable<ListItemModel> GetBroadcastGroups()
{
if(Global.EnableMultiTenant)
{
//load data for all tenants
}
else
{
//load data for current tenant only
}
return broadcastGroups ?? new List<ListItemModel>();
}
...
}
public static class Global
{
public static bool EnableMultiTenant{get;set;}
}
For example, EnableMultiTenant will hold application is running in multi-tenant mode or not.
My concerns are:
Is it ok to use a static global variable class to holds those values?
This application is hosting on Azure app service with load balancing. Is there any effect when running multi-instance and when app pool restarts?
To answer your question as to whether it is 'okay' to do this, I think that comes down to you.
I think the biggest thing to know is when that data is going to get refreshed. From experience I believe that static information gets stored in the application pool, so if it is restarted then the information will be refreshed.
Lifetime of ASP.NET Static Variable
Consider how many times you need that information, if you only need it once at startup, is it worth having it as a static. If you are getting that information a lot (and say for example it is stored in a database) then it may be sensible to store that in a cache somewhere such as a static member.
I think my only recommendation with static member variables is asp is keep them simple, booleans seem fine to me. Remember that users do share the same application meaning that static variables are global for all users. If you want a user specific variable then you want to use sessions cache.
Always remember the two hardest thing in programming
Naming things
Cache invalidation
Off by one errors
https://martinfowler.com/bliki/TwoHardThings.html
Even though this is a joke, it holds a lot of truth
Hope this helps
This is thread safe if you initialize these values once and then only read from them. It is also safe in the presence of multiple worker processes and restarts because the multiple processes don't share variables.
As an alternative consider creating an instance of a class holding your settings:
class MySettings {
bool IsEnabled;
}
Then you can use dependency injection to inject a singleton value of this class to your code. This makes it easier to tests and makes the code more uniform.
I'm creating a WinRT 8.1 app based onto Caliburn.Micro (alpha2) and I'm implementing a simple state management mechanism for it. All it needs is saving a couple of name/value pairs for each of its two or three pages, and restore the current page when resumed. So I'm using the strategy summarized below; given that it seems there is no predefined mechanism for CM/WinRT, it would be interesting to get any community advice on this, or this might hopefully be useful for RT newcomers like me.
1) I define an interface (IHaveSimpleState) to be implemented by VMs having some simple state to be saved and restored. The state is represented by a dictionary where each value is a string, representing any serialized value, and the interface just has 2 methods, one for saving its state into this dictionary, and another to resume it from the dictionary. All my stateful VMs (each corresponding to a view) implement this.
2) In my app.cs (which derives from Caliburn.Application), I create a List<WeakReference<IHaveSimpleState>> to keep track of all the VMs requiring state management: in the GetInstance override which instantiates the VMs (using CM simple container) I add to this list each newly generated instance implementing IHaveSimpleState.
3) for saving state: in the app OnSuspending override, I cycle through all the VMs in this list, and invoke their SaveState method to collect data about their state in a common dictionary. Once the loop is complete, I get ApplicationData.Current.LocalSettings and I copy these data into its Values dictionary, thus effectively saving them.
4) for restoring state: in the app OnResuming override and in the OnActivated override (in the latter case, only if args.PreviousExecutionState is equal to Running, i.e. the app was not terminated by user nor crashed), I invoke a ResumeState method which cycles through all the VMs in the list and invokes their LoadState method to load the state from application data local settings.
All this seems to work fine, I only miss a point: what's the right place to restore the current "page", i.e. to tell Caliburn to navigate to the VM behind the view active at the time of suspension? I tried to do this at the end of my ResumeState method (nr.4), but it seems too early, as when I try to navigate to a VM I get an exception telling me that the corresponding view could not be found. Here is the relevant code for this method:
private void ResumeState()
{
// ... state is a dictionary wrapper class with state data
// restore state for each tracked VM
foreach (WeakReference<IHaveSimpleState> reference in _statefulViewModels)
{
IHaveSimpleState stateful;
if (reference.TryGetTarget(out stateful)) stateful.LoadState(state);
}
// move to the page which was current when the state was saved
string sType = state.Get(APP_CURRENTVM_KEY, null);
if (sType != null)
{
// not so elegant...
INavigationService navigation = IoC.Get<INavigationService>();
Type t = Type.GetType(sType);
navigation.NavigateToViewModel(t);
}
}
I tried this and seems to work, but I feel a bit unsecure about the robustness of my app given that the lifecycle handling in CM for 8.1 does not seem to be clearly documented, so I'd like to get comments or corrections from the community. First override the PrepareViewFirst method:
protected override void PrepareViewFirst(Frame rootFrame)
{
_container.RegisterNavigationService(rootFrame);
}
Then in the OnLaunched override, where before I just called DisplayRootView<MainView>(), I test the args to check whether we are resuming from suspended state, and if so I navigate to the previously active page; else I just go as before:
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
bool bResumed = false;
if (args.PreviousExecutionState == ApplicationExecutionState.Suspended)
{
AppSimpleState state = LoadState();
string sType = state.Get(APP_CURRENTVM_KEY);
if (sType != null)
{
INavigationService navigation = IoC.Get<INavigationService>();
Type t = Type.GetType(sType);
Debug.Assert(t != null);
navigation.NavigateToViewModel(t);
bResumed = true;
} //eif
}
if (!bResumed) DisplayRootView<MainView>();
}
In the system there is a singleton for currently logged user (our own user not windows user) containing a shared instance.
In a few hundred data access class files this is used to set CreatebyID and EditbyID for each request sent to the database. Nearly all the classes inherit a single base although at the moment the two values are set in each class.
This all worked fine for the companies desktop applications for many years however when the same data access classes were used in a new web application the currently logged user was shared across all users sessions and could not be changed without causing issues.
How can I refactor the code without significantly re-writing the whole DAL and without passing in a current user (or User ID) into every instance or setting an EditbyID property on each class for every save.
You could use a static property which get/set a Session variable via HttpContext.Current.Session.
For example:
public class DAL
{
public static int CreatebyID
{
get
{
return (int)HttpContext.Current.Session["CreatebyID"];
}
set
{
HttpContext.Current.Session["CreatebyID"] = value;
}
}
public static int EditbyID
{
get
{
return (int)HttpContext.Current.Session["EditbyID"];
}
set
{
HttpContext.Current.Session["EditbyID"] = value;
}
}
}
I have tabs application, each Tab contain many views... And in each Tab the save mode is global.
If I leave current tab containing changes, a pop-up appear asking user confirm with or without save or cancel. After save, others opened tabs is reloaded.
How would you do, to detect the changes? To save only the views changed? And then to propagate the changes to the other tabs?
My first think is, to implement a IsModified property and ReloadTab method in each ViewModel, but is not really viable solution, each setter must change this property... Any idea ?
class MyViewMode
{
...
public bool IsModified { get { return MyViewModel1.IsModified || MyViewModel2.IsModified ... || _isModified }
...
}
[EDIT]
I hesitate between EventAggregator approach and Messenger (MVVM Light Toolkit implementation)...
I think I am going to create Events/Messages Domain representing each command generates a tab reloading, and create a Handler (Service receive all messages/events and send specific messages/events for each View to be reloaded). Any advices ?
I've done this using a global event broker. The idea is that events such as save which have global scope will pass through this broker class.
The event broker is a singleton, where each class will register it's handlers. The handler would be registered using attributes:
[EventSubscribe(EventNames.Save)]
private void OnSaved(GlobalEventArgs args)
{
// do something on saved
}
And each object that wishes to push itself to the broker would do it like this:
EventBroker.Instance.Register(this);
How does this relate to your tabs issue? Fairly simply, when one tab saves, then that should raise the save event via the EventBroker:
EventBroker.Instance.Publish(
EventNames.Save,
new SavedGlobalEventArgs(typeof(YourModel)));
And all your other tabs would handle the event such as this:
[EventSubscribe(EventNames.Save)]
private void OnSaved(GlobalEventArgs args)
{
var savedArgs = (SavedGlobalEventArgs)args;
if(savedArgs.SavedType == typeof(YourModel)
{
this.Model.Refresh();
}
}
You'll still have to handle the saved event on each tab that might require a refresh when another tab has done something, but this keeps the code relatively nice and simple without having to put all kinds of crap in. Can also extend it outside of a save event, make some other global events that may be useful:
UserCreated
UserLoggedIn
SearchInitiated
whatever; i don't know the context of your app - but the broker is a really nice way to deal with sharing knowledge in a tabbed environment.
Please let me know if you want me to send some code :)
The best way to do this is by modifying each setter. If not, you cannot know exactly if your model data has changed and I wouldn't suggest tracking modified changes in the UI. Something like this should give you a good head start.
public class Person : IModifiable
{
private bool _markDirty;
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
_markDirty = true;
_Name = value;
}
}
public bool IsDirty()
{
return _markDirty;
}
}
public interface IModifiable
{
public bool IsDirty();
}