I've got a bit of a problem with treeview and how the indexing of nodes works. In my program, I have a database that can contain any amount of users. Each user is separated by a carriage return (i.e. one user per line). I'm creating a treeview object that lists all users in the database. If the user clicks on a specific node, how do I refer to that node / handle it being selected, as I am dynamically making nodes from the database?
StreamReader getMembers = new StreamReader(#"[data]\db\users.db");
List<string> mems = new List<string>();
members.Nodes.Add("Members");
while (!getMembers.EndOfStream)
{
mems.Add(getMembers.ReadLine());
}
foreach (string o in mems)
{
TreeNode n = new TreeNode(o);
members.Nodes[0].Nodes.Add(n);
}
Database & Program:
If you are trying to get the tree node that was selected you can achieve that by the TreeView.SelectedNode property... (http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.selectednode.aspx)
if you want to handle an event on treenode selected register the TreeView.AfterSelect event (http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.afterselect)
example:
private void TreeView1_AfterSelect(System.Object sender,
System.Windows.Forms.TreeViewEventArgs e)
{
// Vary the response depending on which TreeViewAction
// triggered the event.
switch((e.Action))
{
case TreeViewAction.ByKeyboard:
MessageBox.Show("You like the keyboard!");
break;
case TreeViewAction.ByMouse:
MessageBox.Show("You like the mouse!");
break;
}
}
Assuming you are using the standard forms treeview, it sounds like you want to subscribe to the event on the TreeView.AfterSelect.
// Handle the After_Select event.
private void TreeView1_AfterSelect(System.Object sender,
System.Windows.Forms.TreeViewEventArgs e)
{
// If (TreeView1.SelectedNode...
}
Related
I have a TreeView that contains database objects that are basically folders. I want to be able to click on a "folder" in the tree and have it populate a set of controls with data about that "folder". While this all works fine with the code I've written, the issue is that using the arrow keys on the keyboard to go up and down the folder list will eventually hang the application. My assumption is that the background worker I am using to populate the controls is getting hung up.
I've searched and I can't find anything similar to my issue.
Here's my tree view afterselect code.
private void dmTree_AfterSelect(object sender, TreeViewEventArgs e)
{
object[] tagParts = e.Node.Tag as object[];
SelectedFolderNumber = tagParts[1].ToString();
if (!String.IsNullOrEmpty(SelectedFolderNumber) && SelectedFolderNumber != "0")
{
//update mini profile
if (bgwMiniProfile.IsBusy)
{
bgwMiniProfile.CancelAsync();
}
while (bgwMiniProfile.CancellationPending)
{
Application.DoEvents();
}
bgwMiniProfile.RunWorkerAsync();
while (bgwMiniProfile.IsBusy)
{
Application.DoEvents();
}
securityPanel.DisplayTrusteeList(folderTrustees);
}
}
securityPanel is a user control on the form.
Here is the DisplayTrusteeList code
public void DisplayTrusteeList(List<DocumentTrustee> documentTrustees)
{
try
{
dgvTrustees.Rows.Clear();
foreach (DocumentTrustee dt in documentTrustees)
{
dgvTrustees.Rows.Add(imagePG.Images[(int)dt.TrusteeType], dt.GetFullName(dmLogin), dt.AccessRights);
}
foreach (DataGridViewRow myRow in dgvTrustees.Rows)
{
ValidateRights(int.Parse(myRow.Cells["dmRights"].Value.ToString()), myRow);
}
dgvTrustees.ClearSelection();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "DisplayTrusteeList");
}
}
And here is the background worker:
private void bgwMiniProfile_DoWork(object sender, DoWorkEventArgs e)
{
if (!bgwMiniProfile.CancellationPending)
{
SetText(txtDocNumber, SelectedFolderNumber);
SetText(txtDocName, Utility.GetProfileValue(adminLogin, SelectedFolderNumber, "DOCNAME"));
SetText(txtClientId, Utility.GetProfileValue(adminLogin, SelectedFolderNumber, "CLIENT_ID"));
SetText(txtClientName, Utility.SetDescription(adminLogin, "CLIENT", txtClientId.Text));
SetText(txtMatterId, Utility.GetProfileValue(adminLogin, SelectedFolderNumber, "MATTER_ID"));
SetText(txtMatterName, Utility.SetDescription(adminLogin, "CLIENT", txtClientId.Text, txtMatterId.Text));
folderTrustees = Utility.GetFolderTrustees(adminLogin, SelectedFolderNumber);
}
else
{
e.Cancel = true;
}
}
I would like to be able to cursor through the tree nodes with the arrow keys and not have the after select code fire until the user lands on a node and stays there for a few seconds. Is that possible?
Thanks and this is my first question. Sorry if the format ins't great. I've used a lot of solutions from here.
I found a better way. Instead of using AfterSelect I'm using NodeMouseClick. This mirrors Windows Explorer functionality. Now the user can cursor up and down the folder tree without any issues. The data to the right will fill in only when they click on the node. This works for me perfectly.
I'm trying to find out how to force node to create only one node child of other type ?
I have 'hotel' node and i want to add one 'rooms' node and one 'facilities' node ('rooms' create 'room' and 'facilities' create 'facility')
by default when i will create hotel the system will create this 2 folders or to force him that he can create only one each
I prefer to do it with the management system (if possible) but code behind be good also !
Thanks in advance for all helpers!
As far as I am concerned, I do not know a way to achieve this by means of the Umbraco backoffice.
However, you can use the ContentService Events in order to cancel the creation of that particular page if it has been already created. The ContentServiceSaving method will fire up each time that you try to create a node in the CMS.
public class ContentServiceEventsController : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
ContentService.Saving += ContentServiceSaving;
}
private void ContentServiceSaving(IContentService sender, SaveEventArgs<IContent> e)
{
switch (e.Entity.ContentType.Alias)
{
case "RoomsDocumentTypeAlias":
case "FacilitiesDocumentTypeAlias":
// Do your logic to detect if your Hotel node "Hilton eilat queen of sheba"
// already contains either a Rooms node or a Facilities node underneath.
// If so, cancel the creation of the event of a new one.
e.Cancel = true;
break;
default:
break;
}
}
My point is to display number of all child nodes at StatusStripLabel. My point is that I want StatusStripLabel to get updated everytime number of child nodes get's changed - I'll add or delete some of already exists. First, I placed code in Public Form, but it didn't worked as I expected. After some time I came with idea that actually works: I placed the code inside button method. But after that I realized that I'll need to place it in second place, in case of deleting node. So My question is: is there anything that can make it simplier? If my explanation isn't enough, just tell me, I'll try my best.
Code from Public Form (Because I want that counter to work from the beggining, not after I'll press the button)
childNodeCounter();
toolStripStatusLabel1.Text = "Number of games in database: " + NodeCounter.ToString();
Method:
public void childNodeCounter()
{
NodeCounter = 0;
foreach (TreeNode RootNode in treeView1.Nodes)
{
foreach (TreeNode ChildNode in RootNode.Nodes)
{
NodeCounter++;
}
}
toolStripStatusLabel1.Text = "Number of games in database: " + NodeCounter.ToString();
}
Code inside button method:
private void button1_Click(object sender, EventArgs e)
{
NodeCounter = 0;
foreach (TreeNode RootNode in treeView1.Nodes)
{
foreach (TreeNode ChildNode in RootNode.Nodes)
{
NodeCounter++;
}
}
toolStripStatusLabel1.Text = "Number of games in database: " + NodeCounter.ToString();
}
Edit: Thanks to mr. Hans Passant I wrote this and it works very well:
public int childNodeCounter(TreeNodeCollection nodes)
{
int count = 0;
foreach (TreeNode RootNode in nodes)
{
foreach (TreeNode ChildNode in RootNode.Nodes)
count++;
}
return count;
Event handler looks like this:
toolStripStatusLabel1.Text = "Number of games in database: " + childNodeCounter(treeView1.Nodes);
Three tiny optimizations
Rather that iterate the tree yourself, just use ChildNode.Nodes.GetNodeCount
Rather than repeat the same logic in different places, have your button Click events simply call the UpdateNodeCount() method.
The text initializer in the first code fragment is redundant, and can be eliminated: the call to childNodeCounter already does the status label update.
The natural way to traverse a tree structure is by using recursion. That's always a bit hard to reason through but there are lots of resources available. Doing it iteratively is a lot uglier, you have to use a Stack<> to allow you to backtrack again out of a nested node. I'll therefore post the recursion solution:
private static int CountNodes(TreeNodeCollection nodes) {
int count = nodes.Count;
foreach (TreeNode node in nodes) count += CountNodes(node.Nodes);
return count;
}
Then your event handler becomes:
private void button1_Click(object sender, EventArgs e) {
toolStripStatusLabel1.Text = "Number of games in database: " +
CountNodes(treeView1.Nodes);
}
If you're adding and removing "game" nodes to the treeView, you must be having methods like void AddGame(string title) and void RemoveGame(string title) which add/remove (child) nodes - those whose total number you want to count. If I understood well, you want toolStripStatusLabel1.Text to be automatically updated each time the number of child nodes changes. In that case you can add field
private int nodesCount;
to your Form class and have something like this:
void AddGame(string title)
{
if(InvokeRequired)
{
Invoke(new MethodInvoker(delegate() { AddGame(title); }));
}
else
{
AddGameNodeToTreeView(title); // add new game node to desired place in TreeView
nodesCount++; // increase node counter
toolStripStatusLabel1.Text = "Number of games in database: " + nodesCount;
}
}
RemoveGame() would be implemented in the same way (or joined with AddGame() into a single method with one additional argument - bool add). Both methods could be extended if you're adding/removing multiple nodes in which case you'll be passing title array and updating nodesCount accordingly.
The advantage of this approach is that you don't have to count nodes in the tree each time before updating toolStripStatusLabel1.Text. Also, toolStripStatusLabel1.Text is updated automatically, not only when user clicks the button.
Drawback is that nodesCount is somewhat redundant information: total number of nodes of interest is 'hidden' in the treeView. You have to make sure that nodesCount is in sync with the actual number of nodes.
I am currently working on Windows Store App in c#.
Now,
I am having a list box 'Listbox1' which gets its items on a button click event from a text box 'tasks', and have selected Items delete property on other button click event.
private void add_Click(object sender, RoutedEventArgs e)
{
string t;
t = tasks.Text;
if (t != "")
{
Listbox1.Items.Add(t);
}
else
{
var a = new MessageDialog("Please Enter the Task First");
a.Commands.Add(new UICommand("Ok"));
a.ShowAsync();
}
tasks.Text = "";
}
private void del_Click(object sender, RoutedEventArgs e)
{
for (int p = 0; p < Listbox1.SelectedItems.Count; p++)
{
Listbox1.Items.Remove(Listbox1.SelectedItems[p].ToString());
p--;
}
}
Now I want to save this list into local application storage, after user complete the changes (on a button click event perhaps).
And also to send all Listbox Items to another page(s).
I am not much a coder, I design things.
Please guide me by sample or reference.
Thank you in advance :)
If you have already stored the data to local storage, you could just read it in the OnNavigatedTo override of the other page. Otherwise, use the navigation parameter: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8cb42356-82bc-4d77-9bbc-ae186990cfd5/passing-parameters-during-navigation-in-windows-8
Edit: I am not sure whether you also need some information about local storage. This is easy: Windows.Storage.ApplicationData.Current.LocalSettings has a property called Values, which is a Dictionary you can write your settings to. Have a look at http://msdn.microsoft.com/en-us/library/windows/apps/hh700361.aspx
Edit: Try something like this code to store your list.
// Try to get the old stuff from local storage.
object oldData = null;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
bool isFound = settings.Values.TryGetValue("List", out oldData);
// Save a list to local storage. (You cannot store the list directly, because it is not
// serialisable, so we use the detours via an array.)
List<string> newData = new List<string>(new string[] { "test", "blah", "blubb" });
settings.Values["List"] = newData.ToArray();
// Test whether the saved list contains the expected data.
Debug.Assert(!isFound || Enumerable.SequenceEqual((string[]) oldData, newData));
Note, this is only demo code for testing - it does not make real sense...
Edit: One advice: Do not persist the list in your click handlers as this will become extremely slow as the list grows. I would load and save the list in the Navigation handlers, i.e. add something like
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
if (this.ListBox1.ItemsSource == null) {
object list;
if (ApplicationData.Current.LocalSettings.Values.TryGetValue("List", out list)) {
this.ListBox1.ItemsSource = new List<string>((string[]) list);
} else {
this.ListBox1.ItemsSource = new List<string>();
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
if (this.ListBox1.ItemsSource != null) {
ApplicationData.Current.LocalSettings.Values["List"] = this.ListBox1.ItemsSource.ToArray();
}
base.OnNavigatedFrom(e);
}
Here is very nice simple example on SQLite DataBase Use in winRT app Development. Look at it and you will know how you can store your Data on the Local Machine. I learned Basic code from this example.
http://blogs.msdn.com/b/robertgreen/archive/2012/11/13/using-sqlite-in-windows-store-apps.aspx
Now, for ease of navigation let me suggest you a flow for this portion of your app.
take one ObservableCollection<> of string and store values of
that textBox into this ObservationCollection with onClick() and then
refer that ObservableCollection<String> to the ItemsList of the
listBox.
now at the time you need to send your Data to the next page, make one parameterised constructor of next page and pass that ObservableCollection<String> as it's parameter.
Now you can access those Data in your constructor and can use as however you want.
Hope this will help..
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.