I'm completely new to C# programming. (4 days)
I did some amateur programming on Excel VB to create an dashboard for our teams. Now I want to lift it to the next level.
My dashboard has 10 buttons changing color from red to green and back each click. Each click changes the label beneath it from Text 1 to Text 2.
Function: overview of the service area who finished work and handed to us.
I got 10 different voids for each of the areas, because I didn't manage to link the clicked button to the label, so I just used 10. "northwest", "east", "southeast" and so on.
void northwest(object sender, EventArgs e)
{
Button button = sender as Button;
Control ctrl = ((Control)sender);
switch (ctrl.BackColor.Name)
{
case "Red":
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.Black;
lbl_nw.Text = "Service";
break;
case "LimeGreen":
ctrl.BackColor = Color.Red;
ctrl.ForeColor = Color.White;
lbl_nw.Text = "North-West";
break;
default:
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.Black;
lbl_nw.Text = "Service";
break;
}
}
Second: I got 145 buttons (the teams) changing between 5 different colors each click. We use this to clarify the state of the team. like "n/a", "available", "on the way" etc.
void MyButtonClick(object sender, EventArgs e)
{
Button button = sender as Button;
Control ctrl = ((Control)sender);
switch (ctrl.BackColor.Name)
{
case "Gainsboro": // switch from n/a to available
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.White;
break;
case "LimeGreen": // switch from available to on the way
ctrl.BackColor = Color.Yellow;
ctrl.ForeColor = Color.Black;
break;
case "Yellow": // switch from on the way to fully occupied
ctrl.BackColor = Color.Orange;
ctrl.ForeColor = Color.Black;
break;
case "Orange": // switch from fully occupied to omitted
ctrl.BackColor = Color.Red;
ctrl.ForeColor = Color.White;
break;
case "Red": // switch from omitted to back at home
ctrl.BackColor = Color.Aqua;
ctrl.ForeColor = Color.Black;
break;
default:
ctrl.BackColor = Color.Gainsboro;
ctrl.ForeColor = Color.Black;
break;
}
}
Now the actual problem: Because my former dashboard can only be used by one person (on one central computer) I wanted to make my new dashboard multi-user friendly.
So my intention is to save the colors/label names into an txt file or xml file (forget about xml. its toooo complex for me) and load it automatically if there were any changes on the board by any user.
I searched but can't find any solution I really understand (because of missing knowledge).
I have tried the following:
void save(object sender, EventArgs e)
{
TextWriter txt = new StreamWriter("C:\\Users\\shift.txt");
txt.Write(button1.Text);
txt.Write(button1.ForeColor);
txt.Write(button1.BackColor);
txt.Close();
}
At this solution I would need to write every button I want to store. Problem: I get something like this:
North-West Color [LimeGreen] Color [Black].
I think the correct solution would be like North-West;LimeGreen;Black, but I cant figure out how! Even then I don't know how to load back all values to the correct objects.
And I tried to loop through them with:
for (int i = 1; i < 155; i++)
But I got problems with the objects (e.g. "is a type but used as an method") every combination.
Second attempt was via xml. But this is way to complex for me.
I tried it like this:
void savedata(object sender, EventArgs e)
{
DataSet d = new DataSet();
DataTable t = new DataTable();
d.Tables.Add(t);
t.Columns.Add(new DataColumn("Name",typeof(string)));
t.Columns.Add(new DataColumn("FontColor",typeof(string)));
t.Columns.Add(new DataColumn("Background",typeof(string)));
t.Rows.Add(button6.Text, button6.ForeColor, button6.BackColor);
t.Rows.Add(button7.Text, button7.ForeColor, button7.BackColor);
t.Rows.Add(button8.Text, button8.ForeColor, button8.BackColor);
t.Rows.Add(button9.Text, button9.ForeColor, button9.BackColor);
t.Rows.Add(button10.Text, button10.ForeColor, button10.BackColor);
t.Rows.Add(button11.Text, button11.ForeColor, button11.BackColor);
t.Rows.Add(button12.Text, button12.ForeColor, button12.BackColor);
t.Rows.Add(button13.Text, button13.ForeColor, button13.BackColor);
t.Rows.Add(button14.Text, button14.ForeColor, button14.BackColor);
t.Rows.Add(button15.Text, button15.ForeColor, button15.BackColor);
d.WriteXml("C:\\Users\\Shift.xml");
}
It saves, yes, but how do I load it back into the correct objects?
I don't understand anything about de/serialization, I just read some code and tried to adopt it and failed big.
My question:
Does anyone have a proper solution for the text file how to save/load/format correctly? The save should be done if there is a new change, same goes for the load. So automatically. Every user can open the dashboard, the file storage would be on our server where everyone has access to it to save/load it via dashboard.
I would really appreciate a possible solution for this. Like I said, I'm a beginner and this problem driving me nuts.
Everything works fine, except for the save/load mechanic.
Just to mention:
i cant download any other software, so i need to stick to windows programms.
for saving and loading from a flat file (.txt, .xml, .json, .whatver) I use the following solution, it's honestly very easy and takes the hard work away. You will need to download and install NewtonSoft package from nuget. But trust me it makes everything easy.
Let's create a hypothetical scenario similar to yours. You have many users and want to save information for each of them. Let's start with a user class.
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Theme { get; set; }
}
Now lets create a little 'database' or 'data holder' class that will actually store and lookup our info. It will have a FilePath variable where we will save our flat file, a List of all the Users and some functions to help get everything:
public static class DataHolder {
public static string FilePath { get; set; }
public static List<Users> Users { get; set; }
static DataHolder()
{
// Initialize the variables
FilePath = "C:/Temp/MyData.json";
Users = new List<User>();
Init();
}
static void Init() { }
public static void LoadData() { }
public static void SaveData() { }
}
Let's start by creating a file to save all the data in.
using System.IO;
using NewtonSoft;
...
static void Init() {
// Check if file exists
if (File.Exist(FilePath))
{
LoadData();
return;
}
// It doesn't so we create it
using (var sw = new StreamWriter(FilePath)){
// Create empty list of users
var emptyUserList = new List<User>();
// Convert it into Json using NewtonSoft
var fileText = JsonConvert.SerializeObject(emptyUserList);
// Write it to the file
sw.Write(fileText);
}
}
Above we used Newton to create a new file with an empty Json object in it. Let's see how loading and saving will work:
public static void LoadData() {
using (var sr = new StreamReader(FilePath)){
Users = JsonConvert.Deserialize<List<User>>(sr.ReadToEnd());
}
}
public static void SaveData() {
using (var sw = new StreamReader(FilePath)){
sw.Write(JsonConvert.Serialize(Users));
}
}
And that is the main bulk of the class. Let's implement a GetUser() and AddUser() function that searches for a user based on Id and then I'll show you an example of how to use the class:
using System.Linq;
...
public static User GetUser(int id) => Users.FirstOrDefault(x => x.Id = id);
public static void AddUser(User user) => Users.Add(user);
Example:
public static void Main(string[] args){
// Test the saving
TestSaving();
// Test the loading
TestLoading();
}
static void TestLoading() {
// Get a user
var loadedUser = DataHolder.GetUser(1);
if (loadedUser != null)
Console.WriteLine("Success");
}
static void TestSaving() {
// Create a new user and add it
var user = new User() {
Id = 1,
Name = "Fred",
Theme = "Red"
};
// Save the data in memory
DataHolder.AddUser(user);
// Save the data to file
DataHolder.SaveData();
}
Related
I am trying to remake my C#/WPF take on the Simon game by making it more clean and use classes now that I finally understand classes. Before I ask my question - here is some sample code from my old version as a reference.
public MainWindow()
{
InitializeComponent();
RedButton.IsEnabled = false;
BlueButton.IsEnabled = false;
GreenButton.IsEnabled = false;
YellowButton.IsEnabled = false;
}
//Use TextBlock clearing to say "Watch the pattern", "Your Turn" and then an empty box between games
private void StartButton_Click(object sender, RoutedEventArgs e)
{
_IsError = false;
RedButton.IsEnabled = true;
BlueButton.IsEnabled = true;
GreenButton.IsEnabled = true;
YellowButton.IsEnabled = true;
StartButton.IsEnabled = false;
if (randomPattern.Count == 0)
{
randomPattern.Add(random.Next(0, 4));
randomPattern.Add(random.Next(0, 4));
}
StatusBox.Text = "Watch the Pattern.";
ShowPattern();
}
So, my question is: Would it be good practice for me to use IsElement as a property/attribute of class "Buttons" instead of writing all of their statements out individually? And if I should add them to the class, I think my next question comes down to - how would I implement it into the class? I know simply typing only "IsEnabled = <T/F>" won't do anything.
EDIT:
To show you what I mean:
Should I have IsEnabled as a class property like such:
class Button {
IsEnabled = false;
}
var greenButton = new Button()
//Some code that lets the player start the game by clicking a button
greenButton.IsEnabled = true;
Or should I keep my IsEnabled statements outside of a class like in my old version of the code? Note - my old version of the code, there are no classes that I made - only functions.
IsEnabled should be placed into Button class as it is property or feature of button.
public class Button {
public IsEnabled { get; set; }
}
The above class has high cohesion as it is focused on what it should be doing. It has only methods/properties, fields relating to the intention of the class.
So I am implementing a rudimentary Hotel booking system with a check in and check out method. For the check in and check out selections, I have a switch statement that allows the user to pick a room by selecting a number. I have a truncated version of the code below, as the case statement pretty much are similar in nature:
case 1:
if (true)//this is supposed to check if the room is booked
{
Console.WriteLine("This room is already booked, please try another place");
}
else
{
var instance1 = new BookingMethods();
instance1.bookRoom1();
revenueGenerated += 100;
}
break;
For the if statements, I'm trying to check if the rooms are booked or not, and I have created objects in another class along the methods for checking in and out. Below is how I did this:
class BookingMethods
{
Room room1 = new Room();
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}
I want the if statement to be able to read room1.occupied for the condition, but I can't seem to be able to reference it correctly. I have bookRoom1() in my BookingMethods.cs from the case statement, which is in my Program.cs. I figured I don't have to move my Room object from BookingMethods.cs to Program.cs to do this, and I haven't been able to figure out a way to call it.
You did not specify an access modifier for room1. If you do not provide one, it is set to private by default, meaning it is only accessible in the class in which it is declared. In addition to this, you should really create a property.
This should get you going:
var instance1 = new BookingMethods();
switch (instance1.room1.occupied)
{
case true:
Console.WriteLine("This room is already booked, please try another place");
break;
default:
instance1.bookRoom1();
revenueGenerated += 100;
break;
}
class BookingMethods
{
public Room room1 {get; set;};
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}
Make room1 a field
class BookingMethods
{
Room room1 {get; set;};
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}
I'm creating a interface for a application at the moment. Background are all 1x1 pixel. In design time the interface is super slow (visual studio 2012). Moving components is super laggy. If I run the application it takes about 10 seconds for the interface to load.
Some parts of the interface can be hidden. Ones they become visible again they take time to build up / load part by part.
How can I improve this and make the interface much smoother / loading faster?
Source code:
namespace WindowsFormsApplication1
{
public partial class MapTool : Form
{
public int Pin_Berekenen = 0;
public int pin_Settings = 0;
public MapTool()
{
InitializeComponent();
BlackSetupUI();
panel_Berekenen.Visible = false;
panel_settings.Visible = false;
}
void BlackSetupUI()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Selectable, false);
pictureBox_RouteBerekenen.BackColor = Color.Transparent;
panel_Berekenen.BackColor = Color.Transparent;
pictureBox_settings.BackColor = Color.Transparent;
button_pin_berekenen.FlatAppearance.BorderSize = 0;
button_Berekenen.FlatAppearance.BorderSize = 0;
label_berekening1.Parent = pictureBox_bar;
label_berekening1.BackColor = Color.Transparent;
label_Berekening2.Parent = pictureBox_bar;
label_Berekening2.BackColor = Color.Transparent;
label_verschil.Parent = pictureBox_bar;
label_verschil.BackColor = Color.Transparent;
button_pin_settings.FlatAppearance.BorderSize = 0;
label_RouterBerekenen.Parent = pictureBox_bar;
label_Settings.Parent = pictureBox_bar;
pictureBox_settings.Parent = pictureBox_bar;
pictureBox_RouteBerekenen.Parent = pictureBox_bar;
pictureBox_innectis.Parent = pictureBox_bar;
label_innectis.Parent = pictureBox_bar;
}
void WhiteSetupUI()
{
}
private void MapTool_MouseClick(object sender, MouseEventArgs e)
{
if (Pin_Berekenen == 0)
{
panel_Berekenen.Visible = false;
}
else
{
return;
}
if (pin_Settings == 0)
{
panel_settings.Visible = false;
}
else
{
return;
}
}
private void button_pin_berekenen_Click(object sender, EventArgs e)
{
switch (Pin_Berekenen)
{
case 0:
Pin_Berekenen = 1;
button_pin_berekenen.Image = Properties.Resources.Pinned;
break;
case 1:
Pin_Berekenen = 0;
button_pin_berekenen.Image = Properties.Resources.Pin;
break;
default:
break;
}
}
private void button_pin_settings_Click(object sender, EventArgs e)
{
switch (pin_Settings)
{
case 0:
pin_Settings = 1;
button_pin_settings.Image = Properties.Resources.Pinned;
break;
case 1:
pin_Settings = 0;
button_pin_settings.Image = Properties.Resources.Pin;
break;
default:
break;
}
}
private void pictureBox_settings_MouseHover_1(object sender, EventArgs e)
{
panel_settings.Visible = true;
}
private void pictureBox_RouteBerekenen_MouseHover_1(object sender, EventArgs e)
{
panel_Berekenen.Visible = true;
}
}
}
I'm just designing the interface there is no data loaded with in this application as you can see.
It is hard to tell what exactly is your interface "doing". But here are some general tips:
You can preload something while showing splash screen (with or without progress) and then doing all time consuming work (to example, constructing all forms, parsing all configurations, caching graphics, etc). Or even put main application into dll and making exe-file small with the only splash screen.
You can optimize parent/child controls redrawing and layout operations (BeginUpdate/EndUpdate and such), so you don't waste time by calling unnecessary draw operation until it's a right time for it to happens. You can use double buffering, WS_EX_COMPOSITED.
If you have to much calculations, then perhaps something has to be virtualized (you display "fast placeholders" and after caculation replace them with real data) or cached.
You can use one of many tricks to simulate faster (as user can feel) operation: show a small part of data on screen, while prepare a much bigger portion of data in the memory (sort of virtual window), add some intelligent conclusions and caching (if user is moving left, then perhaps he want to continue that way, preload more data from left or, if user is moved right by 100 pixels, perhaps he will want to return, so don't unload that data yet).
Perhaps you have to much data what you have to profile it and optimize or even use C++ to write speed demanded parts in separate dll's.
Or maybe you should redesign your UI, split common parts into control, put nested things into user controls, etc, etc..
Hide all of them then when it is fully loaded, show it up
If you want to show animation, you can use Bunifu UI (paid)
I am currently taking a beginner's class in c#. We missed 2 consecutive classes because the teacher couldn't be there. So we didn't really see what we needed to do this. He said to just go see on MSDN, but that is usually way too complicated for me. So here is my problem:
I have to create a "Simon Says" program. Here is my current code (sorry for the French variables):
public partial class Form1 : Form
{
const byte LIMITE = 255;
const byte LIMITEBOUTON = 5;
byte[] _abyBouton = new byte[LIMITE];
Random _rand = new Random();
public Form1()
{
InitializeComponent();
}
//Blinks the Button. Works correctly.
void AnimerBouton(Button btnBouton, Color Cocoleur)
{
btnBouton.BackColor = Color.Black;
btnBouton.ForeColor = Color.White;
Update();
System.Threading.Thread.Sleep(500); // C'est inscrit en miliseconde
btnBouton.BackColor = Cocoleur;
btnBouton.ForeColor = Color.Black;
Update();
System.Threading.Thread.Sleep(500); // C'est inscrit en miliseconde
}
private void btnDémarrer_Click(object sender, EventArgs e)
{
//Creates an array with the 255 eventual moves.
for (byte byIndex = 0; byIndex <= LIMITE - 1; byIndex++)
{
_abyBouton[byIndex] = (byte)_rand.Next(1, LIMITEBOUTON);
}
for (byte byIndex = 0; byIndex <= LIMITE - 1; byIndex++)
{
//Takes care of the current progress in the game.
for (byte byIndex2 = 0; byIndex2 <= byIndex; byIndex2++)
{
switch (_abyBouton[byIndex2])
{
case 1:
{
AnimerBouton(btn1, Color.Green);
}
break;
case 2:
{
AnimerBouton(btn2, Color.Red);
}
break;
case 3:
{
AnimerBouton(btn3, Color.Yellow);
}
break;
case 4:
{
AnimerBouton(btn4, Color.Cyan);
}
break;
}
//Wait to see if the click is correct. No idea how to do this.
}
}
}
}
So I have to wait for the user to click a button and see if it is the correct one. I looked around and it was talking about events, but I couldn't grasp what I needed to do. I would appreciate some help on how to do this.
Thanks!
In the designer, on the properties window, click the lightning bolt icon. You will get a list of events for the selected control. Make sure the btnBouton control is selected, and find the Click event in the list. You should see btnDemarrer_Click in the drop down list. Select it. Now when the button is clicked, it should call your btnDemarrer_Click handler.
When you have not already written a handler, you can double-click the event in the list, and it will generate the method skeleton for you automatically. You can also double-click the control itself to generate the default event handler for that control. (In the case of buttons, I think the default event is the click event.)
If you want a particular method to wait until some work is done, you could look into AutoResetEvent. An extremely simplified example might help you get on the right track:
using System.Threading;
public static AutoResetEvent arEvent = new AutoResetEvent(false);
static void Main()
{
DoWork();
arEvent.WaitOne(); //WaitOne() "pauses" Main and waits for some work to be done.
DoWork();
arEvent.WaitOne();
}
static void DoWork();
{
//Some work is done here.
arEvent.Set(); //This lets Main() continue where it left off.
}
Using this, you could have btnDémarrer_Click wait for the user input then continue on after the user has done his clicking.
What would be the best way to develop a text box that remembers the last x number of entries that were put into it. This is a standalone app written with C#.
This is actually fairly easy, especially in terms of showing the "AutoComplete" part of it. In terms of remembering the last x number of entries, you are just going to have to decide on a particular event (or events) that you consider as an entry being completed and write that entry off to a list... an AutoCompleteStringCollection to be precise.
The TextBox class has the 3 following properties that you will need:
AutoCompleteCustomSource
AutoCompleteMode
AutoCompleteSource
Set AutoCompleteMode to SuggestAppend and AutoCompleteSource to CustomSource.
Then at runtime, every time a new entry is made, use the Add() method of AutoCompleteStringCollection to add that entry to the list (and pop off any old ones if you want). You can actually do this operation directly on the AutoCompleteCustomSource property of the TextBox as long as you've already initialized it.
Now, every time you type in the TextBox it will suggest previous entries :)
See this article for a more complete example: http://www.c-sharpcorner.com/UploadFile/mahesh/AutoCompletion02012006113508AM/AutoCompletion.aspx
AutoComplete also has some built in features like FileSystem and URLs (though it only does stuff that was typed into IE...)
#Ethan
I forgot about the fact that you would want to save that so it wasn't a per session only thing :P But yes, you are completely correct.
This is easily done, especially since it's just basic strings, just write out the contents of AutoCompleteCustomSource from the TextBox to a text file, on separate lines.
I had a few minutes, so I wrote up a complete code example...I would've before as I always try to show code, but didn't have time. Anyway, here's the whole thing (minus the designer code).
namespace AutoComplete
{
public partial class Main : Form
{
//so you don't have to address "txtMain.AutoCompleteCustomSource" every time
AutoCompleteStringCollection acsc;
public Main()
{
InitializeComponent();
//Set to use a Custom source
txtMain.AutoCompleteSource = AutoCompleteSource.CustomSource;
//Set to show drop down *and* append current suggestion to end
txtMain.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
//Init string collection.
acsc = new AutoCompleteStringCollection();
//Set txtMain's AutoComplete Source to acsc
txtMain.AutoCompleteCustomSource = acsc;
}
private void txtMain_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
//Only keep 10 AutoComplete strings
if (acsc.Count < 10)
{
//Add to collection
acsc.Add(txtMain.Text);
}
else
{
//remove oldest
acsc.RemoveAt(0);
//Add to collection
acsc.Add(txtMain.Text);
}
}
}
private void Main_FormClosed(object sender, FormClosedEventArgs e)
{
//open stream to AutoComplete save file
StreamWriter sw = new StreamWriter("AutoComplete.acs");
//Write AutoCompleteStringCollection to stream
foreach (string s in acsc)
sw.WriteLine(s);
//Flush to file
sw.Flush();
//Clean up
sw.Close();
sw.Dispose();
}
private void Main_Load(object sender, EventArgs e)
{
//open stream to AutoComplete save file
StreamReader sr = new StreamReader("AutoComplete.acs");
//initial read
string line = sr.ReadLine();
//loop until end
while (line != null)
{
//add to AutoCompleteStringCollection
acsc.Add(line);
//read again
line = sr.ReadLine();
}
//Clean up
sr.Close();
sr.Dispose();
}
}
}
This code will work exactly as is, you just need to create the GUI with a TextBox named txtMain and hook up the KeyDown, Closed and Load events to the TextBox and Main form.
Also note that, for this example and to make it simple, I just chose to detect the Enter key being pressed as my trigger to save the string to the collection. There is probably more/different events that would be better, depending on your needs.
Also, the model used for populating the collection is not very "smart." It simply deletes the oldest string when the collection gets to the limit of 10. This is likely not ideal, but works for the example. You would probably want some sort of rating system (especially if you really want it to be Google-ish)
A final note, the suggestions will actually show up in the order they are in the collection. If for some reason you want them to show up differently, just sort the list however you like.
Hope that helps!
I store the completion list in the registry.
The code I use is below. It's reusable, in three steps:
replace the namespace and classname in this code with whatever you use.
Call the FillFormFromRegistry() on the Form's Load event, and call SaveFormToRegistry on the Closing event.
compile this into your project.
You need to decorate the assembly with two attributes: [assembly: AssemblyProduct("...")] and [assembly: AssemblyCompany("...")] . (These attributes are normally set automatically in projects created within Visual Studio, so I don't count this as a step.)
Managing state this way is totally automatic and transparent to the user.
You can use the same pattern to store any sort of state for your WPF or WinForms app. Like state of textboxes, checkboxes, dropdowns. Also you can store/restore the size of the window - really handy - the next time the user runs the app, it opens in the same place, and with the same size, as when they closed it. You can store the number of times an app has been run. Lots of possibilities.
namespace Ionic.ExampleCode
{
public partial class NameOfYourForm
{
private void SaveFormToRegistry()
{
if (AppCuKey != null)
{
// the completion list
var converted = _completions.ToList().ConvertAll(x => x.XmlEscapeIexcl());
string completionString = String.Join("¡", converted.ToArray());
AppCuKey.SetValue(_rvn_Completions, completionString);
}
}
private void FillFormFromRegistry()
{
if (!stateLoaded)
{
if (AppCuKey != null)
{
// get the MRU list of .... whatever
_completions = new System.Windows.Forms.AutoCompleteStringCollection();
string c = (string)AppCuKey.GetValue(_rvn_Completions, "");
if (!String.IsNullOrEmpty(c))
{
string[] items = c.Split('¡');
if (items != null && items.Length > 0)
{
//_completions.AddRange(items);
foreach (string item in items)
_completions.Add(item.XmlUnescapeIexcl());
}
}
// Can also store/retrieve items in the registry for
// - textbox contents
// - checkbox state
// - splitter state
// - and so on
//
stateLoaded = true;
}
}
}
private Microsoft.Win32.RegistryKey AppCuKey
{
get
{
if (_appCuKey == null)
{
_appCuKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(AppRegistryPath, true);
if (_appCuKey == null)
_appCuKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(AppRegistryPath);
}
return _appCuKey;
}
set { _appCuKey = null; }
}
private string _appRegistryPath;
private string AppRegistryPath
{
get
{
if (_appRegistryPath == null)
{
// Use a registry path that depends on the assembly attributes,
// that are presumed to be elsewhere. Example:
//
// [assembly: AssemblyCompany("Dino Chiesa")]
// [assembly: AssemblyProduct("XPathVisualizer")]
var a = System.Reflection.Assembly.GetExecutingAssembly();
object[] attr = a.GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true);
var p = attr[0] as System.Reflection.AssemblyProductAttribute;
attr = a.GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), true);
var c = attr[0] as System.Reflection.AssemblyCompanyAttribute;
_appRegistryPath = String.Format("Software\\{0}\\{1}",
p.Product, c.Company);
}
return _appRegistryPath;
}
}
private Microsoft.Win32.RegistryKey _appCuKey;
private string _rvn_Completions = "Completions";
private readonly int _MaxMruListSize = 14;
private System.Windows.Forms.AutoCompleteStringCollection _completions;
private bool stateLoaded;
}
public static class Extensions
{
public static string XmlEscapeIexcl(this String s)
{
while (s.Contains("¡"))
{
s = s.Replace("¡", "¡");
}
return s;
}
public static string XmlUnescapeIexcl(this String s)
{
while (s.Contains("¡"))
{
s = s.Replace("¡", "¡");
}
return s;
}
public static List<String> ToList(this System.Windows.Forms.AutoCompleteStringCollection coll)
{
var list = new List<String>();
foreach (string item in coll)
{
list.Add(item);
}
return list;
}
}
}
Some people shy away from using the Registry for storing state, but I find it's really easy and convenient. If you like, You can very easily build an installer that removes all the registry keys on uninstall.