I have spent few days searching everywhere for a solution to this.
This is for C# with Visual Studio 2013 (ya, I am a newbie):
two textboxes (Last name and First Name) and a listbox with 5 names (Higgins M, Higgins J, King J, Tran A, Dempsey S). I set listbox property as sorted.
if I select Higgins J in listbox, then the word Higgins should appear in Last Name textbox and J should appear in First Name textbox.
if I type Higgins in Last Name textbox, Higgins J should be the selected item in listbox (Higgins J will be selected before Higgins M). If I type M in the First Name textbox, the selected item should change from Higgins J to Higgins M.
but....here are the problems that made me decide to create an account here:
If I type Hi or Hig instead of Higgins, it has to stay that way, it does not become Higgins in the textbox. Only the index/highlight in listbox is changed, not the entry in textbox (whatever I type in the textbox stays). I suspect the events that I use are the reason I cannot get this done. Textbox_textchanged and listbox_selectedindexchanged. So whatever I do in one event will automatically triggers the other. I have tried changing the events, but so far the result simply worse. Using: if (LastName_textbox.Text = "") did not help either.
How do I combine Last Name and First Name as one index?
I apologise if this question has appeared or sounded ambiguous. I suppose I do not know how to phrase the search to get something similar to my problem and English is not my first language. Any help is very much appreciated .Thanks.
Here is part of the codes:
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Project
{
public partial class frmContact : Form
{
//declare file to save all contacts
private string fileName = Directory.GetCurrentDirectory() + "\\Contacts.txt";
//create temporary file for updating and deleting contacts
private string newContacts = Directory.GetCurrentDirectory() + "\\newContacts.txt";
public frmContact()
{
InitializeComponent();
}
private void frmContact_Load(object sender, EventArgs e)
{
//create Contacts.txt if it does not exist
if (!File.Exists(fileName))
{
File.Create(fileName).Close();
MessageBox.Show("New " + fileName +" Has Been Created");
tbLast.Select();
}
//if file already exists
else
{
StreamReader readOb = new StreamReader(fileName);
using (readOb)
{
while (!readOb.EndOfStream)
{
string rdLine = readOb.ReadLine(); //read data in file by line
string[] tmpArr = rdLine.Split(',');
lbContact.Items.Add(tmpArr[0] + "," + tmpArr[1]);
}
tbLast.Select();
}
}
}
private void lbContact_SelectedIndexChanged(object sender, EventArgs e)
{
//show details of contact selected in listbox
string findNames = lbContact.GetItemText(lbContact.SelectedItem);
StreamReader obRead = new StreamReader(fileName);
using (obRead)
{
while (!obRead.EndOfStream)
{
string rdLine = obRead.ReadLine();
if (rdLine.StartsWith(findNames))
{
string[] tmpArr = rdLine.Split(',');
tbLast.Text = tmpArr[0];
tbFirst.Text = tmpArr[1].Trim();
tbAddr.Text = tmpArr[2].Trim();
tbSub.Text = tmpArr[3].Trim();
tbPost.Text = tmpArr[4].Trim();
tbEmail.Text = tmpArr[5].Trim();
tbPhone.Text = tmpArr[6].Trim();
tbMob.Text = tmpArr[7].Trim();
}
}
lbContact.SelectedIndex = lbContact.FindString(findNames);
}
}
private void tbLast_TextChanged(object sender, EventArgs e)
{
lbContact.SelectedItem = lbContact.FindString(tbLast.Text);
}
A simple (but kind of ugly solution) would consist in using a boolean value to inform your lbContact_SelectedIndexChanged method that the index was manually changed thanks to the code. A class member would do the job, something like:
private bool fromCode;
private void lbContact_SelectedIndexChanged(object sender, EventArgs e)
{
if (fromCode)
return;
// Do the job
}
private void tbLast_TextChanged(object sender, EventArgs e)
{
fromCode = true;
lbContact.SetSelected(lbContact.FindString(tbLast.Text), true);
fromCode = false;
}
[Personal remark]
I would also create a Contact struct/class to store your information along with a collection in your form so that you only have to access your file twice:
At loading, so that you can populate your collection
At closing, so that you can save the changes to your file
[Update]
My last remark can be not relevant as I do not have the context in which you are developing your application, that's why I said it was a personal point of view, you don't have to do it.
[Update 2]
What you can do to avoid access your file each time your lbContact_SelectedIndexChanged event is called:
Create a structure or a class to store your contacts information (firstname, lastname, adress, ...)
Create a collection (as a class member of your form) that will contain the contacts (like a List<Contact>)
In your frmContact_Load method, fill this collection with the data contained in the file instead of populating your listbox
So that in your lbContact_SelectedIndexChanged method you will search inside the collection instead of opening your file
Your Add() and Delete() operations must also modify the collection and not the file anymore
Remember to save your collection back to your file at application closing
Hope it helped.
I found the solution (for whoever encounters similar problem), the answer is in the textbox.focused :) and combined with listbox.setselected from Tim.
private void tbLast_TextChanged(object sender, EventArgs e)
{
if (tbLast.Focused && tbLast.Text != "")
{
if (lbContact.FindString(tbLast.Text) > -1)
{
lbContact.SetSelected(lbContact.FindString(tbLast.Text), true);
}
}
}
private void lbContact_SelectedIndexChanged(object sender, EventArgs e)
{
//show details of contact selected in listbox
string findNames = lbContact.GetItemText(lbContact.SelectedItem);
StreamReader obRead = new StreamReader(fileName);
using (obRead)
{
while (!obRead.EndOfStream)
{
string rdLine = obRead.ReadLine();
if (rdLine.StartsWith(findNames))
{
string[] tmpArr = rdLine.Split(',');
if (!tbLast.Focused)
{
tbLast.Text = tmpArr[0];
tbFirst.Text = tmpArr[1].Trim();
tbAddr.Text = tmpArr[2].Trim();
tbSub.Text = tmpArr[3].Trim();
tbPost.Text = tmpArr[4].Trim();
tbEmail.Text = tmpArr[5].Trim();
tbPhone.Text = tmpArr[6].Trim();
tbMob.Text = tmpArr[7].Trim();
}
}
}
lbContact.SelectedIndex = lbContact.FindString(findNames);
}
}
Related
Good afternoon,
I am new to object-oriented programming, .NET and C#. I am studying these previous mentioned topics and presently am doing a relatively simple programming assignment which turned out not to be so simple after all, at least ... for me still.
I want to create a Windows Form Application which contains one form that is filled with country flags (.png, 128px x 128px). In the Form_OnLoad() the files are read and the flags are stored array of PictureBox objects and set several object attributes. Then the form is filled neatly in rows of 8 flags. So far so good.
Problem:
I would like to add a MouseOver event-handler attached to each PictureBox that generates a ToolTip with the country name of the specific flags. In the event-handler I don't know what code to put after what I have managed to do myself already. Specifically, I would like to address the array with flags from the MouseOver event-handler method, but it's not visible from there. I am just stuck here, although my intuition tells me that I am not far from my goal, at this moment my mind decided to give up on me a few meters away from the finish line. Would someone be so kind to help me out with this please?
Here's what I got already:
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace WorldFlags
{
public partial class FormWorldFlags :Form {
public FormWorldFlags() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
int col = 0, row = 0;
string imageDirectory = #"D:\Documents\Visual Studio 2017\Projects\ITvitae\WorldFlags\flags\";
string[] imageFileList = Directory.GetFiles(imageDirectory);
PictureBox[] countryFlag = new PictureBox[imageFileList.Length];
for (int i = 0; i < imageFileList.Length; i++) {
countryFlag[i] = new PictureBox();
countryFlag[i].Name = Path.GetFileNameWithoutExtension(imageFileList[i]);
countryFlag[i].Image = Image.FromFile(imageFileList[i]);
countryFlag[i].Location = new Point(col * 128 + 1, row * 128 + 1);
countryFlag[i].Size = new Size(128, 128);
countryFlag[i].MouseHover += FormWorldFlags_MouseHover;
if (col + 1 == 8) {
row++;
col = 0;
} else
col++;
Controls.Add(countryFlag[i]);
}
}
private void FormWorldFlags_MouseHover(object sender, EventArgs e) {
ToolTip countryName = new ToolTip();
countryName.SetToolTip(?????)
}
}
};
Thanks so much in advance.
Joeri van der Heijden
change your handler to the following:
private void FormWorldFlags_MouseHover(object sender, EventArgs e) {
ToolTip countryName = new ToolTip();
countryName.SetToolTip(sender, "country name");
}
If you want to access the array within the handler you can can just move it out of the form load function. Alternatively you can make use of the Tag property when loading the images and access the value in the handler.
I have Single.cs and Periodic.cs files, which have Button_Click events, and user should add some information into same file by pressing these buttons. Implementation of Button_Click is almost same, this is why I show only one them.
using System.IO;
//.........
namespace ModernUIApp1.Pages
{
public partial class Home : UserControl
{
private void Button_Click(object sender, RoutedEventArgs e)
{
// this is how i get required path
var systemPath = System.Environment.GetFolderPath(
Environment.SpecialFolder.CommonApplicationData);
var _directoryName1 = Path.Combine(systemPath, "RadiolocationQ");
string script = "script";
string path_scriprt1 = Path.Combine(_directoryName1, script+".xml");
cur_script.Add(new XElement("Single_Request" + Convert.ToString(num),
new XElement("numRequest", TxtBlock_numRequest.Text),
));
cur_script.Save(path_scriprt1);
num++; // I use this to create unique name
}
XElement cur_script = new XElement("Requestes");
int num = 1;
}
}
Eventually, user can push buttons many times, and this code will work fine if I dont use these buttons at the same time. Because otherwise it simply overwrites existing information. So the problem is to make this XElement cur_script = new XElement("Requestes"); global. Or do you have some other ways out?
The real issue is that you need to avoid concurrency issue here. What if you have two persons try to update the same file and the following sequences occurs:
User 1 open and load the file;
User 2 open and load the file;
User 1 modify the xml and save it;
User 2 modify the xml and save it;
In this case the change made by user 1 will be lost.
To avoid this problem you want to keep the file open until your have saved it. For example:
using (FileStream stream =
File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
....load xml, modify it, and save it
}
public void WriteToDoc(XmlDocument doc, XElement element)
{
// this method can reside somewhere outside.
// some generic doc loader can load the doc
// add element to doc
}
public void OnButton1Click(object sender, RoutedEventArgs e)
{
// do something with element
WriteToDoc( mydoc, myNewElement)
}
public void OnButton2Click(object sender, RoutedEventArgs e)
{
// do something else with element
WriteToDoc( mydoc, myNewElement)
}
V2
public class ElementAdder
{
public static void WriteToDoc(string path, XElement element)
{
// load doc based on path
// add element to doc
}
}
public void OnButton1Click(object sender, RoutedEventArgs e)
{
// do something with element
string path = ConfigMan.GetDocPath();
ElementAdder.WriteToDoc(path, myNewElement);
}
public void OnButton2Click(object sender, RoutedEventArgs e)
{
// do something else with element
string path = ConfigMan.GetDocPath();
ElementAdder.WriteToDoc(path, myNewElement);
}
Thank you for help, but I solved problem on my own. I just used tips how to write into same xml-file, this way:
XDocument Requestes = XDocument.Load(path_scriprt1);
XElement newElement = new XElement("Single_Request" + Convert.ToString(num),
new XElement("numRequest", TxtBlock_numRequest.Text),
new XElement("IDWork", TxtBlock_IDWork.Text),
new XElement("NumObject", TxtBlock_NumObject.Text),
new XElement("lvlPriority", CmbBox_lvlPriority.Text),
new XElement("NumIn1Period", TxtBlock_NumIn1Period.Text)
);
Requestes.Descendants("Requestes").First().Add(newElement);
Requestes.Save(path_scriprt1);
i try to use effective caching but i face to face a problem. For example; i have 5 user they have used my app. user1,2,3,4 only fill grid by searcing(Caching is run!!!). on the other hand user5 adding new row. i want to refresh my cach data when adding new row. i read Multi threading to do that
code>using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;
using System.Collections;
namespace WebApp.Caching.Threading
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
FillCache();
}
void FillCache()
{
using (var myCtx = new DataClasses1DataContext())
{
if (!(FlyAntCache.Exists("test")))
{
List<Table_1> toolStoreList = myCtx.Table_1s.ToList();
FlyAntCache.Add(toolStoreList, "test");
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
WaitCallback method1 = new WaitCallback(ControlAllChanging);
bool isQueued = ThreadPool.QueueUserWorkItem(method1, new ManualResetEvent(false));
}
protected void ControlAllChanging(object state)
{
if (FlyAntCache.Exists("test"))
{
using (var myCtx = new DataClasses1DataContext())
{
List<Table_1> list;
list = myCtx.Table_1s.ToList();
List<Table_1> listCache = FlyAntCache.Get<List<Table_1>>("test");
bool IsIntersect = list.Except(listCache).Count() > 0;
if (IsIntersect)
{
FlyAntCache.Clear("test");
FillCache();
}
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
// Search
using (var myCtx = new DataClasses1DataContext())
{
var Qry = myCtx.Table_1s.
FromCache<Table_1>("test").
AsQueryable().Where(t => t.ad == TextBox1.Text.Trim());
GridView1.DataSource = Qry;
GridView1.DataBind();
}
}
}
}
My Scenario:
LOOK please :http://i53.tinypic.com/20pdc41.png
i really control if another user change my data, i must refresh my cache. is there any sensitivity to CAPTURE any new changing update new row save. for example :
1) i must capture new update . this mechanizm must run when changes occurs
2) i must capture new save. this mechanizm must run when new row adds
I'm still not quite sure what you're asking. My best guess is it sounds like you're trying to let a cache know when its data is stale.
Most caching implementations have this built in. Basically, you can expire a cache item (usually be removing it from the cache) when it has been updated.
For example, if you're just using the plain old built in caching that comes with ASP.net:
private static Cache Cache;
public void AddItem(string data)
{
//Do a database call to add the data
//This will force clients to requery the source when GetItems is called again.
Cache.Remove("test");
}
public List<string> GetItems()
{
//Attempt to get the data from cache
List<string> data = Cache.Get("test") as string;
//Check to see if we got it from cache
if (data == null)
{
//We didn't get it from cache, so load it from
// wherever it comes from.
data = "From database or something";
//Put it in cache for the next user
Cache["test"] = data;
}
return data;
}
UPDATE I updated the code sample to return a list of strings instead of just a string. This should make it more obvious what is happening.
To reiterate, the GetItems() call retrieves a list of strings. If that list is in cache, the cached list is returned. Otherwise, the list is retrieved and cached / returned.
The AddItem method explicitly removes the list from the cache, forcing the requery of the data source.
Not sure but are you looking for events? You could set up events in your caching mechanism to fire when an update occurs.
Here is a googled example
I have so many issues with my project, i really don't know where to begin. First off, i get an error "an object reference is required for non-static field, method or property". It underlines retPath (the line: DriveRecursion_results.DriveRecursion(retPath);). I have no idea how to fix this.
THe other thing i'm still stumped on is how to populate a listview on my Windows Form. What i want is a list of files that need to be renamed (versus a list of all files in my list.)
Can anybody help? I have been struggling miserably with this for several hours now.
Here is my code:
Form1.cs:
namespace FileMigration
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
FolderSelect("Please select:");
}
public string FolderSelect(string txtPrompt)
{
//Value to be returned
string result = string.Empty;
//Now, we want to use the path information to population our folder selection initial location
string initialCheckoutPathDir = (#"C:\");
System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(initialCheckoutPathDir);
FolderBrowserDialog FolderSelect = new FolderBrowserDialog();
FolderSelect.SelectedPath = info.FullName;
FolderSelect.Description = txtPrompt;
FolderSelect.ShowNewFolderButton = true;
if (FolderSelect.ShowDialog() == DialogResult.OK)
{
string retPath = FolderSelect.SelectedPath;
if (retPath == null)
{
retPath = "";
}
DriveRecursion_Results ds = new DriveRecursion_Results();
ds(retPath);
result = retPath;
//Close this form.
}
return result;
}
}
}
Here is DriveRecursion_Results.cs:
namespace FileMigration
{
public partial class DriveRecursion_Results : Form
{
public DriveRecursion_Results()
{
InitializeComponent();
}
private void fileOutput_SelectedIndexChanged(object sender, EventArgs e)
{
}
public void DriveRecursion(string retPath)
{
// string[] files = Directory.GetFiles(retPath, "*.*", SearchOption.AllDirectories);
string pattern = " *[\\~#%&*{}/<>?|\"-]+ *";
string replacement = "";
Regex regEx = new Regex(pattern);
string[] fileDrive = Directory.GetFiles(retPath, "*.*", SearchOption.AllDirectories);
List<string> filePath = new List<string>();
foreach (string fileNames in fileDrive)
{
if (regEx.IsMatch(fileNames))
{
filePath.Add(fileNames);
//I tried adding my listview (fileOptions) here but I cannot for some reason
}
}
}
}
}
ANY help would really be appreciated :( Does anybody have any ideas on how to change my code so it actually works?
Issue 1: your function is static. If it stops being such, this will work. This is because a static function does not have this hidden 'this' argument - the reference to object it acts upon. So, it can only access static data members, not regular ones.
You can't add the items to your listview from that level because the listview is non-static and the method DriveRecursion is static. I would start by changing the DriveRecursion method to be non-static or return a list of file paths.
You are not able to add items to your list view because you are trying to add them from a static method.
Since it is static there is no ListView because there isn't actually a Form to add things to. You will need to make DriveRecursion() not static in order to add things to the ListView.
Additionally, when you make DriveRecursion() not static, you will need a way to let Form1 know which DriveRecursion_Results class to populate.
Another approach you could take is having Form1 return retPath to DriveRecursion_Results.
Edit
Removed irrelevant parts of my original answer
I have copied your code exactly how you posted it. And then made the following changes to FolderSelect() in Form1.cs When I run this code. I can get the second window to pop up, but not the other window to close, because that will cause the application to quit.
Please make sure you have ds.Show() and do at some point call ds.DriveRecursion(retPath)
Modified FolderSelect(string) in Form1.cs:
private void FolderSelect( string txtPrompt )
{
//Value to be returned
string result = string.Empty;
//Now, we want to use the path information to population our folder selection initial location
string initialCheckoutPathDir = ( "C:\\" );
System.IO.DirectoryInfo info = new System.IO.DirectoryInfo( initialCheckoutPathDir );
FolderBrowserDialog FolderSelect = new FolderBrowserDialog();
FolderSelect.SelectedPath = info.FullName;
FolderSelect.Description = txtPrompt;
FolderSelect.ShowNewFolderButton = true;
if( FolderSelect.ShowDialog() == DialogResult.OK )
{
string retPath = FolderSelect.SelectedPath;
if( retPath == null )
{
retPath = "";
}
DriveRecursion_Results ds = new DriveRecursion_Results();
ds.DriveRecursion( retPath );
ds.Show();
result = retPath;
//Close this form.
}
return;
}
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.