Reading XML ElementExtensions in C# Win8.1 App - c#

I'm new to both C# and Windows App development, my comes experience mostly in python but I'm trying to challenge myself to learn something new.
I've been practicing/learning c# by making windows store apps, and after doing some WinJS/HTML5 tutorials I started on this tutorial: http://msdn.microsoft.com/en-us/library/windows/apps/br211380.aspx
I've completed the tutorial (including modifying the instructions for 8.1 apps since the tutorial is behind. I'm now trying to adapt my application to handle other types of data.
I'm attempting to pull all of the media:image tags from each item in a post so that I can display them. This is the structure of my XML data from an RSS feed.
<item>
<title>Post with photos</title>
<link>http://website.com/2014/07/23/post-1//</link>
<comments>http://website.com/2014/07/23/post-1/#comments</comments>
<pubDate>Wed, 23 Jul 2014 15:45:02 +0000</pubDate>
<dc:creator><![CDATA[Author]]></dc:creator>
<category><![CDATA[Post]]></category>
<guid isPermaLink="false">http://www.website.com/p?1</guid>
<description><![CDATA[description here]]></description>
<wfw:commentRss>http://www.website.com/post/1/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<enclosure type="image/jpeg" length="1" url="http://0.gravatar.com/avatar/" />
<media:thumbnail url="http://www.website.com/thumbnail_urle1406130272443.jpg?w=500" />
<media:content url="http://0.gravatar.com/avatar/" medium="image">
<media:category>author</media:category>
</media:content>
<media:content url="http://www.website.com/post/1/233-e1406130272443.jpg" medium="image">
<media:title>image-23</media:title>
</media:content>
<media:content url="http://www.website.com/post/1/163.jpg" medium="image">
<media:title>image-16</media:title>
</media:content>
<media:content url="http://www.website.com/post/1/73.jpg" medium="image">
<media:title>bimage-7</media:title>
</media:content>
</item>
I implemented this in HTML5/JS and when I did I used this method:
var thumb = post.querySelector(
"thumbnail").attributes.getNamedItem("url").textContent;
var postImages = post.querySelectorAll("content");
var ImageList = [];
for (var imgIndex = 1; imgIndex < postImages.length; imgIndex++) {
var imgHTML = "<img src='" + postImages[imgIndex].attributes.getNamedItem("url").textContent + "'</img><br/>";
var ImageList = ImageList += imgHTML;
}
But naturally.. that won't do in C#. I have looked at these thread in SO: Get media elements from RSS using SyndicationFeed as well as the one linked inside of it and they don't work for me. When I try to use
var elements = rss.Feed.Items.SelectMany(s => s.ElementExtensions.Select(x => x.GetObject().Value));
I do not have a GetObject method available when I put it all together using my code. Here is my current Data model.
private async Task<FeedData> GetFeedAsync(string feedUriString)
{
Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
Uri feedUri = new Uri(feedUriString);
try
{
SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri);
// This code is executed after RetrieveFeedAsync returns the SyndicationFeed.
// Process the feed and copy the data you want into the FeedData and FeedItem classes.
FeedData feedData = new FeedData();
if (feed.Title != null && feed.Title.Text != null)
{
feedData.Title = feed.Title.Text;
}
if (feed.Subtitle != null && feed.Subtitle.Text != null)
{
feedData.Description = feed.Subtitle.Text;
}
if (feed.Items != null && feed.Items.Count > 0)
{
// Use the date of the latest post as the last updated date.
feedData.PubDate = feed.Items[0].PublishedDate.DateTime;
foreach (SyndicationItem item in feed.Items)
{
FeedItem feedItem = new FeedItem();
if (item.Title != null && item.Title.Text != null)
{
feedItem.Title = item.Title.Text;
}
if (item.PublishedDate != null)
{
feedItem.PubDate = item.PublishedDate.DateTime;
}
if (item.Authors != null && item.Authors.Count > 0)
{
feedItem.Author = item.Authors[0].Name.ToString();
}
// Handles RSS / Atom Feed differences..
if (feed.SourceFormat == SyndicationFormat.Atom10)
{
if (item.Content != null && item.Content.Text != null)
{
feedItem.Content = item.Content.Text;
}
if (item.Id != null)
{
feedItem.Link = new Uri(item.Id);
}
}
else if (feed.SourceFormat == SyndicationFormat.Rss20)
{
if (item.Summary != null && item.Summary.Text != null)
{
feedItem.Content = item.Summary.Text;
}
if (item.Links != null && item.Links.Count > 0)
{
feedItem.Link = item.Links[0].Uri;
}
}
feedData.Items.Add(feedItem);
}
}
return feedData;
}
catch (Exception)
{
return null;
}
}
I've tried setting breakpoints and looking at the item data in my RSS2.0 syndication handler, and I can see var media = item.ElementExtensions; has an accurate acount of all the element extensions in my post, and the title of them. (NodeName "content", NodeValue is the "title" from the element. but no URI for the url tag..)
I am looking for some way to integrate some way of getting these media:content, media thumbnail, etc, into a list so that I can assemble them and use them for HTML content later in my app.
Any advice would be appreciated!
edit:
More of my code..
public class FeedData
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime PubDate { get; set; }
public Uri Image { get; set; }
private List<FeedItem> _Items = new List<FeedItem>();
public List<FeedItem> Items
{
get
{
return this._Items;
}
}
}
// Holds info for a single blog post
public class FeedItem
{
public string Title { get; set; }
public string Author { get; set; }
public string Content { get; set; }
public DateTime PubDate { get; set; }
public Uri Link { get; set; }
}
// Holds a collection of blog feeds (FeedData), and contains methods needed to retrieve
// the feeds
public class FeedDataSource
{
private ObservableCollection<FeedData> _Feeds = new ObservableCollection<FeedData>();
public ObservableCollection<FeedData> Feeds
{
get
{
return this._Feeds;
}
}
public async Task GetFeedsAsync()
{
Task<FeedData> feed1 =
GetFeedAsync("http://url.com/feed");
Task<FeedData> feed2 =
GetFeedAsync("http://url.com/feed");
...
...
this.Feeds.Add(await feed1);
...
...
...
this.Feeds.Add(await feed15);
}

I'm not familiar with phone apps but you'd be putting this data into a repeater of some sort, data grid, etc.,
yes? Not being familiar with the Task generic object I'll still hazard a guess that you could use the C# key-value pair class with the List, then in this object in a Repeater, Grid, directly. I believe any object that implements IEnumerable (which List should) can be iterated over with a KeyValuePair
Let me know a bit more of what you want to do. It seems to me a
foreach KeyValuePair KVP in ListObject
{
//Access KVP.key and values with your code here ...
}
A more clear example is using a KVP to iterate over a dictionary object:
foreach (KeyValuePair<string, string> entry in dictTestEasement)
{
builder.RowFormat.Height = 0.2; //force row height by text font by setting height small
builder.InsertCell();
builder.Font.Bold = true;
builder.InsertHtml(entry.Key);
builder.Font.Bold = false;
builder.InsertCell();
builder.InsertHtml(entry.Value);
}
The builder in this case is a global object for document construction (Aspose Words Object) -- it's constructing a Word document. Don't be thrown by it.
seems straightforward ... I've used such solutions before and believe that's what you're fishing for.

SyndicationItem has a GetXMLDocument method that takes a SourceFormat (like Rss20) and then generates an XML string that you can parse using GEtElementsByTagName (or ID). Once you have that you can iterate through duplicate tags in a loop and use Attributes.GetNamedItem() to grab named attributes of an XML tag. It seems that 'media' is lopped off when exploring the xml for media:content tags in Rss20 format, so all thats necessary is content to search for.
var allImages = item.GetXmlDocument(feed.SourceFormat).GetElementsByTagName("content");
foreach (var s in allImages)
{
Debug.WriteLine(s.Attributes.GetNamedItem("url").InnerText;)
}

Related

Twincat Ads Reactive weird Handle behaviour

I'm working with TwincatAds.Reactive 6.0.190 in .NET 6 WPF Desktop application.
I'm also using MVVM pattern.
My goal is to create a Class that is going to observe for a PLC Variable changes, collect those variables to a dictionary, and later on use those values in the ViewModel.
Here's the method where I'm attaching the notification and action where I'm handling the notification.
public void AttachNotification(IEnumerable<(string key, Type type)> Symbols)
{
_observerValueNotification = Observer.Create<ValueNotification>(val =>
{
// Does handle really start from 2?
var handle = val.Handle;
if (val.UserData is object[] objects)
{
string tag = objects[handle - 2].ToString();
if (!_values.Any(x => x.Key == tag))
_values.Add(new SymbolModel { Key = tag, Value = val.Value });
else
{
var symbol = _values.First(x => x.Key == tag);
symbol.Value = val.Value;
}
}
ValuesChanged?.Invoke(_values);
});
if (_plcWrapper.AdsClient != null)
{
// Get Symbols from SymbolLoader
List<AnySymbolSpecifier> list = new();
List<string> userData = new();
foreach (var (key, type) in Symbols)
{
list.Add(new AnySymbolSpecifier(key, new AnyTypeSpecifier(type)));
userData.Add(key);
}
_subscription2 = _plcWrapper.AdsClient.WhenNotificationEx(list, NotificationSettings.ImmediatelyOnChange, userData.ToArray())
.Subscribe(_observerValueNotification);
}
}
I'm using ValueNotification simply because, I'd like to use this pattern also for complex PLC Variables like Structs.
As You can see, in the WhenNotificationEx method I'm using UserData[] to provide some sort of identification of what Variable has changed when handling the change.
My idea was to use Handle property from ValueNotification as an indexer in UserData[] to identify what variable I'm dealing with, but for some reason Handle starts from 2.
My question is, is it expected behaviour, does the Handle value really always start from 2?
I've decided that relying on the Handle being index in the UserData array is quite unpredictable as Handle is being created by the Twincat Ads server.
Solved the issue by creating own extension method to the WhenNotificationEx. Turned out IDisposableHandleBag has exactly what I was looking for, which is SourceResultHandles property, where AnySymbolSpecifier and ResultHandle are both stored!
Here's created extension method
public static Dictionary<string, uint> Handles { get; private set; } = new();
public static IObservable<ValueNotification> WhenNotificationWithHandle(this IAdsConnection connection, IList<AnySymbolSpecifier> symbols, NotificationSettings settings)
{
IAdsConnection connection2 = connection;
IList<AnySymbolSpecifier> symbols2 = symbols;
NotificationSettings settings2 = settings;
if (connection2 == null)
{
throw new ArgumentNullException("connection");
}
if (symbols2 == null)
{
throw new ArgumentNullException("symbols");
}
if (symbols2.Count == 0)
{
throw new ArgumentOutOfRangeException("symbols", "Symbol list is empty!");
}
IDisposableHandleBag<AnySymbolSpecifier> bag = null;
EventLoopScheduler scheduler = new EventLoopScheduler();
IObservable<int> whenSymbolChangeObserver = connection2.WhenSymbolVersionChanges(scheduler);
IDisposable whenSymbolChanges = null;
Action<EventHandler<AdsNotificationExEventArgs>> addHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
connection2.AdsNotificationEx += h;
bag = ((IAdsHandleCacheProvider)connection2).CreateNotificationExHandleBag(symbols2, relaxSubErrors: false, settings2, null);
bag.CreateHandles();
// Collect Handles
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
whenSymbolChanges = whenSymbolChangeObserver.Subscribe((Action<int>)delegate
{
bag.CreateHandles();
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
}, (Action<Exception>)delegate
{
TcTraceSource traceAds = AdsModule.TraceAds;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(101, 1);
defaultInterpolatedStringHandler.AppendLiteral("The AdsServer '");
defaultInterpolatedStringHandler.AppendFormatted(connection2.Address);
defaultInterpolatedStringHandler.AppendLiteral("' doesn't support SymbolVersionChanged Notifications! Handle recreation is not active!");
traceAds.TraceInformation(defaultInterpolatedStringHandler.ToStringAndClear());
});
};
Action<EventHandler<AdsNotificationExEventArgs>> removeHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
if (whenSymbolChanges != null)
{
whenSymbolChanges.Dispose();
}
scheduler.Dispose();
if (bag != null)
{
bag.Dispose();
bag = null;
Handles.Clear();
}
connection2.AdsNotificationEx -= h;
};
return from ev in Observable.FromEventPattern<EventHandler<AdsNotificationExEventArgs>, AdsNotificationExEventArgs>(addHandler, removeHandler)
where bag.Contains(ev.EventArgs.Handle)
select new ValueNotification(ev.EventArgs, ev.EventArgs.Value);
}

Determine if a Database is "Equal" to a DacPackage

Is there a way to use the SQL Server 2012 Microsoft.SqlServer.Dac Namespace to determine if a database has an identical schema to that described by a DacPackage object? I've looked at the API docs for DacPackage as well as DacServices, but not having any luck; am I missing something?
Yes there is, I have been using the following technique since 2012 without issue.
Calculate a fingerprint of the dacpac.
Store that fingerprint in the target database.
The .dacpac is just a zip file containing goodies like metadata, and
model information.
Here's a screen-grab of what you will find in the .dacpac:
The file model.xml has XML structured like the following
<DataSchemaModel>
<Header>
... developer specific stuff is in here
</Header>
<Model>
.. database model definition is in here
</Model>
</<DataSchemaModel>
What we need to do is extract the contents from <Model>...</Model>
and treat this as the fingerprint of the schema.
"But wait!" you say. "Origin.xml has the following nodes:"
<Checksums>
<Checksum Uri="/model.xml">EB1B87793DB57B3BB5D4D9826D5566B42FA956EDF711BB96F713D06BA3D309DE</Checksum>
</Checksums>
In my experience, this <Checksum> node changes regardless of a schema change in the model.
So let's get to it.
Calculate the fingerprint of the dacpac.
using System.IO;
using System.IO.Packaging;
using System.Security.Cryptography;
static string DacPacFingerprint(byte[] dacPacBytes)
{
using (var ms = new MemoryStream(dacPacBytes))
using (var package = ZipPackage.Open(ms))
{
var modelFile = package.GetPart(new Uri("/model.xml", UriKind.Relative));
using (var streamReader = new System.IO.StreamReader(modelFile.GetStream()))
{
var xmlDoc = new XmlDocument() { InnerXml = streamReader.ReadToEnd() };
foreach (XmlNode childNode in xmlDoc.DocumentElement.ChildNodes)
{
if (childNode.Name == "Header")
{
// skip the Header node as described
xmlDoc.DocumentElement.RemoveChild(childNode);
break;
}
}
using (var crypto = new SHA512CryptoServiceProvider())
{
byte[] retVal = crypto.ComputeHash(Encoding.UTF8.GetBytes(xmlDoc.InnerXml));
return BitConverter.ToString(retVal).Replace("-", "");// hex string
}
}
}
}
With this fingerprint now available, pseudo code for applying a dacpac can be:
void main()
{
var dacpacBytes = File.ReadAllBytes("<path-to-dacpac>");
var dacpacFingerPrint = DacPacFingerprint(dacpacBytes);// see above
var databaseFingerPrint = Database.GetFingerprint();//however you choose to do this
if(databaseFingerPrint != dacpacFingerPrint)
{
DeployDacpac(...);//however you choose to do this
Database.SetFingerprint(dacpacFingerPrint);//however you choose to do this
}
}
Here's what I've come up with, but I'm not really crazy about it. If anyone can point out any bugs, edge cases, or better approaches, I'd be much obliged.
...
DacServices dacSvc = new DacServices(connectionString);
string deployScript = dacSvc.GenerateDeployScript(myDacpac, #"aDb", deployOptions);
if (DatabaseEqualsDacPackage(deployScript))
{
Console.WriteLine("The database and the DacPackage are equal");
}
...
bool DatabaseEqualsDacPackage(string deployScript)
{
string equalStr = string.Format("GO{0}USE [$(DatabaseName)];{0}{0}{0}GO{0}PRINT N'Update complete.'{0}GO", Environment.NewLine);
return deployScript.Contains(equalStr);
}
...
What I really don't like about this approach is that it's entirely dependent upon the format of the generated deployment script, and therefore extremely brittle. Questions, comments and suggestions very welcome.
#Aaron Hudon answer does not account for post script changes. Sometimes you just add a new entry to a type table without changing the model. In our case we want this to count as new dacpac. Here is my modification of his code to account for that
private static string DacPacFingerprint(string path)
{
using (var stream = File.OpenRead(path))
using (var package = Package.Open(stream))
{
var extractors = new IDacPacDataExtractor [] {new ModelExtractor(), new PostScriptExtractor()};
string content = string.Join("_", extractors.Select(e =>
{
var modelFile = package.GetPart(new Uri($"/{e.Filename}", UriKind.Relative));
using (var streamReader = new StreamReader(modelFile.GetStream()))
{
return e.ExtractData(streamReader);
}
}));
using (var crypto = new MD5CryptoServiceProvider())
{
byte[] retVal = crypto.ComputeHash(Encoding.UTF8.GetBytes(content));
return BitConverter.ToString(retVal).Replace("-", "");// hex string
}
}
}
private class ModelExtractor : IDacPacDataExtractor
{
public string Filename { get; } = "model.xml";
public string ExtractData(StreamReader streamReader)
{
var xmlDoc = new XmlDocument() { InnerXml = streamReader.ReadToEnd() };
foreach (XmlNode childNode in xmlDoc.DocumentElement.ChildNodes)
{
if (childNode.Name == "Header")
{
// skip the Header node as described
xmlDoc.DocumentElement.RemoveChild(childNode);
break;
}
}
return xmlDoc.InnerXml;
}
}
private class PostScriptExtractor : IDacPacDataExtractor
{
public string Filename { get; } = "postdeploy.sql";
public string ExtractData(StreamReader stream)
{
return stream.ReadToEnd();
}
}
private interface IDacPacDataExtractor
{
string Filename { get; }
string ExtractData(StreamReader stream);
}

Sitecore Dynamic Placeholders Allowed Renderings

I'm implementing Dynamic Placeholders in Sitecore 7 as described in the articles
http://trueclarity.wordpress.com/2012/06/19/dynamic-placeholder-keys-in-sitecore/
http://www.techphoria414.com/Blog/2011/August/Dynamic_Placeholder_Keys_Prototype
It is working correctly such that I can add the same Rendering to the layout and the renderings will go in the appropriate Dynamic Placeholder. However when I click to add a Rendering to the Dynamic Placeholder, the placeholder settings aren't being used.
What I am expecting is to be prompted with the allowed renderings that may be placed on the Dynamic Placeholder. Instead the Rendering/Layout tree is presented to manually select the rendering - giving Content Editors the ability to add disallowed renderings to the placeholder.
I have debugged the code and the correct Placeholder Settings Item is being found for the Dynamic Placeholder and the list of allowed Renderings are being retrieved however despite being set in the args the list is not presented for the User. See code below.
public class GetDynamicKeyAllowedRenderings : GetAllowedRenderings
{
//string that ends in a GUID
public const string DynamicKeyRegex = #"(.+){[\d\w]{8}\-([\d\w]{4}\-){3}[\d\w]{12}}";
public new void Process(GetPlaceholderRenderingsArgs args)
{
Assert.IsNotNull(args, "args");
// get the placeholder key
string placeholderKey = args.PlaceholderKey;
var regex = new Regex(DynamicKeyRegex);
Match match = regex.Match(placeholderKey);
// if the placeholder key text followed by a Guid
if (match.Success && match.Groups.Count > 0)
{
// Is a dynamic placeholder
placeholderKey = match.Groups[1].Value;
}
else
{
return;
}
Item placeholderItem = null;
if (ID.IsNullOrEmpty(args.DeviceId))
{
placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
args.LayoutDefinition);
}
else
{
using (new DeviceSwitcher(args.DeviceId, args.ContentDatabase))
{
placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
args.LayoutDefinition);
}
}
// Retrieve the allowed renderings for the Placeholder
List<Item> collection = null;
if (placeholderItem != null)
{
bool allowedControlsSpecified;
args.HasPlaceholderSettings = true;
collection = this.GetRenderings(placeholderItem, out allowedControlsSpecified);
if (allowedControlsSpecified)
{
args.CustomData["allowedControlsSpecified"] = true;
}
}
if (collection != null)
{
if (args.PlaceholderRenderings == null)
{
args.PlaceholderRenderings = new List<Item>();
}
args.PlaceholderRenderings.AddRange(collection);
}
}
}
As this code was developed for Sitecore 6.5 / 6.6 I wonder if in the jump to Sitecore 7.0 brought a change that affects the latter half of the code
I have found the source of the issue by decompiling the Sitecore 7 Kernel and viewing the default GetAllowedRenderings class. If Allowed Renderings are found the ShowTree Option needs to be set to false. See below
public class GetDynamicKeyAllowedRenderings : GetAllowedRenderings
{
//string that ends in a GUID
public const string DynamicKeyRegex = #"(.+){[\d\w]{8}\-([\d\w]{4}\-){3}[\d\w]{12}}";
public new void Process(GetPlaceholderRenderingsArgs args)
{
Assert.IsNotNull(args, "args");
// get the placeholder key
string placeholderKey = args.PlaceholderKey;
var regex = new Regex(DynamicKeyRegex);
Match match = regex.Match(placeholderKey);
// if the placeholder key text followed by a Guid
if (match.Success && match.Groups.Count > 0)
{
// Is a dynamic placeholder
placeholderKey = match.Groups[1].Value;
}
else
{
return;
}
// Same as Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings from here but with fake placeholderKey
// i.e. the placeholder without the Guid
Item placeholderItem = null;
if (ID.IsNullOrEmpty(args.DeviceId))
{
placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
args.LayoutDefinition);
}
else
{
using (new DeviceSwitcher(args.DeviceId, args.ContentDatabase))
{
placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
args.LayoutDefinition);
}
}
List<Item> collection = null;
if (placeholderItem != null)
{
bool allowedControlsSpecified;
args.HasPlaceholderSettings = true;
collection = this.GetRenderings(placeholderItem, out allowedControlsSpecified);
if (allowedControlsSpecified)
{
// Hide the Layout/Rendering tree to show the Allowed Renderings
args.Options.ShowTree = false;
}
}
if (collection != null)
{
if (args.PlaceholderRenderings == null)
{
args.PlaceholderRenderings = new List<Item>();
}
args.PlaceholderRenderings.AddRange(collection);
}
}
}
This is a change brought in by Sitecore 7 it seems.

XML Schemas -- List allowed attributes/tags at position in XML

Is there a way to query an XmlSchema or XmlSchemaSet for a list of available tags/attributes at a certain point in the XML? So say my cursor is between <b> and </b> and my schema only allows for a <c/> element there, can I figure that out using anything built in to C#?
<tagset>
<a></a>
<b><!-- CURSOR IS HERE --></b>
</tagset>
There is a way, but the Xml Schema specification is complex so it will take some effort and a few hundred lines of code.
The GetExpectedParticles method of the .NET XmlSchemaValidator class is the key part to a solution. This uses the XmlSchemaSet, passed as an argument, to return a set of XmlSchemaObject instances.
Before you can call this method you need to build a node path to your cursor location which must include ancestor elements and their preceding siblings and also the preceding siblings at the current nesting level. This node path is used to set the context for the schema validator.
After GetExpectedParticles has been called you need to process the particles. For instance, check if each the expected particle is a member of a substitution group, and check whether the expected particle is a restricted simple type that's an enumeration.
It's probably best to separate out code that fetches expected elements and attributes respectively.
The following incomplete code snippet includes the GetExpectedParticles method call, this only caters for element tag content, not attributes:
public static List<XmlSchemaObject> XsdExpectedElements(XmlSchemaSet schemaSet,
List<NodeDescriptor> nodePath)
{
List<XmlSchemaObject> elementNames = new List<XmlSchemaObject>();
NameTable nt = new NameTable();
XmlNamespaceManager manager = new XmlNamespaceManager(nt);
XmlSchemaValidator validator = new XmlSchemaValidator(nt, schemaSet, manager, XmlSchemaValidationFlags.None);
// event handler sets validationErrorFound local field
validator.ValidationEventHandler += new ValidationEventHandler(validator_ValidationEventHandler);
validator.Initialize();
XmlSchemaInfo xsInfo = new XmlSchemaInfo();
int i = 0;
foreach (nodeDescriptor nameUri in nodePath)
{
validator.ValidateElement(nameUri.LocalName, nameUri.NamespaceUri, xsInfo);
if ((i >= siblingPosition && siblingPosition > -1) || nameUri.Closed)
{
validator.SkipToEndElement(null);
}
else
{
validator.ValidateEndOfAttributes(null);
}
i++;
}
XmlSchemaParticle[] parts = validator.GetExpectedParticles();
if (parts.Length == 0)
{
bool hasElements = true;
bool elementClosed = nodePath[nodePath.Count - 1].Closed;
if (elementClosed) // we're outside the element tags
{
hasElements = true;
}
else if (xsInfo.SchemaType is XmlSchemaSimpleType)
{
hasElements = false;
}
else
{
XmlSchemaComplexType xsCt = xsInfo.SchemaType as XmlSchemaComplexType;
XmlSchemaContentType xsContent = (XmlSchemaContentType)xsCt.ContentType;
if (xsContent == XmlSchemaContentType.TextOnly)
{
hasElements = false;
}
}
if (!hasElements)
{
expectedType = XmlEditor.expectedListType.elementValue;
if (xsInfo.SchemaElement != null)
{
elementNames.Add(xsInfo.SchemaElement);
}
}
return elementNames;
}
foreach (XmlSchemaObject xso in parts)
{
if (xso is XmlSchemaElement)
{
XmlSchemaElement xse = (XmlSchemaElement)xso;
if (subGroupList.ContainsKey(xse.QualifiedName))
{
List<XmlSchemaElement> xses = subGroupList[xse.QualifiedName];
foreach (XmlSchemaElement xseInstance in xses)
{
elementNames.Add(xseInstance);
}
}
else
{
elementNames.Add(xse);
}
}
else if (xso is XmlSchemaAny)
{
XmlSchemaAny xsa = (XmlSchemaAny)xso;
foreach (XmlSchema xs in schemaSet.Schemas())
{
if (xs.TargetNamespace == xsa.Namespace)
{
foreach (XmlSchemaElement xseAny in xs.Elements)
{
elementNames.Add(xseAny);
}
}
}
}
}
}
The following (incomplete) code snippet shows how to get expected enumerated values from a particle:
private List<string> ExpectedEnumValues(XmlSchemaObject xsso)
{
XmlSchemaSimpleType xst = null;
XmlSchemaComplexType xsCt = null;
List<string> values = new List<string>();
if (xsso == null)
{
return values;
}
if (xsso is XmlSchemaAttribute)
{
XmlSchemaAttribute xsa = (XmlSchemaAttribute)xsso;
xst = xsa.AttributeSchemaType;
}
else
{
XmlSchemaElement xse = (XmlSchemaElement)xsso;
XmlSchemaType gxst = xse.ElementSchemaType;
if (gxst is XmlSchemaSimpleType)
{
xst = (XmlSchemaSimpleType)gxst;
}
else if (gxst is XmlSchemaComplexType)
{
xsCt = (XmlSchemaComplexType)gxst;
}
else
{
return values;
}
}
if(xst != null)
{
if (xst.TypeCode == XmlTypeCode.Boolean)
{
values.Add("true");
values.Add("false");
}
else
{
ProcessXmlSimpleType(xst, values);
}
}
else if (xsCt != null)
{
XmlSchemaContentType xsContent = (XmlSchemaContentType) xsCt.ContentType;
XmlSchemaContentModel xsModel = (XmlSchemaContentModel)xsCt.ContentModel;
if (xsModel is XmlSchemaSimpleContent)
{
XmlSchemaSimpleContent xsSC = (XmlSchemaSimpleContent)xsModel;
XmlSchemaContent xsRE = xsSC.Content;
if (xsRE != null)
{
if (xsRE is XmlSchemaSimpleContentRestriction)
{
XmlSchemaSimpleContentRestriction xsCCR = (XmlSchemaSimpleContentRestriction)xsRE;
foreach (XmlSchemaObject xso in xsCCR.Facets)
{
if (xso is XmlSchemaEnumerationFacet)
{
XmlSchemaEnumerationFacet xsef = (XmlSchemaEnumerationFacet)xso;
values.Add(xsef.Value);
}
}
}
}
}
else
{
XmlSchemaComplexContent xsCC = (XmlSchemaComplexContent)xsModel;
XmlSchemaContent xsRE = xsCC.Content;
if (xsRE != null)
{
if (xsRE is XmlSchemaComplexContentRestriction)
{
XmlSchemaComplexContentRestriction xsR = (XmlSchemaComplexContentRestriction)xsRE;
}
else if (xsRE is XmlSchemaComplexContentExtension)
{
XmlSchemaComplexContentExtension xsE = (XmlSchemaComplexContentExtension)xsRE;
}
}
}
}
return values;
}
And to process a simple type:
private static void ProcessXmlSimpleType(XmlSchemaSimpleType xst, List<string> values)
{
if (xst == null)
{
return;
}
XmlSchemaSimpleTypeContent xsstc = xst.Content;
if (xsstc is XmlSchemaSimpleTypeRestriction)
{
XmlSchemaSimpleTypeRestriction xsr = (XmlSchemaSimpleTypeRestriction)xsstc;
XmlSchemaObjectCollection xsoc = xsr.Facets;
XmlSchemaSimpleType bastTypeOfRestiction = xsr.BaseType;
foreach (XmlSchemaObject xso in xsoc)
{
if (xso is XmlSchemaEnumerationFacet)
{
XmlSchemaEnumerationFacet xsef = (XmlSchemaEnumerationFacet)xso;
values.Add(xsef.Value);
}
}
}
else if (xsstc is XmlSchemaSimpleTypeList)
{
XmlSchemaSimpleTypeList xsstL = (XmlSchemaSimpleTypeList)xsstc;
XmlSchemaSimpleType xstL = xsstL.BaseItemType;
ProcessXmlSimpleType(xstL, values); // recursive
}
else if (xsstc is XmlSchemaSimpleTypeUnion)
{
XmlSchemaSimpleTypeUnion xstU = (XmlSchemaSimpleTypeUnion)xsstc;
XmlSchemaSimpleType[] xsstArray = xstU.BaseMemberTypes;
foreach (XmlSchemaSimpleType xsstA in xsstArray)
{
ProcessXmlSimpleType(xsstA, values); // recursive
}
}
}
The above code snippets probably address 20% of what's needed, but hopefully give you some idea of what you will be dealing with. .NET provides a very powerful set of classes for analysing the Schema Object Model, but you will need detailed knowledge of the XML Schema specification to get usable results.
XML editors should still provide auto-completion help when the XML is not valid, this adds an extra dimension to the problem because there may be ambiguities if there's limited validation context and the schema design is more 'russian-doll' than 'salami sliced'.
Summary
Getting a list of expected XML schema particles for a given context within an XML instance using .NET is possible but relatively complex. In view of this, it would be worthwhile to first check if libraries from existing .NET XML editors provide the functionality you need.
For a working implementation under LGPL have a look at SharpDevelops XmlEditor part.
You get the code completion for xml in one dll, namely the XmlEditor.dll in the AddIns/DisplayBindings directory.

WPF: Drag and drop virtual files into Windows explorer

I'm developing an application similar to dropbox and i show the remote files on a WPF listview. I want to drag those elements and drop it into windows explorer.
I've seen code like this:
var dataObject = new DataObject(DataFormats.FileDrop, files.ToArray());
dataObject.SetData(DataFormats.StringFormat, dataObject);
DoDragDrop(dataObject, DragDropEffects.Copy);
But as you may think, those file are not at the local system yet, before copiying them I need to connect to server, donwload and unzip the files. Like a ftp client does.
I dont how to do it but i was wondering if there is any "drop" event or similiar that i can handle.
Thanks!
This snippet:
var virtualFileDataObject = new VirtualFileDataObject(
// BeginInvoke ensures UI operations happen on the right thread
(vfdo) => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Visible)),
(vfdo) => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Collapsed)));
// Provide a virtual file (downloaded on demand), its URL, and descriptive text
virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[]
{
new VirtualFileDataObject.FileDescriptor
{
Name = "DelaysBlog.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("http://blogs.msdn.com/delay/rss.xml");
stream.Write(data, 0, data.Length);
}
}
},
});
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(CFSTR_INETURLA).Id),
Encoding.Default.GetBytes("http://blogs.msdn.com/delay/rss.xml\0"));
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(DataFormats.Text).Id),
Encoding.Default.GetBytes("[The RSS feed for Delay's Blog]\0"));
DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Copy);
Using the class linked should work. . Very nice and easy solution.
http://pavanpodila.spaces.live.com/blog/cns!9C9E888164859398!190.entry
http://pavanpodila.spaces.live.com/blog/cns!9C9E888164859398!199.entry
http://pavanpodila.spaces.live.com/blog/cns!9C9E888164859398!225.entry
See this series of articles. This should help you get started.
EDIT: See this for an amplementation of the dragsourceadvisor
internal class ImagesViewPanelDragSourceAdvisor : IDragSourceAdvisor
{
private FrameworkElement _dragSource;
public DependencyObject DragSource
{
get
{
return _dragSource;
}
set
{
_dragSource = value as FrameworkElement;
}
}
public DependencyObject DragObject { get; set; }
public DragDropEffects GetDragDropEffects()
{
DragDropEffects effects = DragDropEffects.None;
FrameworkElement frameworkObj = DragObject as FrameworkElement;
if (frameworkObj != null && frameworkObj.DataContext is ImageViewModel)
{
effects = DragDropEffects.Copy;
}
return effects;
}
public IDataObject GetDragDataObject()
{
Debug.Assert(GetDragDropEffects() != DragDropEffects.None);
ImagesViewModel imagesVM = (FrameworkElement)DragSource).DataContext as ImagesViewModel;
StringCollection fileList = new StringCollection();
foreach (ImageViewModel imageVM in imagesVM.Items.Where(imageVM => imageVM.IsSelected))
{
fileList.Add(imageVM.ImagePath);
}
Debug.Assert(fileList.Count > 0);
DataObject dataObj = new DataObject();
dataObj.SetFileDropList(fileList);
return dataObj;
}
public void FinishDrag(DragDropEffects finalEffect)
{
}

Categories

Resources