Using XML elements - c#

I have working code that I'd simply like to reference parts of multiple times in other area's of my program, however my problem seems to be with anonymous types (var)
here's a section of my program that loads my XML file
string path = "Data//handling4.meta";
var doc = XDocument.Load(path);
var items = doc.Descendants("HandlingData").Elements("Item");
var query = from x in items
select new
{
HandlingName = (string)x.Element("handlingName"),
HandlingType = (string)x.Element("HandlingType"),
Mass = (decimal?)x.Element("fMass").Attribute("value"),
InitialDragCoeff = (decimal?)x.Element("fInitialDragCoeff").Attribute("value"),
PercentSubmerged = (decimal?)x.Element("fPercentSubmerged").Attribute("value"),
DriveBiasFront = (decimal?)x.Element("fDriveBiasFront").Attribute("value")
};
This code works great, I can also use a foreach loop like this to reference items
foreach(var HandlingName in query)
{
string Names = HandlingName.HandlingName;
}
So my question is, how to reference that to another place in the program? for e.g.
private void button1_Click(object sender, EventArgs e)
{
comboBox1.Items.Add( Names);
}
The above code throws the error 'Names does not exist in the current context'
I need for my comboBox to display each handling name, but it fails because anonymous types cannot be referenced. Am I doing something wrong, or am I just not using the incorrect method? Thanks
Edit: Also to mention that the XML is needing to be referenced in multiple parts of the program, button click even shows the values in a DataGridView for example. If I could make a class to load the XML, then each element in different area's of the program that'd be ideal, rather than loading the hefty block of code each time

the XML is needing to be referenced in multiple parts of the program,
But all your variables are local. You need to store something in a Form fiead or property.
// outside any method:
private List<string> names = new List<string>();
void myLoadMethod()
{
...
foreach(var HandlingName in query)
{
//string Names = HandlingName.HandlingName;
Names.Add(HandlingName.HandlingName);
}
}
private void button1_Click(object sender, EventArgs e)
{
comboBox1.Items.Add( Names);
}

Related

Unwanted member change because of reference

I am writing a piece of code but cannot figure out why it is showing some (from my point of view) weird behaviour. I get this is due to how I have written the code, but I cannot understand why it is not doing what I want it to do.
I have an eventhandler:
private void heatFinishedHandler(object sender, List<Kart> e)
{
mainForm.enableControls();
server.karts = e;
}
If I add e.Clear(); to it, it also clears server.karts.
The argument e comes from another class:
private List<Kart> kartlist = new List<Kart>();
heatFinished?.Invoke(this, kartlist);
The heatFinished event and the heatFinishedEventHandler are connected.
What do I need to do to get e by value, instead of reference? Or is it a value type, but is it pointing to kartlist?
I want to be able to get the value of e and store it in server.karts, not being able to change kartlist.
Make a copy of the list you pass to the function like
private List<Kart> kartlist = new List<Kart>();
heatFinished?.Invoke(this, kartlist.ToList());
Or do it inside the function like
private void heatFinishedHandler(object sender, List<Kart> e)
{
mainForm.enableControls();
server.karts = e.ToList();
}
.ToList() will make a new List-instance with the same values as the original one.

save and load Listbox Items locally and pass them to other pages

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..

Use of 'this' in an event

I need to retrieve some property of an element that trigger the event in this context:
SoundEffect alarm;
public MainPage()
{
InitializeComponent();
Pad1.MouseLeftButtonUp += new MouseButtonEventHandler(makeasound);
Pad2.MouseLeftButtonUp += new MouseButtonEventHandler(makeasound);
Pad3.MouseLeftButtonUp += new MouseButtonEventHandler(makeasound);
}
Pad1,2 and 3 are the names of some Ellipse I have in my xaml. Now if I try to do this in the code executed by the event ( declared immediately after the sample code above):
private void makeasound(object sender, MouseButtonEventArgs e)
{
string text = this.Name;
textBlock1.Text = text;
}
The Text Block becomes empty, so I guess the Name of the triggering element never gets there.
Now, things get more messy if I'm trying to retrieve a custom property of the "pads" called "Son", declared with the dependency method , which is a string, like this:
private void makeasound(object sender, MouseButtonEventArgs e)
{
string text = this.Son;
textBlock1.Text = text;
}
VS reports error:
'PhoneApplication.MainPage' does not contain a definition for 'Son' and no extension method 'Son' accepting a first argument of type 'PhoneApplication.MainPage' could be found (are you missing a using directive or an assembly reference?)
Where Phoneapplication is the name of the app and the main namespace of the code behind.
As if it weren't simple enough, what I'm tryin to do is this:
The custom property is actually an INT. I know I declared the dependency right since VS let me compile. Each Pad has this custom property storing an int, and I need to retrieve it to access an array element. The function triggered is this:
private void makeasound(object sender, MouseButtonEventArgs e)
{
int x = this.Son;
var sons = new[] { "sons/firstsound.wav", "sons/secondsound.wav", "sons/thirdsound.wav" };
string target = sons[x];
StreamResourceInfo info = Application.GetResourceStream(
new Uri(target, UriKind.Relative));
alarm = SoundEffect.FromStream(info.Stream);
Microsoft.Xna.Framework.FrameworkDispatcher.Update();
alarm.Play();
}
So, I declare an array storing URI's for sounds that I'd like to play("son" means sound in french, I'm from Belgium). I then use the INT associated with the triggering element to access the URI of a sound, then I play this sound.
The reason I do this is because I'd like to let the user change the INT value for each pad and therefore choose what sound each pad plays. The fact that I seem to have no choice but to declare this array each time the function is called (otherwise it's not in context) is not very elegant but I guess I can live with that ( array will have 50-60 elements in it)
So, for those who read this far, my problem is to use a property of the triggering event, which seems to be harder when it's a custom property. I put the rest of the logic in case someone had advices.
I thank anyone who read this message and who could maybe help me sorting this out. I read online documentation and I have two good c# books, but I havent found a solution for me.
Have a nice day.
EDIT: Some others are willing to help so here is the declaration of the dependency property(Sorry Daniel, hadn't seen you commented my original post)
namespace MyNamespace
{
public static class MyClass
{
public static readonly DependencyProperty SonProperty = DependencyProperty.RegisterAttached("Son",
typeof(string), typeof(MyClass), new PropertyMetadata(null));
public static string GetSon(UIElement element)
{
if (element == null)
throw new ArgumentNullException("element");
return (string)element.GetValue(SonProperty);
}
public static void SetSon(UIElement element, string value)
{
if (element == null)
throw new ArgumentNullException("element");
element.SetValue(SonProperty, value);
}
}
Mynamespace is nested inside the main namespace.
this refers to the current instance of MainPage. Not to the pad that was clicked. That's the sender:
var pad = (Pad)sender;
var text = pad.Name;
textBlock1.Text = text;
Son is an attached property, not a normal one. You can get its value like this:
var pad = (Pad)sender;
var son = MyClass.GetSon(pad);
Please note that you have declared it as a string. Seeing how you want to use it, it seems to make more sense to declare it as an int.
this refers to the instance of the type in which the method is defined in, in this case, MainPage. If you want to get the instance of the type that triggered the event, that's what's in the sender parameter:
private void makeasound(object sender, MouseButtonEventArgs e)
{
textBlock1.Text = ((Pad)sender).Name;
}

Labels removed from this.Controls keep reappearing

I am a novice C# programmer, trying to create a simple RSS/Atom aggregator webpart for a SharePoint site (I can't use the ootb part as I don't have an Enterprise licence).
I have 3 buttons:
New Feed, which takes a URI from the user and attempts to construct a label displaying the feed and add it to this.Controls;
Clear Feeds, which clears all the displayed feeds (removing them from this.Controls)
Default Feeds, which takes an ArrayList of default URIs and converts them each into labels (also adding them to this.Controls).
Clearing the feeds works fine, as does reverting to the defaults. However, after clearing all the feeds and trying to add a new feed, the defaults are added as well (and only one new feed can be added at a time, the new one overwriting the old). I suspect this is because I don't fully understand the add/remove controls function. Code follows below:
ArrayList viewedFeeds = new ArrayList();
ArrayList defaultFeeds = new ArrayList(); //Contains several default feeds, e.g. BBC news (http://feeds.bbci.co.uk/news/rss.xml)
private void newFeed_Click(object sender, EventArgs e)
{
renderFeed(userText.Text);
}
private void clearFeeds_Click(object sender, EventArgs e)
{
clearAllFeeds();
}
private void defaultFeeds_Click(object sender, EventArgs e)
{
clearAllFeeds();
initialiseFeedViewer();
}
private void clearAllFeeds()
{
foreach (Label feed in viewedFeeds)
{
this.Controls.Remove(feed);
}
viewedFeeds.Clear();
}
private void initialiseFeedViewer()
{
foreach (string uri in defaultFeeds)
renderFeed(uri);
}
private void renderFeed(String uri)
{
try
{
Label feed = new Label();
// Create a Syndicated feed reader, parse the XML and add the relevant text to the label "feed"
feed.BorderStyle = System.Web.UI.WebControls.BorderStyle.Double;
viewedFeeds.Add(feed);
this.Controls.Add(feed);
}
catch (Exception ex)
{
//Print an error message (e.g. If the URI does not link to a suitable feed
}
}
It turns out I was calling initialiseFeedViewer() more than once - it was in the createChildControls() method. I assumed this was only called once, similar to a constructor, but it seems to be called every time the page is loaded.
This has the annoying side effect of clearing all of my variables, so the List<Label>s I'm using to keep track of displayed feeds are rendered useless.
I'll attempt to get around this by storing them in a Sharepoint List instead.
For you're problem of being unable to add more than one control at a time. Try setting the "Name" property of the label before you try adding it to the arraylist and the "panel" you are using to hold you're label controls.
private void renderFeed(String uri)
{
try
{
Label feed = new Label();
feed.Name = uri;
//Create a Syndicated feed reader, parse the XML and add the relevant text to the label "feed"
feed.BorderStyle = System.Web.UI.WebControls.BorderStyle.Double;
viewedFeeds.Add(uri);
this.Controls.Add(feed);
}
catch (Exception ex)
{
//Print an error message (e.g. If the URI does not link to a suitable feed
}
}
As for it rendering your defaults when you add a new one. I would just make sure you're not calling the initialiseFeedViewer() on accident.
Just as a sidenote, arraylist is considered to be depreciated. Consider using List instead.
List<string> viewedFeeds = new List<string>();
List<string> defaultFeeds = new List<string>();

Google Suggestish text box (autocomplete)

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.

Categories

Resources