WPF: Drag and drop virtual files into Windows explorer - c#

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)
{
}

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);
}

UWP Event on content changed

I want to observe on a folder. Like i want a Event When the content is changed.
I found this
var options = new Windows.Storage.Search.QueryOptions
{
FolderDepth = Windows.Storage.Search.FolderDepth.Deep
};
var query = Folder.CreateFileQueryWithOptions(options);
query.ContentsChanged += QueryContentsChanged;
var files = await query.GetFilesAsync();
private void QueryContentsChanged(IStorageQueryResultBase sender, object args)
{
//Code here
}
But the problem with this is I can't find which file caused the event and i even can't know what caused the Event (Like Modify , Create , Delete or Rename of file) How to get these details?
I used this code
public async void MonitorFolder()
{
var options = new Windows.Storage.Search.QueryOptions
{
FolderDepth = Windows.Storage.Search.FolderDepth.Deep
};
var query = Folder1.CreateFileQueryWithOptions(options);
query.ContentsChanged += QueryContentsChanged;
var files = await query.GetFilesAsync();
await addtoOld(Folder1, Old);
}
private async void addtoOld(StorageFolder folder1, List<FDate> old)
{
var files = await folder1.GetFilesAsync();
foreach (var file in files)
{
BasicProperties basicProperties = await file.GetBasicPropertiesAsync();
FDate f = new FDate
{
Path = file.Path,
Id = file.FolderRelativeId,
Modified = basicProperties.DateModified,
Change = ChangeType.NoChange,
FileType = Type.File
};
old.Add(f);
}
var folders = await folder1.GetFoldersAsync();
foreach (var folder in folders)
{
BasicProperties basicProperties = await folder.GetBasicPropertiesAsync();
FDate f = new FDate
{
Path = folder.Path,
Id = folder.FolderRelativeId,
Modified = basicProperties.DateModified,
Change = ChangeType.NoChange,
FileType = Type.Folder
};
old.Add(f);
addtoOld(folder, old);
}
return;
}
private async void QueryContentsChanged(IStorageQueryResultBase sender, object args)
{
New.Clear();
List<FDate> changed = new List<FDate>();
await addtoOld(Folder1, New);
foreach(var f in New)
{
var f1 = getFile(f);
if (f1 != null)
{
if (f1.Modified < f.Modified)
{
f1.Change = ChangeType.Modified;
changed.Add(f1);
}
Old.Remove(f1);
}
else
{
f.Change = ChangeType.Created;
changed.Add(f);
}
}
foreach (var f in Old)
{
f.Change = ChangeType.Deleted;
changed.Add(f);
}
Old = New;
foreach (var f in changed)
{
if(f.FileType== Type.File)
{
if (f.Change == ChangeType.Modified)
{
//code here
}
if(f.Change == ChangeType.Created)
{
//Created code here
}
if(f.Change == ChangeType.Deleted)
{
//Deleted code here
}
}
else
{
if (f.Change == ChangeType.Created)
{
//Created code here
}
if(f.Change == ChangeType.Deleted)
{
//Deleted code here
}
}
}
private FDate getFile(FDate f)
{
foreach(var fi in Old)
{
if (f.Name == fi.Name)
return fi;
}
return null;
}
This code is not working properly I looks like it is because the addtoOld is async The code can't be substituted because it is recursive. and the function can't be made sync it has many await how do i solve this?
Note:OLD and New are Lists ChangeType and Type are enums
According to the following blog post, there is unfortunately no way to identify the reason of the event and there is also no information about affected items.
File system change notifications in WinRT: http://lunarfrog.com/blog/filesystem-change-notifications
So I guess you will have to go through all files and check their Properties property to determine when each file was last modified, created etc.: https://learn.microsoft.com/en-us/uwp/api/windows.storage.storagefile

How to check size of library document file before uploaded with event receiver

I'm new in sharepoint environment, but i have a question
I wanna Create An Event Receiver to check the size of file in library that's user will upload,
because I want to reject sizes bigger than 1 MB.
any help will be appreciated
What I have tried:
I'm tried catch the attachment file to get it's size but I can't !
public override void ItemAdding(SPItemEventProperties properties){
using (SPSite oSPsite = new SPSite("http://win-sp:1001/"))
{
using (SPWeb oSPWeb = oSPsite.OpenWeb())
{
oSPWeb.AllowUnsafeUpdates = true;
SPDocumentLibrary oLibrary = oSPWeb.Lists["myLibrary"] as SPDocumentLibrary;
if (oLibrary != null)
{
properties.BeforeProperties["Title"].ToString();
}
}
}
base.ItemAdding(properties);
}
You can try and modify the below mentioned code:
We need to use either the properties.AfterProperties["vti_filesize"] or properties.ListItem.File.TotalLength to check the file size.
public override void ItemAdding(SPItemEventProperties properties)
{
long validFileSize = 1000000;//1MB;
long currentFileSize;
if (properties.ListItem == null)
{
using (SPWeb web = properties.OpenWeb())
{
if (properties.ListTitle.ToLower() == "myLibrary")
{
currentFileSize = long.Parse(properties.AfterProperties["vti_filesize"].ToString());
if (currentFileSize > validFileSize)
{
return false;
}
}
}
}
else if (properties.ListItem.ParentList.Title.ToLower() == "myLibrary")
{
currentFileSize = properties.ListItem.File.TotalLength;
if (currentFileSize > validFileSize)
{
return false;
}
}
return true;
}

OpenXml Excel: throw error in any word after mail address

I read Excel files using OpenXml. all work fine but if the spreadsheet contains one cell that has an address mail and after it a space and another word, such as:
abc#abc.com abc
It throws an exception immediately at the opening of the spreadsheet:
var _doc = SpreadsheetDocument.Open(_filePath, false);
exception:
DocumentFormat.OpenXml.Packaging.OpenXmlPackageException
Additional information:
Invalid Hyperlink: Malformed URI is embedded as a
hyperlink in the document.
There is an open issue on the OpenXml forum related to this problem: Malformed Hyperlink causes exception
In the post they talk about encountering this issue with a malformed "mailto:" hyperlink within a Word document.
They propose a work-around here: Workaround for malformed hyperlink exception
The workaround is essentially a small console application which locates the invalid URL and replaces it with a hard-coded value; here is the code snippet from their sample that does the replacement; you could augment this code to attempt to correct the passed brokenUri:
private static Uri FixUri(string brokenUri)
{
return new Uri("http://broken-link/");
}
The problem I had was actually with an Excel document (like you) and it had to do with a malformed http URL; I was pleasantly surprised to find that their code worked just fine with my Excel file.
Here is the entire work-around source code, just in case one of these links goes away in the future:
void Main(string[] args)
{
var fileName = #"C:\temp\corrupt.xlsx";
var newFileName = #"c:\temp\Fixed.xlsx";
var newFileInfo = new FileInfo(newFileName);
if (newFileInfo.Exists)
newFileInfo.Delete();
File.Copy(fileName, newFileName);
WordprocessingDocument wDoc;
try
{
using (wDoc = WordprocessingDocument.Open(newFileName, true))
{
ProcessDocument(wDoc);
}
}
catch (OpenXmlPackageException e)
{
e.Dump();
if (e.ToString().Contains("The specified package is not valid."))
{
using (FileStream fs = new FileStream(newFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
UriFixer.FixInvalidUri(fs, brokenUri => FixUri(brokenUri));
}
}
}
}
private static Uri FixUri(string brokenUri)
{
brokenUri.Dump();
return new Uri("http://broken-link/");
}
private static void ProcessDocument(WordprocessingDocument wDoc)
{
var elementCount = wDoc.MainDocumentPart.Document.Descendants().Count();
Console.WriteLine(elementCount);
}
}
public static class UriFixer
{
public static void FixInvalidUri(Stream fs, Func<string, Uri> invalidUriHandler)
{
XNamespace relNs = "http://schemas.openxmlformats.org/package/2006/relationships";
using (ZipArchive za = new ZipArchive(fs, ZipArchiveMode.Update))
{
foreach (var entry in za.Entries.ToList())
{
if (!entry.Name.EndsWith(".rels"))
continue;
bool replaceEntry = false;
XDocument entryXDoc = null;
using (var entryStream = entry.Open())
{
try
{
entryXDoc = XDocument.Load(entryStream);
if (entryXDoc.Root != null && entryXDoc.Root.Name.Namespace == relNs)
{
var urisToCheck = entryXDoc
.Descendants(relNs + "Relationship")
.Where(r => r.Attribute("TargetMode") != null && (string)r.Attribute("TargetMode") == "External");
foreach (var rel in urisToCheck)
{
var target = (string)rel.Attribute("Target");
if (target != null)
{
try
{
Uri uri = new Uri(target);
}
catch (UriFormatException)
{
Uri newUri = invalidUriHandler(target);
rel.Attribute("Target").Value = newUri.ToString();
replaceEntry = true;
}
}
}
}
}
catch (XmlException)
{
continue;
}
}
if (replaceEntry)
{
var fullName = entry.FullName;
entry.Delete();
var newEntry = za.CreateEntry(fullName);
using (StreamWriter writer = new StreamWriter(newEntry.Open()))
using (XmlWriter xmlWriter = XmlWriter.Create(writer))
{
entryXDoc.WriteTo(xmlWriter);
}
}
}
}
}
The fix by #RMD works great. I've been using it for years. But there is a new fix.
You can see the fix here in the changelog for issue #793
Upgrade OpenXML to 2.12.0.
Right click solution and select Manage NuGet Packages.
Implement the fix
It is helpful to have a unit test. Create an excel file with a bad email address like test#gmail,com. (Note the comma instead of the dot).
Make sure the stream you open and the call to SpreadsheetDocument.Open allows Read AND Write.
You need to implement a RelationshipErrorHandlerFactory and use it in the options when you open. Here is the code I used:
public class UriRelationshipErrorHandler : RelationshipErrorHandler
{
public override string Rewrite(Uri partUri, string id, string uri)
{
return "https://broken-link";
}
}
Then you need to use it when you open the document like this:
var openSettings = new OpenSettings
{
RelationshipErrorHandlerFactory = package =>
{
return new UriRelationshipErrorHandler();
}
};
using var document = SpreadsheetDocument.Open(stream, true, openSettings);
One of the nice things about this solution is that it does not require you to create a temporary "fixed" version of your file and it is far less code.
Unfortunately solution where you have to open file as zip and replace broken hyperlink would not help me.
I just was wondering how it is posible that it works fine when your target framework is 4.0 even if your only installed .Net Framework has version 4.7.2.
I have found out that there is private static field inside System.UriParser that selects version of URI's RFC specification. So it is possible to set it to V2 as it is set for .net 4.0 and lower versions of .Net Framework. Only problem that it is private static readonly.
Maybe someone will want to set it globally for whole application. But I wrote UriQuirksVersionPatcher that will update this version and restore it back in Dispose method. It is obviously not thread-safe but it is acceptable for my purpose.
using System;
using System.Diagnostics;
using System.Reflection;
namespace BarCap.RiskServices.RateSubmissions.Utility
{
#if (NET20 || NET35 || NET40)
public class UriQuirksVersionPatcher : IDisposable
{
public void Dispose()
{
}
}
#else
public class UriQuirksVersionPatcher : IDisposable
{
private const string _quirksVersionFieldName = "s_QuirksVersion"; //See Source\ndp\fx\src\net\System\_UriSyntax.cs in NexFX sources
private const string _uriQuirksVersionEnumName = "UriQuirksVersion";
/// <code>
/// private enum UriQuirksVersion
/// {
/// V1 = 1, // RFC 1738 - Not supported
/// V2 = 2, // RFC 2396
/// V3 = 3, // RFC 3986, 3987
/// }
/// </code>
private const string _oldQuirksVersion = "V2";
private static readonly Lazy<FieldInfo> _targetFieldInfo;
private static readonly Lazy<int?> _patchValue;
private readonly int _oldValue;
private readonly bool _isEnabled;
static UriQuirksVersionPatcher()
{
var targetType = typeof(UriParser);
_targetFieldInfo = new Lazy<FieldInfo>(() => targetType.GetField(_quirksVersionFieldName, BindingFlags.Static | BindingFlags.NonPublic));
_patchValue = new Lazy<int?>(() => GetUriQuirksVersion(targetType));
}
public UriQuirksVersionPatcher()
{
int? patchValue = _patchValue.Value;
_isEnabled = patchValue.HasValue;
if (!_isEnabled) //Disabled if it failed to get enum value
{
return;
}
int originalValue = QuirksVersion;
_isEnabled = originalValue != patchValue;
if (!_isEnabled) //Disabled if value is proper
{
return;
}
_oldValue = originalValue;
QuirksVersion = patchValue.Value;
}
private int QuirksVersion
{
get
{
return (int)_targetFieldInfo.Value.GetValue(null);
}
set
{
_targetFieldInfo.Value.SetValue(null, value);
}
}
private static int? GetUriQuirksVersion(Type targetType)
{
int? result = null;
try
{
result = (int)targetType.GetNestedType(_uriQuirksVersionEnumName, BindingFlags.Static | BindingFlags.NonPublic)
.GetField(_oldQuirksVersion, BindingFlags.Static | BindingFlags.Public)
.GetValue(null);
}
catch
{
#if DEBUG
Debug.WriteLine("ERROR: Failed to find UriQuirksVersion.V2 enum member.");
throw;
#endif
}
return result;
}
public void Dispose()
{
if (_isEnabled)
{
QuirksVersion = _oldValue;
}
}
}
#endif
}
Usage:
using(new UriQuirksVersionPatcher())
{
using(var document = SpreadsheetDocument.Open(fullPath, false))
{
//.....
}
}
P.S. Later I found that someone already implemented this pathcher: https://github.com/google/google-api-dotnet-client/blob/master/Src/Support/Google.Apis.Core/Util/UriPatcher.cs
I haven't use OpenXml but if there's no specific reason for using it then I highly recommend LinqToExcel from LinqToExcel. Example of code is here:
var sheet = new ExcelQueryFactory("filePath");
var allRows = from r in sheet.Worksheet() select r;
foreach (var r in allRows) {
var cella = r["Header"].ToString();
}

Reading XML ElementExtensions in C# Win8.1 App

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;)
}

Categories

Resources