I am developing a WPF application. I need some variables/information not to destroy until user closes that application. Which method is best? Static Class with static variables? Moreover what is the best practice in this scenario?
I belive what you can do is to write a class which would hold variables for you as the session object does in ASP .net . You can do something like
public static class ApplicationState
{
private static Dictionary<string, object> _values =
new Dictionary<string, object>();
public static void SetValue(string key, object value)
{
if (_values.ContainsKey(key))
{
_values.Remove(key);
}
_values.Add(key, value);
}
public static T GetValue<T>(string key)
{
if (_values.ContainsKey(key))
{
return (T)_values[key];
}
else
{
return default(T);
}
}
}
To save a variable:
ApplicationState.SetValue("MyVariableName", "Value");
To read a variable:
MainText.Text = ApplicationState.GetValue<string>("MyVariableName");
This would be accesses all through yuor application and would remain in memory throughout.
In this situation, you can use a static Class with static fields. He is never released, it doesn't have any destructors, and is not involved in garbage collection.
If you want to a normal class stayed alive, you can use the method GC.KeepAlive():
SampleClass sample = new SampleClass();
//... Somewhere in the end ...
GC.KeepAlive(sample);
Here, KeepAlive() creates a reference to your instance of the class in order to garbage collection think, that he still in use in your application. The purpose of the KeepAlive() is to ensure the existence of a reference to an object that is at risk of being prematurely reclaimed by the GC.
Quote from MSDN:
This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available.
The KeepAlive method performs no operation and produces no side effects other than extending the lifetime of the object passed in as a parameter.
Alternatively, information can be stored in the WPF-application settings. Especially if the information is important and should not be lost after system failure or reboot.
Settings located approximately here Project -> Properties -> Parameters. Example with setting new value:
MyProject.Properties.Settings.Default.MyButtonColor = "Red";
Saving is performed as follows:
MyProject.Properties.Settings.Default.Save();
It also possible using Binding with properties, indicating class of the settings in Source:
xmlns:properties="clr-namespace:MyProject.Properties"
<TextBlock Text="{Binding Source={x:Static properties:Settings.Default},
Path=MyButtonColor,
Mode=TwoWay}" />
For more information about using settings in WPF, please see:
User settings in WPF
A configurable Window for WPF
Saving user color settings of a clicked Button in WPF
You can also create static class and reference it in xaml like this:
namespace MyNamespace
{
public static class Globals
{
public static double SomeVariable { get { return 1.0; } }
}
}
Then access it from xaml like this:
<UserControl Width="{x:Static globals:Globals.SomeVariable}" />
where globals is defined at top top of your xaml like following:
<Window x:Class="MyNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:globals="clr-namespace:MyNamespace">
</Window>
Related
I have the following Singleton Pattern for the ViewModel of my Options:
private static volatile GeneralOptionsViewModel instance;
private static object syncRoot = new object();
/// <summary>
/// threadsave singleton
/// </summary>
public static GeneralOptionsViewModel Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new GeneralOptionsViewModel();
}
}
return instance;
}
}
In my XAML I have a color picker from the extended toolkit package:
<xctk:PropertyGridEditorColorPicker Background="Transparent" Name="face"
Margin="5,0" Width="50" BorderBrush="#32FFFFFF" BorderThickness="1"
SelectedColor="{Binding FaceRectColor, Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged}"/>
As you can see it is bound to FaceRectColor property of the GeneralOptionsViewModel class which is defined like follows.
Within the setter there is a conversion to MCvScalar (also a property of the same class), the format I later need for my application:
public Color FaceRectColor
{
get
{
return faceRectColor;
}
set
{
if (faceRectColor != value)
{
faceRectColor = value;
FaceRectColorScalar = new MCvScalar(value.B, value.R, value.G, value.A);
SetProperty(ref faceRectColor, value);
}
}
}
My problem now is, that the binding works and also the correct values are written to the variable, however when I call the singleton with the property from a different class - and from a different thread - it always shows zero for all color channels. However, if I break the program directly within the singleton class I can see the correct values. AFAIK the singleton should be threadsafe, so I'm looking for the reason of this behavior.
My guess is some threading issue, since other properties from the singleton class are displayed correctly, but they are only called in the main thread.
Edit: In my case all property values of the singleton class are set before the worker thread is active. This means no changes during the time the worker thread is active.
Edit II: Here is the complete project for code evaluation.
In the class CameraViewModel in line 202 is the relevant call for a function, where I want to pass the values from the singleton.
Your "singleton" contains a public constructor which effectively makes it a non-singleton. And you are not binding to the singleton in your GeneralOptionsView.
If you really want GeneralOptionsViewModel to be a singleton, you should implement it like this:
public sealed class GeneralOptionsViewModel : ViewModelBase
{
private static readonly GeneralOptionsViewModel _instance = new GeneralOptionsViewModel();
private GeneralOptionsViewModel()
{
GetAvailableCameraList();
DetectorTypeList = new List<string>() { "Cascade Detector" };
SelectedDetectorTypeIndex = 0;
}
public static GeneralOptionsViewModel Instance => _instance;
//...
}
You should then set the DataContext of your view to the singleton:
<Grid DataContext="{Binding Source={x:Static local:GeneralOptionsViewModel.Instance}}">
When your property changes and it does so on a different thread, the calls that are made to notify everybody (in particular the UI) of this change are running in the calling thread. Accessing the UI in a thread that is not the UI thread is a bad idea. It might sometimes work. But it will fail sooner or later.
The solution to your current problem is changing the property in the UI thread.
That said, maybe you should think about whether you need a Singleton. That's a huge red flag that something is wrong with the structure of your program. You don't need a Singleton. Nothing bad would happen if some other context had a second settings viewmodel. You seem to want a Singleton because it's so nice and easy to have a global variable. That is the drawback of a Singleton. It's disadvantage that you buy into because you need something from this pattern. If you find you are using this pattern only because it's disadvantage gives you an excuse to have a global variable, you are doing patterns wrong. It's an anti-pattern.
I'm working in a WPF project, it has a numerous number of user control.
for some forms , when the user close the container tab, a confirmation close is shown up, for other forms they are just closed.
so I use the
FrmAccounts FrmAcc = new FrmAccounts {Tag = "showConfirmClose"};
to decide what tab I need to close.
and in the closing event I check if the tag is set to showConfirmClose to show the confirmation message. But I don't like using Tag it's not good in C#, also I thought, what if I want to send more data (the only solution will be to comma separating them in the Tag and Split them, but this is worse).
I can't find a good, performant way to accomplish such task:
if this is possible:
FrmAccounts FrmAcc = new FrmAccounts {new{ShowConfirmClose= true }};
Attached property
You can use an AttachedProperty to store the additional information for your objects. However, these object have to be DependencyObjects, otherwise it won't work.
Here is an example.
Define your attached property in a separate class (it can be a simple static class or a DependencyObject):
namespace YourNamespace
{
public static class CloseConfirmation
{
public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached(
"IsActive",
typeof(bool),
typeof(CloseConfirmation));
public static bool GetIsActive(DependencyObject obj)
{
return (bool)obj.GetValue(HiddenProperty);
}
public static void SetIsActive(DependencyObject obj, bool value)
{
obj.SetValue(HiddenProperty, value);
}
}
}
In XAML, set the attached property value for your objects (WPF Windows, Pages and other DependencyObjects):
<FrmAccounts xmlns:yns="YourNamespace"
yns:CloseConfirmation.IsActive="true">
<!-- Content -->
</FrmAccounts>
You can use this attached property in your XAML definition, e.g. in triggers.
You can also access its value in code-behind:
FrmAccounts frm = new FrmAccounts();
CloseConfirmation.SetIsActive(frm, true);
var isActive = CloseConfirmation.GetIsActive(frm); // true
You can create an attached property not only for a simple type like bool or int, but also for any custom type. So use your own container class if you need to transfer a lot of data.
Tag object
Using Tags is fully OK in .NET. Virtually any UI type has this property, so that you can attach objects of any type to your UI elements. However, these types cannot be anonymous types. You have to create your own container type:
class Container
{
public bool IsConfirmationNeeded { get; }
public IEnumerable<IItem> MyData { get; } // any data you need to pass
}
FrmAccounts frm = new FrmAccounts();
frm.Tag = new Container();
var container = (Container)frm.Tag;
var confirmation = container.IsConfirmationNeeded; // false by default
In a xamarin app on a xaml page I am loading localized strings using a xaml extension (the details are described here). For example:
<Label Text={i18n:Translate Label_Text}/>
Now, I want the user to be able to change the language of the app at runtime (using a picker). If that happens, I want to change the language immediately.
Can I somehow reload all translated texts?
I could delete all pages and recreate them, but I am trying to avoid that.
I could also bind all localised texts to strings in the pages model. But that is a lot of unnecessary code for truly static strings.
Unfortunately you cannot force controls set up with markup extensions in XAML to reevaluate their properties using those extensions - the evaluation is only done once upon parsing XAML file. What basically happens behind the scenes is this:
Your extension is instantiated
ProvideValue method is called on the created instance and the returned value is used on the target control
The reference to the created instance is not stored (or is a weak reference, I'm not sure), so your extension is ready for GC
You can confirm that your extension is only used once by defining a finalizer (desctructor) and setting a breakpoint in it. It will be hit soon after your page is loaded (at least it was in my case - you may need to call GC.Collect() explicitly). So I think the problem is clear - you cannot call ProvideValue on your extension again at an arbitrary time, because it possibly no longer exists.
However, there is a solution to your problem, which doesn't even need making any changes to your XAML files - you only need to modify the TranslateExtension class. The idea is that under the hood it will setup proper binding rather than simply return a value.
First off we need a class that will serve as a source for all the bindings (we'll use singleton design pattern):
public class Translator : INotifyPropertyChanged
{
public string this[string text]
{
get
{
//return translation of "text" for current language settings
}
}
public static Translator Instance { get; } = new Translator();
public event PropertyChangedEventHandler PropertyChanged;
public void Invalidate()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Binding.IndexerName));
}
}
The goal here is that Translator.Instance["Label_Text"] should return the translation that your current extension returns for "Label_Text". Then the extension should setup the binding in the ProvideValue method:
public class TranslateExtension : MarkupExtension
{
public TranslateExtension(string text)
{
Text = text;
}
public string Text { get; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = new PropertyPath($"[{Text}]"),
Source = Translator.Instance,
};
return binding.ProvideValue(serviceProvider);
}
}
Now all you need to do is to call Translator.Instance.Invalidate() every time the language is changed.
Note that using {i18n:Translate Label_Text} will be equivalent to using {Binding [Label_Text], Source={x:Static i18n:Translator.Instance}}, but is more concise and saves you the effort of revising your XAML files.
I'd tried to implement #Grx70's great proposed solution, but some of the classes and properties the example used are internal to Xamarin so couldn't be used in that way.
Picking up on their last comment though, was the clue to get it working, though not quite as elegantly as initially proposed, we can do this:
public class TranslateExtension : IMarkupExtension<BindingBase>
{
public TranslateExtension(string text)
{
Text = text;
}
public string Text { get; set; }
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return ProvideValue(serviceProvider);
}
public BindingBase ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = $"[{Text}]",
Source = Translator.Instance,
};
return binding;
}
}
and this the Translator class as initially proposed, but reproduced here for clarity with the GetString call:
public class Translator : INotifyPropertyChanged
{
public string this[string text]
{
get
{
return Strings.ResourceManager.GetString(text, Strings.Culture);
}
}
public static Translator Instance { get; } = new Translator();
public event PropertyChangedEventHandler PropertyChanged;
public void Invalidate()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
Then as the original post suggested, instead of binding text with:
{i18n:Translate Label_Text}
Bind
{Binding [Label_Text], Source={x:Static i18n:Translator.Instance}}
I'd hit this right at the end of a project (adding the multiple languages), but using Visual Studio Community and Search/Replace with RegEx, the binding can be replaced across the project, replacing:
\{resources:Translate (.*?)\}
with:
{Binding [$1], Source={x:Static core:Translator.Instance}}
NOTE: The Regex assumes the 'resources' namespace for the original Translate macro, and 'core' namespace for the Translator class, you may have to update as appropriate.
I appreciate this is a small tweak to #Grx70's otherwise great solution (I'm standing on the shoulders of giants with this one), but I'm posting this here for any that follow with the same problem of getting this working.
I want to have a global collection which I can access/edit on any page i.e SpenderList.
Here I have the instance of a ObservableCollection created in App.xaml.cs:
namespace v003
{
public sealed partial class App : Application
{
public ObservableCollection<Spender> SpenderList = new ObservableCollection<Spender>();
public App()
{
this.InitializeComponent();
this.Suspending += this.OnSuspending;
}
...
}
}
When I type SpenderList on a page (MainPage.xaml.cs), the intellisense doesn't show that collection.
How do I access this collection from MainPage.xaml.cs and other pages?
Note: When I declared that instance in the MainPage.xaml.cs, all my subsequent codes work, so there's no issue with custom class, Spender.
Also I would like your opinion on this way of designing the app. Essentially all tasks are based on the items in this collection, and I'd want to any changes to this collection and its items performed from any page to be global.
you need to cast:
((App)Application.Current).SpenderList
or make it static:
public static ObservableCollection<Spender> SpenderList = new ObservableCollection<Spender>();
then you access it like this:
App.SpenderList
Generally you should try to avoid globally accessible static variables.
I need to attach an event handler to an object, and I placed this code on a button click event. However, I noticed that this will cause the same event to attach multiple times with each click.
Is there a way to run a piece of code on class creation? The class in question is a static class btw.
I can do something like:
if (bool == false)
{
attach event handler;
bool = true;
}
Just not sure if this is the right way to do it. Thanks.
There are static constructors, that are (in principle) only run once per class.
Something like this:
public static class MyStaticClass
{
public static int MyStaticProperty;
//no accessors required, as this is never explicitly invoked
static MyStaticClass() //no parameters either
{
MyStaticProperty = 100;
}
}
....
//writes: 100
Console.WriteLine(MyStaticClass.MyStaticProperty);
However, if a constructor won't do it, because you have some parameters that need to be set, or there are some prerequisite steps that need to be done, I would indeed recommend a private boolean check, as you have done.
You use a constructor - it will run on class creation.
Constructors are class methods that are executed when an object of a class or struct is created. They have the same name as the class or struct, and usually initialize the data members of the new object.
For static classes, use static constructors:
A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only. It is called automatically before the first instance is created or any static members are referenced.
try
if(Button1.Click == null)
Button1.Click += new System.EventHandler(this.myEventHandler);