How do I programmatically remove unwanted user settings? - c#

I'm currently using the example from MSDN (https://msdn.microsoft.com/en-us/library/ms171565%28v=vs.110%29.aspx) to save user configuration settings.
This works fine for my needs as the number of objects I need to store may differ from run to run. Since the user can create or destroy objects at will I need to track those and update the settings file accordingly. I use a SettingsKey to read/write each object since the properties between objects may differ. Each object has a unique id that I use in the SettingsKey.
What I have observed is this: each object is saved and restored properly. However when an object is destroyed it remains in the settings file.
I need to remove the destroyed objects from the settings file.
How is this done? I haven't been able to find any code examples that show how to do this.
These are declared earlier in the code:
UserSettings uSet;
uSet = new UserSettings();
Here's the code that saves the objects:
for (int i = 0; i < CurrrentViewportCount; i++)
{
if (VP_ID[i] != -1)
{
uSet.SettingsKey = "Viewport" + VP_ID[i];
foreach (Control cP in this.Controls)
{
if (cP.Name.Equals(uSet.SettingsKey) == true)
{
Viewport VP = (Viewport)cP;
uSet.PanelName = VP.Name;
uSet.PanelID = VP.PanelID;
uSet.ConnectionName = VP.SourceMRL;
uSet.Save();
break;
}
}
}
else
{
// Need to remove unused object
}
}
I apologize for the formatting (indentation) if it's off. This is my first post and I tried following the guidelines for posting code....
I have considered deleting the user.config file and reloading but I have the feeling that a Reload() will only recreate the user.config from the app.exe.config and not present me with an "empty" user.config file.
To recap, my main question is how do I remove specific user settings?
Thanks for your help.

Related

How do I save multiple user settings in runtime in C#

I am trying to make my app stay the way I left it after closing the app. Therefore I want to save set of items from ListView to the settings and I can't figure out how to do that. I've found some solutions but I believe they are outdated as they don't seem to work.
Image shows set of items in ListView which I want to save so they appear there after restarting the app:
Items
This is where I want them to appear:
Settings
And this is part of code that I've tried out so far
private void btn_SaveConfig_Click(object sender, EventArgs e)
{
int i = 0;
Settings.Default["IP"] = tBox_discoverServerFrom.Text;
Settings.Default["DiscoveredServers"] = cBox_discoveredServers.Text;
foreach (var item in lV_groups.Items)
{
var property = new System.Configuration.SettingsProperty("Group"+i);
property.PropertyType = typeof(string);
property.Name = "Group " + i;
Settings.Default.Properties.Add(property);
Settings.Default.Save();
i++;
}
}
I do not think using the Settings API is a great idea if you want to save any significant amount of data.
I would recommend the following
Create a class describing all the data you want to save. To make serialization easier it should have a parameter less constructor and public setters for all properties. This is sometimes called a Data-Transfer-Object (DTO)
Use json to serialize the object to a file. You would normally place the file in a subfolder in the local app data folder: Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).
Do the reverse when you start the application. If there is a file, use Json to deserialize it and use it however you want.
You may optionally add logic to save the file periodically, this would allow recovery in case of application crashes. You might also want some system to keep more than one file, in case the application or computer crashes in the middle of a save operation, and the file becomes corrupt.

Application setting doesn't work well

I have a checkbox(Name:tarahi_algouritm) and a button(Name:button1) on my form(Name:frm_choose).I want to save the latest changes on my checkbox as user clicked on the button.it means user run the program and check the checkbox and then click on button and then close the program.when he/she Rerun it,checkbox should be checked.or someway he disable the checkbox and click on button and after another run,checkbox should be disabled.
for this, in application setting(table part) put a checkbox (Name:s_tarahi_algouritm)and choose USER in scope part..as I said changes are apply on checkbox and s_tarahi_algouritm is used for save the latest changes on checkbox.I wrote these codes:
private void frm_choose_Load(object sender, EventArgs e)
{
if (Properties.Settings.Default.s_tarahi_algouritm!=null)
tarahi_algouritm= Properties.Settings.Default.s_tarahi_algouritm;
}
private void button1_Click(object sender, EventArgs e)
{
Properties.Settings.Default.s_tarahi_algouritm = tarahi_algouritm;
Properties.Settings.Default.Save();
}
but When I make changes on checkbox and close the debug and Rerun it,changes are not applied.
what should I do?where is wrong?I am partly beginner so explain explicit.
thank you all
The problem is the settings files are written out in two parts: one to the application settings (which you can't save to) and the other to the user settings (which you can save to). You need to save the user settings (it gets written to your c:\users{userid}... directory).
Look at the most up-voted response to Farzin's link. It explains the issue as well.
Here's a more thorough explanation: App.config: User vs Application Scope
Here's an example.
I created a webform app and added a Settings file to it (called TestSettings.settings). I added two values:
When you run this application it creates a file in the application directory named the same as your executable with .config appended that contains (among other things) a element and a element. But this file only contains the initial values. If you change the value under the element and call Save() it will not update this file. It will create a file:
c:\Users{username}\AppData\Local{appname}{random_dir_name}{version}\user.config
My code to demonstrate this was:
Console.WriteLine(TestSettings.Default["UserValue"]);
TestSettings.Default["UserValue"] = "def";
TestSettings.Default.Save();
I tested many things like:
Properties.Settings.Default.Properties.Add(new System.Configuration.SettingsProperty("a"));
Properties.Settings.Default.Properties["a"].DefaultValue = "b";
Properties.Settings.Default.Save();
it has not error but do not save. In this link:
C# Settings.Default.Save() not saving?
Answered you must add Properties.Settings.Default.Reload(); after saving, I did that but not changed. It seems no one knows the answer.(I read many articles).
It looks like a cancer to me! I suggest you to easily save your settings to a xml file.
Below i add an easy xml saving method:
using System.Xml.Linq;
And
XElement settings;
try
{
settings = XElement.Load("settings.xml"); //beside the app .exe file
}
catch (Exception) // it is first time and you have not file yet.
{
settings = new XElement("settings");
settings.Save("settings.xml");
}
If you want to add new element:
settings.Add(new XElement("firstKey", tarahi_algouritm.Checked.ToString()));
settings.Save("settings.xml");
If you want to read or edit element:
XElement settings = XElement.Load("settings.xml");
string firstKey = settings.Element("firstKey").Value; //reading value
settings.Element("firstKey").Value = "New Value"; //Edit
settings.Save("settings.xml"); //Save
Remember that firstKey is only a name and you can use another names instead.

Getting the current Directory

I have been working with a simple program essentially designed to be digital flash cards. Ideally, I want the program to be portable. I am trying to get the current directory. My setup is this:
I have a FileIO.cs method which reads currentDir/Data. I then have a winform which calls the method and retrieves a string[] array of the list of folders. Both of these methods are public static. I then have a separate button to create controls on the form.
The problem:
I have cleaned/rebuilt the solution and been using the debugger; The program is running from C:\Users\user\a\b\c\solution\bin\debug. The control (radioButton) I created to verify this data is telling me the 'currentDir' is C:\Users\user and not the programs current directory.
Notes:
I have manually created Debug\Data and 4 folders within the data folder so I am 100% sure they exist. No warnings or compile or run time errors are thrown. Card_Base.GetGrades is automatically called on form load successfully.
I have also confirmed the Release folder is empty and not being used. Also I have a backup program which shows an icon when a file is being uploaded to my backup every time the file(s) change. So I am 100% sure bin\debug is the proper working folder in this scenario.
Perhaps what is puzzling me most is that I have a totally separate program written on the same PC using the same IDE and it properly retrieves the path using the same setup.
In FileIO.cs
//public static string pathPortable = System.IO.Directory.GetCurrentDirectory();
public static string pathPortable = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
static string[] grade1;
public static string[] GetGrade()
{
string fullPath = FileIO.pathPortable + #"\Data";
grade1 = Directory.GetDirectories(fullPath);
return grade1;
}
in Card_Base.cs
public static List<RadioButton> buttonGrade = new List<RadioButton>(10);
public static void GetGrades()
{
string[] grade2 = FileIO.GetGrade();
//Proper and accurate names of the folders I manually added
//C:\Users\user\a\b\c\solution\bin\debug\Data\K
//C:\Users\user\a\b\c\solution\bin\debug\Data\1
//C:\Users\user\a\b\c\solution\bin\debug\Data\2
//C:\Users\user\a\b\c\solution\bin\debug\Data\3
MessageBox.Show("" + grade2[0]); //Information (entire path) is accurate as of this line
int x = 5;
int y = 0;
for (int i = 0; i < grade2.Count(); i++)
{
y = i * 21;
Card_Base.buttonGrade.Add(new RadioButton());
Card_Base.buttonGrade[i].Text = grade2[i];
MessageBox.Show("" + buttonGrade[i].Text); //Proper entire path is shown here!
Card_Base.buttonGrade[i].Location = new System.Drawing.Point(x, y);
}
}
The control whose .text property which shows C:\Users\User, not the Bin\Debug folder.
private void buttonTest_Click(object sender, EventArgs e)
{
MessageBox.Show("abc:" + buttonGrade[0].Text);
for (int i = 0; i < buttonGrade.Count(); i++)
{
panelGrade.Controls.Add(buttonGrade[i]);
}
MessageBox.Show("def:" + buttonGrade[0].Text); //Proper string path is displayed in the popup box here!
}
Result: ???
Four vertically lined radioButtons on a form panel which all have text reading "C:\Users\user\"
Update1:
The text also appears slightly misaligned, slightly higher than the radioButton bubble itself, very strange. However, I've gone back into the editor and confirmed the panelGrade is initially empty. Click/Drag grabs nothing and right clicking the panel does not reveal any underlying objects in the panel's space.
After modifying the target directory to its parent (1 level higher), each messageBox checkpoint reveals the proper string/path is being sent in. The visible radioButton when the program is launcher, after the "test" button is pushed is the only occurrence of this unusual text/string appearing anywhere.
Placing messageBoxes before/after the loop within the test button itself shows that the control (radioButton in buttonGrade[0]) DOES contain the proper string / text / path. Thus the change must occur at some point after the buttonTest code is finished executing.
Update2:
I just opened a brand new solution and copy/pasted the relevant code eliminating anything extraneous. Named all the items/controls with the same names. Completely bare bones. Exact same problem.
HOWEVER, when I change radioButtons to TextBoxs ... the program displays the proper information. Um. What???
Update 3:
Looking through the MSDN radioButtons are derived from buttonBase. The only relevant event I see at a glance is the textChanged event. While it does note that the way the text property of derived classes like radioButton varies, it fails to specify exactly how or what limits it has. Control.Text is simply a System.String , thus I see no reason why a radioButton would not be able to contain that information.
I tried creating a 'test2' button to change 'buttonGrade[0].Text = FileIO.pathPortable;' . Oddly enough, it does not change the text all. Nor does it throw an error.
AH HA! I noticed that the folder after user was my google drive, which is "Google Drive" and has a space in it. I then copied my trash program to C:\and named it TrashMe2 and ensured no folder names contained spaces. The result was "C:\TrashMe2\bi". I then tried "1234567890123456789". The result was that it showed up to the second 3.
The radioButton wasn't receiving a different string and the string/path/data was never changed. It simply showed a 'different folder' because that, by luck of the draw' was the exact number of visible characters it showed. Because I created the radioButtons programmatically, AutoSize did NOT default to true. The proper string was part of the radioButton, it simply was not all visible.
And for security reasons (and a bit of humor), I actually call my user account "user". So it wasn't a scenario where I would see C:\Users\JoeNomidBlow was cut off.
I feel... rather stupid at this particular moment. Thanks for the help!
Programmatically adding a control, particularly a radioButton, does not automatically set the autoSize property to true. When creating a radioButton programatically, one must be sure to add
radioButton.Autosize = true;
or as this case is/was
buttonGrade[i].Autosize = true;
Otherwise the visible space of the text is shortened. In this case, leaving behind a partial path. Which coincidentally happens to be identical to a different valid path.

Prevent an object from being coppied in C# PowerPoint 2010 add-in

I am building a PowerPoint 2010 C# add-in using Visual Studio 2010. One of the functions of the add-in is to add a shape to the current slide. Once the shape is added to the slide though, I need to prevent it from being copied. That is where I am running into issues. I have looked at all the application level events and am not seeing any sort of beforeCopy or beforePaste type of events.
The only option I can think of right now is to add a keydown event listener to listen for "ctrl+c" and block that if my shape is selected and then create a custom right click menu (not even sure if I can yet) to remove the "Copy" option if my shape is selected. There has to be simpler option though.
Anyone have any ideas how I would prevent a user from copying a shape?
The commands executed by built-in ribbon buttons Microsoft Office can be disabled or re-routed. Microsoft calls this "Repurposing", an introduction can be found here.
So another approach could be to "repurpose" the built-in Copy button with something like this. (Needs to be returned by GetCustomUI to customize the ribbon, see the link above.) This modifies the action executed by the Copy button and the callback method that determines whether the button is enabled or not.
<command idMso="Copy" onAction="copyAction" getEnabled="copyEnabled" />
Implement copyAction to return cancelDefault = true when your shape is selected so it will not be copied.
Implement copyEnabled to return false if your shape is selected. Remember to invalidate the button on selection change events.
Actually, one of both approaches should be sufficient. I guess onAction is easier to implement.
Just to close the loop on this, I am sharing my work-around in the hopes that someone else who has this issue will not waste as much time as I have on this. I ended up just using the SlideSelectionChanged and WindowSelectionChange events and a dictionary to delete my objects that have been coppied.
First, when my shape is added to the stage I add a new entry into the dictionary containing the shape name (in my case it was actually a group of shapes) and its ID.
itemIDDictionary.Add(myGroup.Name, myGroup.Id);
WindowSelectionChange is a fairly simple check. It just looks to see if the newly selected item is in the dictionary already. If it is, it then checks to see if the ID matches. If not, it deletes the item. This works because when you copy and paste an item, the newly pasted item is automatically selected on the slide.
public void itemSelectionChange(PowerPoint.Selection SelectedItem)
{
try
{
if (Globals.Ribbons.Ribbon2.itemIDDictionary.ContainsKey(SelectedItem.ShapeRange.Name))
{
for (int shapeIDCount = 0; shapeIDCount < Globals.Ribbons.Ribbon2.itemIDDictionary.Count; shapeIDCount++)
{
if (!Globals.Ribbons.Ribbon2.itemIDDictionary.ContainsValue(SelectedItem.ShapeRange[1].Id))
{
SelectedItem.Delete();
MessageBox.Show("You can not copy the browser object.\nAdd a new one using the ribbon bar");
}
}
}
}
catch {}
SlideSelectionChanged is just a little bit more complicated as I have to loop through all the shapes on the slide.
try
{
if (SldRange.Count > 0)
{
var showWarning = false;
for (int slideCount = 1; slideCount <= SldRange.Count; slideCount++)
{
int shapeCount = 1;
while (shapeCount <= SldRange[slideCount].Shapes.Count)
{
if (Globals.Ribbons.Ribbon2.itemIDDictionary.ContainsKey(SldRange[slideCount].Shapes[shapeCount].Name))
{
if (!Globals.Ribbons.Ribbon2.itemIDDictionary.ContainsValue(SldRange[slideCount].Shapes[shapeCount].Id))
{
SldRange[slideCount].Shapes[shapeCount].Delete();
showWarning = true;
}
else
{
shapeCount++;
}
}
else
{
shapeCount++;
}
}
}
if(showWarning == true)
{
MessageBox.Show("You can not copy the browser object.\nAdd a new one using the ribbon bar");
}
}
}
catch { }
As I said in my initial post, I am sure there is a cleaner way to do this. I just couldn't find one to save my life.

c#: Create new settings at run time

c# windows forms: How do you create new settings at run time so that they are permanently saved as Settings.Default.-- values?
Just in case that still matters to anyone:
You can dynamically add settings through Settings.Default.Properties.Add(...) and have these also persisted in the local storage after saving (I had those entries reflected in the roaming file).
Nevertheless it seems that the dynamically added settings keep missing in the Settings.Default.Properties collecion after loading again.
I could work around this problem by adding the dynamic property before first accessing it.
Example (notice that I "create" my dynamic setting from a base setting):
// create new setting from a base setting:
var property = new SettingsProperty(Settings.Default.Properties["<baseSetting>"]);
property.Name = "<dynamicSettingName>";
Settings.Default.Properties.Add(property);
// will have the stored value:
var dynamicSetting = Settings.Default["<dynamicSettingName>"];
I don't know if this is supported by Microsoft as the documentation is very rare on this topic.
Problem is also described here http://www.vbdotnetforums.com/vb-net-general-discussion/29805-my-settings-run-time-added-properties-dont-save.html#post88152 with some solution offered here http://msdn.microsoft.com/en-us/library/saa62613(v=VS.100).aspx (see Community Content - headline "How to Create / Save / Load Dynamic (at Runtime) Settings"). But this is VB.NET.
In addition to John's solution for saving, the proper method for loading is add the property, and then do a Reload() on your settings.
Your dynamic setting will be there!
For a full example, valid for using in library code, as you can pass the settings in ..
ApplicationSettingsBase settings = passed_in;
SettingsProvider sp = settings.Providers["LocalFileSettingsProvider"];
SettingsProperty p = new SettingsProperty("your_prop_name");
your_class conf = null;
p.PropertyType = typeof( your_class );
p.Attributes.Add(typeof(UserScopedSettingAttribute),new UserScopedSettingAttribute());
p.Provider = sp;
p.SerializeAs = SettingsSerializeAs.Xml;
SettingsPropertyValue v = new SettingsPropertyValue( p );
settings.Properties.Add( p );
settings.Reload();
conf = (your_class)settings["your_prop_name"];
if( conf == null )
{
settings["your_prop_name"] = conf = new your_class();
settings.Save();
}
Since the Settings class is generated at build time (or, actually, whenever you update the settings file from within the designer), you can't use this mechanism for dynamic scenarios. You can, however, add some collection or dictionary to the application settings and modify that dynamically.
You can't add settings directly (at least not without editing the config XML at runtime), but you can fake it.
In my case, I had a group of identical custom controls on the form, and I wanted to store the runtime state of each control. I needed to store the state of each control, since each one had different data it.
I created a new StringCollection setting named ControlData and placed my own data in there. I then load the data from that list and use it to initialize my controls.
The list looks like this:
Box1Text=A
Box1List=abc;def;foo;bar;
Box2Text=hello
Box2List=server1;server2;
In my startup code, I read through the key/value pairs like this:
foreach (string item in Properties.Settings.Default.ControlData) {
string[] parts=item.split('=');
parts[0] will have the key and parts[1] will have the value. You can now do stuff based on this data.
During the shutdown phase, I do the inverse to write the data back to the list. (Iterate through all the controls in the form and add their settings to ControlData.
How would you access the new settings that you have created? The point of the Visual Studio settings designer is that you can write code that uses these settings with compile-time checking of your code. If you want to dynamically create new settings for your app to use, you will also need to dynamically load them. For dynamic settings, you may want to look at the System.Configuration assembly, notably ConfigurationSection. You can create a custom configuration section with that, which you could use for dynamic setting addition/removal. You might use a ConfigurationCollection for that dynamic addition/removal.
INI files eh? Google turned up this INI library for .NET.
What you could do is create a new registry key.
Name the new key "Your program settings".
RegistryKey ProgSettings = Registry.CurrentUser.OpenSubKey("Software", true);
ProgSettings.CreateSubKey("Your Program settings");
ProgSettings.Close();
Now you can add String Identifiers and values.
RegistryKey ProgSettings = Registry.CurrentUser.OpenSubKey("Software\\Your Program settings", true);
ProgSettings.SetValue("Setting Name", value); // store settings
string settings = ProgSettings.GetValue("Setting Name", false); // retreave settings
ProgSettings.DeleteValue("Setting Name", false);
Besure to close the registry key when you are done to avoid conflicts with other parts of your program that may write to the registry.
Many comercial software applications use these methods.
stackoverflow has many examples about writing and reading to the registry.
This is much easyer then modifying the appconfig.xml file that is used when you create settings.
It took me a long time using the top two answers here plus this link (Create new settings on runtime and read after restart) to get it to finally work.
First of all, set your expectations. The answer here will create a new user setting and you can get its value the next time you launch your app. However, the setting you created this way will not appear in the Settings designer. In fact, when you relaunch the app and try to access the setting in your code, it will not find it. However, the setting you have created through code is saved in the user.config file (say jDoe.config) somewhere in your file system. For you to access this value, you have to add the setting again.
Here is a working example I have:
private void FormPersistence_Load(object sender, EventArgs e)
{
StartPosition = FormStartPosition.Manual;
// Set window location
var exists = Settings.Default.Properties.OfType<SettingsProperty>().Any(p => p.Name == Name + "Location");
if (exists)
{
this.Location = (Point)Settings.Default[Name + "Location"];
}
else
{
var property = new SettingsProperty(Settings.Default.Properties["baseLocation"]);
property.Name = Name + "Location";
Settings.Default.Properties.Add(property);
Settings.Default.Reload();
this.Location = (Point)Settings.Default[Name + "Location"];
}
}
Note:
My new setting's name will be resolved at run time. Name is really this.Name, which is the form's name. This is a base form that other forms can inherit from, so all the child forms will be able to remember their locations.
baseLocation is a setting I have manually created in Settings designer. The new setting I have is the same type. This way I don't have to worry about things like provider, type, etc. in code.
I see how what I wanted was the wrong idea. I'm porting a c++ app over to c# and it has a lot of ini file settings and I was looking for a shortcut to add them in. I'm lazy.

Categories