I am trying to write an editor for an xml config file. I have the xml file bound to a listview and selecting an element and then clicking edit allows you to edit an element. When the user clicks save, a delegate is called which updates the element within the xml document. I have tried to do the updating in various ways including appendnode, prependnode, insertafter, and replace child. I have gotten them all working without any compile time or runtime errors, yet they don't update the xmldataprovider or the listview. It's probably just something simple that I am missing but I am having trouble figuring out what it is. Can someone please help?
Thanks,
Brian
If it helps, here's the source code of my main form (the one with the listview)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.IO;
namespace g2config
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public XmlDataProvider dp;
string cfgfile;
public managecmpnts manage_components;
public delegate void managecmpnts(int id, XmlElement component);
public MainWindow()
{
InitializeComponent();
dp = this.FindResource("configfile") as XmlDataProvider;
cfgfile = "C:\\Users\\briansvgs\\Desktop\\g2config\\g2config\\bin\\Debug\\g2config.pxml";
if(Environment.GetCommandLineArgs().Count() > 1)
{
string path = Environment.GetCommandLineArgs().ElementAt(1);
//MessageBox.Show("Path: " + path);
cfgfile = path;
}
if (File.Exists(cfgfile))
{
dp.Source = new Uri(cfgfile, UriKind.RelativeOrAbsolute);
}
else
{
MessageBox.Show("config file not found");
}
this.Title = this.Title + " (" + cfgfile + ") ";
}
public void browsedir( object sender, EventArgs e)
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Filter = "PXML Files (*.pxml)|*.pxml"; ;
//http://www.kirupa.com/net/using_open_file_dialog_pg4.htm
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) //how to browse dirs instead of files?????
{
startpath.Text = ofd.FileName;
}
}
public void newcomponent(object sender, RoutedEventArgs e)
{
componentsettings new_cmpnt = new componentsettings();
new_cmpnt.Title = "Add Component";
new_cmpnt.ShowDialog();
}
public void editcomponent(object sender, RoutedEventArgs e)
{
int selected = components.SelectedIndex;
XmlElement current = (XmlElement)components.Items.CurrentItem;
componentsettings edit_cmpnt = new componentsettings(current);
edit_cmpnt.cmpnt_mgr.manage_components += new manager.mngcmpnts(test);
edit_cmpnt.Title = "Edit Component";
edit_cmpnt.ShowDialog();
}
private void browsedir(object sender, RoutedEventArgs e)
{
System.Windows.Forms.FolderBrowserDialog directory;
directory = new System.Windows.Forms.FolderBrowserDialog();
directory.Description = "Please select the folder containing your g2 configuration (pxml) files. Default is C:\\Program Files\\Exacom, Inc.";
directory.ShowDialog();
startpath.Text = directory.SelectedPath;
}
private void save(object sender, RoutedEventArgs e)
{
MessageBox.Show(dp.Source.ToString());
if(File.Exists(cfgfile + ".old"))
{
File.Delete(cfgfile + ".old");
}
File.Move(cfgfile, cfgfile + ".old");
dp.Document.Save(cfgfile);
}
private void remove(object sender, RoutedEventArgs e)
{
XmlNode component = dp.Document.DocumentElement["Components"].ChildNodes[components.SelectedIndex];
dp.Document.DocumentElement["Components"].RemoveChild(component);
}
private void temp(object sender, RoutedEventArgs e)
{
MessageBox.Show(dp.Document.DocumentElement["Components"].ChildNodes[components.SelectedIndex].InnerText.ToString()); //will this give me trouble????? Probably not since it's my config, but...
}
private void test(int id, XmlElement testelement)
{
//dp.Document.DocumentElement["Components"].ChildNodes[id] = testelement;
//XmlNode testnode = dp.Document.DocumentElement["Components"].ChildNodes[id + 1];
//XmlNode testnode = testelement;
MessageBox.Show(testelement.OuterXml.ToString());
//dp.Document.DocumentElement["Components"].InsertAfter(dp.Document.DocumentElement["Components"].ChildNodes[0], testelement);
//dp.Document.DocumentElement["Components"].RemoveChild(testelement);
//dp.Document.DocumentElement["Components"].ReplaceChild(testnode, dp.Document.DocumentElement["Components"].ChildNodes[id]);
dp.Document.DocumentElement["Components"].PrependChild(testelement);
//dp.Document.NodeChanged();
//dp.Document.DocumentElement["Components"].CloneNode(true);
dp.Document.DocumentElement["Components"].AppendChild(testelement);
//dp.Document.
//MessageBox.Show(dp.Document.OuterXml.ToString());
//MessageBox.Show(dp.Document.DocumentElement["Components"].FirstChild.AppendChild(testelement).OuterXml.ToString());
}
}
}
JMSA. Thank you for the suggestion. I was playing around with it today and I found a solution. It's a little messy, but it works. I copy one of the present nodes and then clear all of the values. Here is the code if anyone is interested.
private void add(object sender, RoutedEventArgs e)
{
System.Xml.XmlNode test = configfile.Document.DocumentElement["Components"].FirstChild;
System.Xml.XmlNode testclone = test.Clone();
for (int i = 0; i < testclone.ChildNodes.Count; i++)
{
testclone.ChildNodes[i].RemoveAll();
}
configfile.Document.DocumentElement["Components"].AppendChild(testclone);
components.SelectedItem = components.Items.Count + 1;
}
My suggestion would be to create a class for each element by using a composite pattern, lazy loading the XML doc in the class and perform operations as you need. Then again save it. Quite a handful though.
Related
why can't I add a new item/object to my list in button (or combobox etc.) events? I mean, the events don't see my list if it's outside of the brackets...it's underlined in red... can you help me?
long story short: i want to add a new object by clicking the button
using System;
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;
using System.Xml.Serialization;
using System.IO;
namespace Samochody
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
List<Samochod> ListaSamochodow = new List<Samochod>();
comboBox1.DataSource = ListaSamochodow;
comboBox1.DisplayMember = "Marka";
XmlRootAttribute oRootAttr = new XmlRootAttribute();
XmlSerializer oSerializer = new XmlSerializer(typeof(List<Samochod>), oRootAttr);
StreamWriter oStreamWriter = null;
oStreamWriter = new StreamWriter("samochody.xml");
oSerializer.Serialize(oStreamWriter, ListaSamochodow);
}
private void button1_Click(object sender, EventArgs e)
{
try
{
ListaSamochodow.Add(new Samochod(textBox1.Text, textBox2.Text, Convert.ToInt32(textBox3.Text)));
}
catch (Exception oException)
{
Console.WriteLine("Aplikacja wygenerowała następujący wyjątek: " + oException.Message);
}
}
I think you should instantiate your list globally and not under Form1_Load event.
that way it will be accessible all around your class (the window in this case).
This seems to work fine. You might need to set your textboxes to contain valid values when you start the form. also make sure that you make the list visible throughout your form1 class.
namespace Samochody
{
public partial class Form1 : Form
{
// make sure your list looks like this, created outside your functions.
List<Samochod> ListaSamochodow = new List<Samochod>();
public Form1()
{
InitializeComponent();
label1.Text = "the amount in your list is " + ListaSamochodow.Count.ToString();
textBox1.Text = "string here";
textBox2.Text = "string here";
textBox3.Text = "100";
}
private void Form1_Load(object sender, EventArgs e)
{
XmlRootAttribute oRootAttr = new XmlRootAttribute();
XmlSerializer oSerializer = new XmlSerializer(typeof(List<Samochod>), oRootAttr);
StreamWriter oStreamWriter = null;
oStreamWriter = new StreamWriter("samochody.xml");
oSerializer.Serialize(oStreamWriter, ListaSamochodow);
}
private void button1_Click_1(object sender, EventArgs e)
{
Samochod s = new Samochod(textBox1.Text, textBox2.Text, Convert.ToInt32(textBox3.Text));
ListaSamochodow.Add(s);
label1.Text = "the amount in your list is " + ListaSamochodow.Count.ToString();
}
}
}
Read them back in app constructor and also maybe in other places in program.
I have a new form i created with some checkboxes:
using System;
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;
using System.IO;
namespace Options
{
public partial class OptionsMenuForm : Form
{
public OptionsMenuForm()
{
InitializeComponent();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
Settings.downloadonstart = true;
}
else
{
Settings.downloadonstart = false;
}
}
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
if (checkBox2.Checked)
{
Settings.loadonstart = true;
}
else
{
Settings.loadonstart = false;
}
}
private void checkBox3_CheckedChanged(object sender, EventArgs e)
{
if (checkBox3.Checked)
{
Settings.startminimized = true;
}
else
{
Settings.startminimized = false;
}
}
private void checkBox4_CheckedChanged(object sender, EventArgs e)
{
if (checkBox4.Checked)
{
Settings.displaynotifications = true;
}
else
{
Settings.displaynotifications = false;
}
}
}
}
Now i want to save each time the state of one/any of the checkboxes.
I also added a class i'm using th pass the variables between the new form and form1:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Options
{
class Settings
{
public static bool downloadonstart;
public static bool loadonstart;
public static bool startminimized;
public static bool displaynotifications;
}
}
Now how can i use this in form1 by saving the settings to a text file ?
For example in the text file the content will be something like:
CheckBox1 = true
CheckBox2 = false
CheckBox3 = false
CheckBox4 = true
And then if i change the state of one of them it will write it in the text file:
CheckBox1 = false
CheckBox2 = false
CheckBox3 = true
CheckBox4 = true
In form1 top i added
string settingsFile = "settings.txt";
string settingsFileDirectory = "\\settings";
StreamWriter writetosettingsfile;
Then in constructor
settingsFileDirectory = Path.GetDirectoryName(Application.LocalUserAppDataPath) +
settingsFileDirectory;
if (!Directory.Exists(settingsFileDirectory))
{
Directory.CreateDirectory(settingsFileDirectory);
}
I know the app it self have a settings in the properties but i wanted to use a text file this time since i have many settings and i might have more later.
Or using the settings in the app in properties i did:
But now how do i use with it in my program to save every checkbox state in the new form and then using it in form1 ?
You can use json.net
Something like this to save the data
private void Form1_Load(object sender, EventArgs e)
{
checkBox1.CheckedChanged += checkBox_CheckedChanged;
checkBox2.CheckedChanged += checkBox_CheckedChanged;
checkBox3.CheckedChanged += checkBox_CheckedChanged;
checkBox4.CheckedChanged += checkBox_CheckedChanged;
}
private void checkBox_CheckedChanged(object sender, EventArgs e)
{
var settings = new Settings();
settings.downloadonstart = checkBox1.Checked;
settings.loadonstart = checkBox2.Checked;
settings.startminimized = checkBox3.Checked;
settings.displaynotifications = checkBox4.Checked;
File.WriteAllText(#"c:\configfile.json", JsonConvert.SerializeObject(settings));
}
You can read the file like this
Settings settings = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(#"c:\configfile.json"));
Documentation:
http://www.newtonsoft.com/json/help/html/Introduction.htm
public OptionsMenuForm()
{
InitializeComponent();
//Read the settings from a file with comma delimited 1's and 0's or whatever you like
string[] values = System.IO.File.ReadAllText("c:\temp\a.txt").Split(',');
checkBox1.Checked = Convert.ToBoolean(Convert.ToInt32(values[0]));
checkBox2.Checked = Convert.ToBoolean(Convert.ToInt32(values[1]));;
//On checkbox changes save the settings
checkBox1.CheckedChanged +=SaveSettings;
checkBox2.CheckedChanged +=SaveSettings;
}
public void SaveSettings(object sender,EventArgs e)
{
StringBuilder sbValues = new StringBuilder();
int i = 0;
i = checkBox1.Checked ? 1 : 0;
sbValues.Append(i.ToString() + ",");
i = checkBox2.Checked ? 1 : 0;
sbValues.Append(i.ToString() + ",");
System.IO.File.WriteAllText("c:\temp\a.txt",sbValues.ToString());
}
It is recommended that you use XML when using the .NET framework.
public static void ConvertStringToXmlList()
{
XElement xml = new XElement
(
"Items",
(
from x in [YourList] select new XElement("Item", x)
)
);
XDocument doc = new XDocument
(
xml
);
doc.Save(Environment.CurrentDirectory + "/settings.xml");
}
var xmlReader = new XmlTextReader("settings.xml");
while (xmlReader.Read())
{
switch (reader.NodeType)
{
case [nodetype]:
// Code here
break;
}
}
Also, instead of creating the same event for all four checkbox's, just use 1.
Inside of the single event put:
ckCheckBox1.Checked = !ckCheckBox1.Checked;
I have created a RichTextBox text editor as a c# Form application solution. I have added a WPF application to this solution to act as a spellchecker. Basically, when I run the spell checker the contents of the RichTextBox are copied to a WPF RichTextBox in the WPF application which I have configured to look like a spell check dialog. I have included a ListBox which displays the spelling error suggestions and buttons that allow the user to ignore errors or change if one of the suggestions are clicked.
The main routine loops through the text in the RichTextBox until it finds a spelling error. The user then has the option to ignore by clicking a button that calls EditingCommands.IgnoreSpellingError or change by clicking a button that calls EditingCommands.CorrectSpellingError. Currently I am using another button to then re-cycle through the RichTextBox to find the next spelling error.
Ideally what I would like to happen is, for example, when the ignore button is clicked, the EditingCommands.IgnoreSpellingError is called and then the main routine is run immediately after, like so:
private void button3_Click(object sender, RoutedEventArgs e)
{
button3.Command = EditingCommands.IgnoreSpellingError;
button3.CommandTarget = richTextBox1;
spellCheckWords();
}
However, this does not work as the RichTextBox seems to go out of synchronisation with the spell checker. It's as if the form or the RichTextBox are not refreshing properly. Just to re-iterate, if I use a separate button to re-run the main routine then it works ok.
The full code is below.
Any help would be appreciated.
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
richTextBox1.SpellCheck.IsEnabled = true;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
richTextBox1.Paste();
richTextBox1.ContextMenu = GetContextMenu();
spellCheckWords();
}
private static int chrPos = 0;
private void spellCheckWords()
{
SpellingError spellingError;
TextRange spellErrorRange;
TextPointer start_pointer, end_pointer;
richTextBox1.ContextMenu = GetContextMenu();
richTextBox1.SelectAll();
int txtLen = richTextBox1.Selection.Text.Length;
bool noSpellingErrors = true; ;
for (int i = chrPos; i < txtLen; i++)
{
start_pointer = richTextBox1.Document.ContentStart.GetNextInsertionPosition
(LogicalDirection.Forward).GetPositionAtOffset(i, LogicalDirection.Forward);
spellingError = richTextBox1.GetSpellingError(start_pointer);
if (spellingError != null)
{
spellErrorRange = richTextBox1.GetSpellingErrorRange(start_pointer);
int errRange = spellErrorRange.Text.Length;
textBox1.Text = spellErrorRange.Text;
noSpellingErrors = true;
string textRun = start_pointer.GetTextInRun(LogicalDirection.Forward);
string trimmedString = string.Empty;
end_pointer = richTextBox1.Document.ContentStart.GetNextInsertionPosition
(LogicalDirection.Forward).GetPositionAtOffset(i + errRange, LogicalDirection.Forward);
richTextBox1.Selection.Select(start_pointer, start_pointer);
richTextBox1.Focus();
Rect screenPos = richTextBox1.Selection.Start.GetCharacterRect(LogicalDirection.Forward);
double offset = screenPos.Top + richTextBox1.VerticalOffset;
richTextBox1.ScrollToVerticalOffset(offset - richTextBox1.ActualHeight / 2);
listBox1.Items.Clear();
foreach (string str in spellingError.Suggestions)
{
listBox1.Items.Add(str);
}
//chrPos = i + errRange + 1;
return;
}
}
if (noSpellingErrors == true)
{
textBox1.Text = "No spelling Errors";
listBox1.Items.Clear();
button2.IsEnabled = false;
button3.IsEnabled = false;
button4.IsEnabled = false;
button6.IsEnabled = false;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
richTextBox1.SelectAll();
richTextBox1.Copy();
richTextBox1.Document.Blocks.Clear();
richTextBox1.SpellCheck.IsEnabled = false;
this.Close();
}
private ContextMenu GetContextMenu()
{
return null;
}
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (listBox1.SelectedItem != null)
textBox1.Text = listBox1.SelectedItem.ToString();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
button2.Command = EditingCommands.CorrectSpellingError;
button2.CommandParameter = textBox1.Text;
button2.CommandTarget = richTextBox1;
spellCheckWords();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
button3.Command = EditingCommands.IgnoreSpellingError;
button3.CommandTarget = richTextBox1;
spellCheckWords();
}
private void button4_Click(object sender, RoutedEventArgs e)
{
spellCheckWords();
}
}
}
After a while i got your problem^^ (i think) and it is easy to fix.
In you Button-Click-events you set the command of the button and the parameters and then execute spellCheckWords. But the actual command is executed AFTER the code has leaved the method.
Which means, spellCheckWords() is executed BEFORE IgnoreSpellingError.
Change your code to this:
private void button2_Click(object sender, RoutedEventArgs e)
{
EditingCommands.CorrectSpellingError.Execute(textBox1.Text, richTextBox1);
spellCheckWords();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
EditingCommands.IgnoreSpellingError.Execute(null, richTextBox1);
spellCheckWords();
}
This will execute the command first and then the spellcheck again.
All of a sudden my form window has begun to close as soon as the application is launched. There's nothing in the output window that gives a hint as to what could be causing it and there are no errors thrown at me either. Does anybody have any ideas?
I've provided to form's class.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ProjectBoardManagement {
public partial class CreateBoard : Form {
Functions funcs = new Functions();
public CreateBoard() {
InitializeComponent();
}
private void CreateBoardButton_Click(object sender, EventArgs e) {
String BoardName = BoardNameText.Text;
String Pages = "";
String Labels = "";
foreach (ListViewItem i in PageNameList.Items) {
Pages = (Pages + i.Name.ToString() + ",");
}
foreach (ListViewItem i in LabelNameList.Items) {
Labels = (Labels + i.Name.ToString() + ",");
}
String BoardFile = ("board_" + BoardName + ".txt");
funcs.SaveSetting(BoardFile, "name", BoardName);
funcs.SaveSetting(BoardFile, "pages", Pages);
funcs.SaveSetting(BoardFile, "labels", Labels);
FormManagement.CreateBoard.Hide();
FormManagement.BoardList.LoadBoardList();
}
private void PageNameButtonAdd_Click(object sender, EventArgs e) {
String pagename = PageNameText.Text;
if (pagename != "") {
PageNameList.Items.Add(pagename);
}
PageNameText.Text = "";
}
private void LabelNameButtonAdd_Click(object sender, EventArgs e) {
String labelname = LabelNameText.Text;
if (labelname != "") {
LabelNameList.Items.Add(labelname);
}
LabelNameText.Text = "";
}
}
}
Obvious first thing to do - run it in Debug mode and stop execution on all exceptions. This should give you enough information on how to go from there.
Otherwise Functions funcs = new Functions(); looks suspicious.
I am trying to create a simple Silverlight application that calls an ATOM feed and displays the article title and submit date. I found this very easy to do with RSS feeds and LINQ but I am stuck trying to do the same with an ATOM feed. The code below produces no errors but it also produced no results! What am I missing?
Source ATOM feed: weblogs.asp.net/scottgu/atom.aspx
Source Tutorial: www.switchonthecode.com/tutorials/silverlight-datagrid-the-basics
Source code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;
namespace BasicDataGridTutorial
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void btnPopulate_Click(object sender, RoutedEventArgs e)
{
//disable the populate button so it's not clicked twice
//while the data is being requested
this.btnPopulate.IsEnabled = false;
//make a new WebClient object
WebClient client = new WebClient();
//hook the event that's called when the data is received
client.DownloadStringCompleted += client_DownloadStringCompleted;
//tell the WebClient to download the data asynchronously
client.DownloadStringAsync(
//new Uri("http://feeds.feedburner.com/SwitchOnTheCode?format=xml"));
new Uri("http://weblogs.asp.net/scottgu/atom.aspx"));
}
private void client_DownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
this.btnPopulate.IsEnabled = true;
if (e.Error == null)
{
XDocument document = XDocument.Parse(e.Result);
XNamespace xmlns = "http://www.w3.org/2005/Atom";
var sotcPosts = from entry in document.Descendants(xmlns+ "entry")
select new SOTCPost
{
Title = (string)entry.Element(xmlns + "feedEntryContent").Value,
Date = (string)entry.Element(xmlns + "lastUpdated").Value
};
this.sotcDataGrid.ItemsSource = sotcPosts;
}
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
this.sotcDataGrid.ItemsSource = null;
}
}
public class SOTCPost
{
public string Title { get; set; }
public string Date { get; set; }
}
}
I'd recommend using the SyndicationFeed instead of parsing the ATOM feed yourself. It'll do a better job of handling edge cases you may not have considered.
XmlReader reader = XmlReader.Create("http://localhost/feeds/serializedFeed.xml");
SyndicationFeed feed = SyndicationFeed.Load(reader);
var sotcPosts = from item in feed.Items
select new SOTCPost
{
Title = item.Title.Text,
Date = item.PublishDate
};
You have "feedEntryContent" and "lastUpdated" as element names, but I think you want "title" and "published".
The reason you get "no results" is that elements by the names you're selecting don't exist in the document.