MVVM Saving xDoc file exception - c#

I'm trying to learn c#/WPF/MVVM by converting a sample WPF app to MVVM. This app opens a xml file for editing and then saves it. The app preforms well until I try to save the file, I'm getting an InvalidCastException. Here is some code
mDataSource.cs
public static List<MediaItem> Load(string filename)
{
var mediafiles = XDocument.Load(filename).Root.Elements("style").Elements("item").Select(
x => new MediaItem(
(string)x.Element("title"),
(string)x.Element("artist"),
(string)x.Element("year")));
return mediafiles.ToList();
}
MainViewModel.cs - Load the xml file
public void LoadList(string filename)
{
this.mediafiles = new ObservableCollection<MediaItemViewModel>();
List<MediaItem> mediabaseList = mDataSource.Load(filename);
foreach (MediaItem mediaitem in mediabaseList)
{
this.mediafiles.Add(new MediaItemViewModel(mediaitem));
}
this.collectionView = CollectionViewSource.GetDefaultView(mediafiles);
if (this.collectionView == null)
throw new NullReferenceException("collectionView");
this.collectionView.CurrentChanged += new EventHandler(this.OnCollectionViewCurrentChanged);
}
Saving the file
private void Save(ICollectionView collectionView)
{
mDataSource mds = new mDataSource();
mds.Save(this.collectionView);
}
mDataSource - Saving the file, during debugging data shows up properly everywhere but the exception comes on the line - MediaItem mi = (MediaItem)mediaitem; {"Unable to cast object of type 'mList.ViewModels.MediaItemViewModel' to type 'mList.Models.MediaItem'."}
public void Save(ICollectionView collectionView)
{
XDocument xdoc = new XDocument();
XElement xeRoot = new XElement("art");
XElement xeSubRoot = new XElement("style");
foreach (var mediaitem in collectionView)
{
MediaItem mi = (MediaItem)mediaitem;
XElement xRow = new XElement("item");
xRow.Add(new XElement("title", mi.Title));
xRow.Add(new XElement("artist", mi.Artist));
xRow.Add(new XElement("year", mi.Year));
xeSubRoot.Add(xRow);
}
xeRoot.Add(xeSubRoot);
xdoc.Add(xeRoot);
xdoc.Save(filename);
}
Thank You

ICollectionView (which shouldn't be referenced in your VM) contains a bunch of MediaItemViewModels.
this.mediafiles.Add(new MediaItemViewModel(mediaitem));
So, you need to get the MediaItem that is wrapped by the given MediaItemViewModel. You didn't include that code, so I can't tell you where the original MI is stored.
foreach (var mediaitem in collectionView.OfType<MediaItemViewModel>())
{
MediaItem mi = mediaitem.ThisPropertyContainsTheWrappedMediaItem;

Related

Edit existing XML file tree

I have problem with an editting existing xml file. I was looking for a solution but I can not find solution which I need. Can someone help me please?
Here is my code:
private void referenceToXML(string path)
{
var filePath = path;
var xmlDoc = XDocument.Load(filePath);
var parentElement = new XElement("Items");
var firstNameElement = new XElement("Item");
firstNameElement.SetAttributeValue("name", question.text);
var lastNameElement = new XElement("Cathegory", SM.text);
parentElement.Add(firstNameElement);
firstNameElement.Add(lastNameElement);
var rootElement = xmlDoc.Element("ItemCollection");
rootElement.Add(parentElement);
xmlDoc.Save(path);
}
And here is result of the code:
https://pastebin.com/LKGJER38
But I need this:
https://pastebin.com/RRC75pR8
I will appreciate every help.
Add to the Items Element instead :
private void referenceToXML(string path)
{
var xmlDoc = XDocument.Load(path);
xmlDoc.Element("ItemCollection").Element("Items").Add(
new XElement("Item", new XAttribute("name", question.text), SM.text));
xmlDoc.Save(path);
}
Your problem is that you are unconditionally adding an <Items> element every time you add an <Item> element. Instead, you need to check whether such an element exists, and if so, use it. The following extension method makes that easy:
public static partial class XNodeExtensions
{
public static XElement GetOrAddElement(this XContainer container, XName name)
{
if (container == null || name == null)
throw new ArgumentNullException();
var element = container.Element(name);
if (element == null)
container.Add(element = new XElement(name));
return element;
}
}
Now you can modify your referenceToXML method to use it as follows:
private static void referenceToXML(string path, string questionText, string smText)
{
AddItem(XDocument.Load(path), questionText, smText).Save(path);
}
static XDocument AddItem(XDocument doc, string questionText, string smText)
{
var firstNameElement = new XElement("Item");
firstNameElement.SetAttributeValue("name", questionText);
var lastNameElement = new XElement("Cathegory", smText);
firstNameElement.Add(lastNameElement);
// Get or create the root element ItemCollection
var root = doc.GetOrAddElement("ItemCollection");
// Get or create the Items list
var items = root.GetOrAddElement("Items");
// Add the item
items.Add(firstNameElement);
return doc;
}
Demo fiddle here.
Note I modified your code to make question.text and SM.text arguments instead of class members for clarity and testing purposes.

How to Use NRefactory to Modify C# Code

I am trying to use NRefactory to modify an existing piece of C# code. I have tried to use the method described in this article. I'm getting an exception complaining about duplicate changes. Can someone point out what I am doing wrong, and maybe point me in the right direction?
I have the following section of code:
// contains the source code that needs to be modified
var text = "...";
// is the name of the file in which the target class exists
var fileName = "Foo";
// is the name of the target class
var className = "Foo.cs";
// FormattingOptions and TextEditorOptions are properties available to
// the class containing this code...
var parser = new CSharpParser();
var syntaxTree = parser.Parse(text, fileName);
syntaxTree.Freeze();
var document = new StringBuilderDocument();
document.Text = syntaxTree.ToString(FormattingOptions);
using (var documentScript = new DocumentScript(document, FormattingOptions, TextEditorOptions))
{
var typeDeclarations = syntaxTree.Descendants.OfType<TypeDeclaration>().Where(typeDeclaration => typeDeclaration.Name == className);
var templateProviderDeclaration = typeDeclarations.SingleOrDefault();
if (templateProviderDeclaration != null)
{
var constructorDeclarations = templateProviderDeclaration.Descendants.OfType<ConstructorDeclaration>();
foreach (var constructorDeclaration in constructorDeclarations)
{
var assignmentExpressions = constructorDeclaration.Descendants.OfType<AssignmentExpression>();
foreach (var assignmentExpression in assignmentExpressions)
{
var leftExpression = assignmentExpression.Left;
if (leftExpression != null && leftExpression.ToString(FormattingOptions) == "this.templates")
{
var rightExpression = assignmentExpression.Right;
foreach (var arrayInitializerExpression in rightExpression.Children.OfType<ArrayInitializerExpression>())
{
// uses AddTemplate(), which is an extension method
// which takes in an expression and modifies it, and
// returns an expression
var newExpression = ((ArrayInitializerExpression)arrayInitializerExpression).AddTemplate();
documentScript.Replace(arrayInitializerExpression, newExpression);
}
}
}
}
}
// text retrieved here is *clobbered*, by a few characters
text = documentScript.CurrentDocument.Text;
}
Other Code...
public static class ExtensionMethods
{
public static ArrayInitializerExpression AddTemplate(this ArrayInitializerExpression expression)
{
// TODO : will modify the AST for the target expression
return (ArrayInitializerExpression)expression.Clone();
}
}
Update:
I am trying to change a line like this:
this.templates = new Dictionary<string, ITemplate>
{
{ "Foo", new FooTemplate() }
};
to:
this.templates = new Dictionary<string, ITemplate>
{
{ "Foo", new FooTemplate() },
{ "Bar", new BarTemplate() }
};
For the purposes of this question, the exact specifics of the change I am trying to make aren't important though. I suspect that I have something not constructed quite correctly, but I cannot figure out if that's the issue.

Creating a blank document in a document library using client object model

I'm creating a function where you can provide a content type name and target either a list or document library and create a default item. Im using the client object model for office 2013
public void MyFunction()
{
//clientContext must be authenticated already on your sharepoint site
var listName = "Default Document Set";
var docSetContentTypeName = "Document";
var newDocSetName = string.Format("Item {0}", Guid.NewGuid());
Web web = clientContext.Web;
List list = clientContext.Web.Lists.GetByTitle(listName);
clientContext.Load(clientContext.Site);
ContentTypeCollection listContentTypes = list.ContentTypes;
clientContext.Load(listContentTypes, types => types.Include
(type => type.Id, type => type.Name,
type => type.Parent));
var result = clientContext.LoadQuery(listContentTypes.Where
(c => c.Name == docSetContentTypeName));
clientContext.ExecuteQuery();
ContentType targetDocumentSetContentType = result.FirstOrDefault();
ListItemCreationInformation newItemInfo = new ListItemCreationInformation();
newItemInfo.UnderlyingObjectType = FileSystemObjectType.Folder;
newItemInfo.LeafName = newDocSetName;
ListItem newListItem = list.AddItem(newItemInfo);
newListItem["ContentTypeId"] = targetDocumentSetContentType.Id.ToString();
newListItem["Title"] = newDocSetName;
newListItem.Update();
clientContext.Load(list);
clientContext.ExecuteQuery();
}
The function works fine on ContentTypes like Item and Document Set but when I use Document, it creates an item with a content type "Document" but it has an icon of folder and acts like a folder.
Is there something I need to add?
Thanks in advance.
FileSystemObjectType.Folder is used for creating a Folder object and therefore could not be specified for creating File object. At the same time List.AddItem Method could not be used for creating a File object.
You could consider the following example that demonstrates how to create(upload) a file into Documents library:
public static void UploadFile(List list,string filePath,IDictionary<string,object> itemProperties)
{
var ctx = list.Context;
var fileInfo = new FileCreationInformation();
fileInfo.Url = Path.GetFileName(filePath);
fileInfo.Overwrite = true;
fileInfo.Content = System.IO.File.ReadAllBytes(filePath);
var file = list.RootFolder.Files.Add(fileInfo);
var listItem = file.ListItemAllFields;
foreach (var p in itemProperties)
{
listItem[p.Key] = p.Value;
}
listItem.Update();
ctx.ExecuteQuery();
}
Using Open XML SDK 2.0 for Microsoft Office you could create an empty document and upload it into Documents library:
public static void CreateAndUploadFile(List list, string filePath, IDictionary<string, object> itemProperties)
{
using (var document = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
{
var mainPart = document.AddMainDocumentPart();
mainPart.Document = new Document(new Body());
}
UploadFile(list, filePath, itemProperties);
}
Usage:
var list = web.Lists.GetByTitle(listTitle);
var itemProperties = new Dictionary<string,object>();
itemProperties["Title"] = "SharePoint User Guide";
CreateAndUploadFile(list, "./SharePoint User Guide.docx", itemProperties);

ListView adding items with subitems

I'm trying to read a file line by line, which works perfectly but I want to seperate the results I get into subitems in the listview.
I am also searching for all .jar files in the folder so I can use those as the name (first column). The second column needs to have the "version", the third column the "author" and the fourth column the "description".
Here's one of the text files I receive from within the jar files:
name: AFK
main: com.github.alesvojta.AFK.AFK
version: 2.0.5
author: Ales Vojta / schneckk
description: Provides AFK messages
website: http://dev.bukkit.org/server-mods/afk/
commands:
afk:
description: Provides AFK message when player types /afk.
usage: /<command>
this is the code I have right now:
private List<string> GetInstalledPlugins()
{
List<string> list = new List<string>();
lvInstalledPlugins.Items.Clear();
if (!Directory.Exists(Environment.CurrentDirectory + "\\plugins"))
{
Directory.CreateDirectory(Environment.CurrentDirectory + "\\plugins");
DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + "\\plugins");
FileInfo[] fileInfo = di.GetFiles("*.jar");
foreach (var info in fileInfo)
{
//lvInstalledPlugins.Items.Add(info.Name);
list.Add(info.Name);
}
}
else
{
DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + "\\plugins");
FileInfo[] fileInfo = di.GetFiles("*.jar");
foreach (var info in fileInfo)
{
//lvInstalledPlugins.Items.Add(info.Name);
list.Add(info.Name);
}
}
return list;
}
private void test(IEnumerable<string> list)
{
List<ListViewItem> PluginList = new List<ListViewItem>();
var items = new string[4];
try
{
foreach (var ListItem in list)
{
Console.WriteLine(ListItem);
var name = Environment.CurrentDirectory + "\\plugins\\" + ListItem;
var zip = new ZipInputStream(File.OpenRead(name));
var filestream = new FileStream(name, FileMode.Open, FileAccess.Read);
var zipfile = new ZipFile(filestream);
ZipEntry item;
while ((item = zip.GetNextEntry()) != null)
{
if (item.Name == "plugin.yml")
{
using (var s = new StreamReader(zipfile.GetInputStream(item)))
{
string line;
while ((line = s.ReadLine()) != null)
{
if (line.Contains("name"))
{
items[0] = line;
}
if (line.Contains("version"))
{
items[1] = line;
}
if (line.Contains("author"))
{
items[2] = line;
}
if (line.Contains("description"))
{
items[3] = line;
}
try
{
var lvitem = new ListViewItem(items);
lvitem.Name = items[0];
lvitem.Text = items[0];
lvitem.SubItems.Add(items[1]);
lvitem.SubItems.Add(items[2]);
lvitem.SubItems.Add(items[3]);
PluginList.Add(lvitem);
}
catch (Exception)
{
}
}
lvInstalledPlugins.Items.AddRange(PluginList.ToArray());
}
}
}
}
This doesn't seem to work :/, any ideas? I've been working on this for the whole day and can't seem to get it to work :(.
Not exactly sure of your question, but going by the title, the answer to the question below may provide some assistance.
C# listView, how do I add items to columns 2, 3 and 4 etc?

Serializing a list of objects to XDocument

I'm trying to use the following code to serialize a list of objects into XDocument, but I'm getting an error stating that "Non white space characters cannot be added to content
"
public XDocument GetEngagement(MyApplication application)
{
ProxyClient client = new ProxyClient();
List<Engagement> engs;
List<Engagement> allEngs = new List<Engagement>();
foreach (Applicant app in application.Applicants)
{
engs = new List<Engagement>();
engs = client.GetEngagements("myConnString", app.SSN.ToString());
allEngs.AddRange(engs);
}
DataContractSerializer ser = new DataContractSerializer(allEngs.GetType());
StringBuilder sb = new StringBuilder();
System.Xml.XmlWriterSettings xws = new System.Xml.XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
using (System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(sb, xws))
{
ser.WriteObject(xw, allEngs);
}
return new XDocument(sb.ToString());
}
What am I doing wrong? Is it the XDocument constructor that doesn't take a list of objects? how do solve this?
I would think that last line should be
return XDocument.Parse(sb.ToString());
And it might be an idea to cut out the serializer altogether, it should be easy to directly create an XDoc from the List<> . That gives you full control over the outcome.
Roughly:
var xDoc = new XDocument( new XElement("Engagements",
from eng in allEngs
select new XElement ("Engagement",
new XAttribute("Name", eng.Name),
new XElement("When", eng.When) )
));
The ctor of XDocument expects other objects like XElement and XAttribute. Have a look at the documentation. What you are looking for is XDocument.Parse(...).
The following should work too (not tested):
XDocument doc = new XDocument();
XmlWriter writer = doc.CreateNavigator().AppendChild();
Now you can write directly into the document without using a StringBuilder. Should be much faster.
I have done the job this way.
private void button2_Click(object sender, EventArgs e)
{
List<BrokerInfo> listOfBroker = new List<BrokerInfo>()
{
new BrokerInfo { Section = "TestSec1", Lineitem ="TestLi1" },
new BrokerInfo { Section = "TestSec2", Lineitem = "TestLi2" },
new BrokerInfo { Section = "TestSec3", Lineitem ="TestLi3" }
};
var xDoc = new XDocument(new XElement("Engagements",
new XElement("BrokerData",
from broker in listOfBroker
select new XElement("BrokerInfo",
new XElement("Section", broker.Section),
new XElement("When", broker.Lineitem))
)));
xDoc.Save("D:\\BrokerInfo.xml");
}
public class BrokerInfo
{
public string Section { get; set; }
public string Lineitem { get; set; }
}

Categories

Resources