tag:blogger.com,1999:blog-39110403795363767232024-02-06T23:45:21.935-06:00The Old man and the c#Dedicated to share experiences of an experienced developer who has been in the role of technologist, business analyst and architect for over thirty years.
The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.comBlogger11125tag:blogger.com,1999:blog-3911040379536376723.post-2553264718933527562020-09-04T17:12:00.000-05:002020-09-04T17:12:03.792-05:00A new posting to CodeProject.<p> So it's been awhile. Still working with Polaris Solution working from home like most of us lately.</p><p>Today I released a small project using CodeProject's Import GitHub project. Those guys at CodeProject have done a great job to connect to and reduce the friction between themselves, In the past I would write an article on CodeProject, upload images and insert codesnippets. Now all this can be done with the Readme.md file in GitHub.</p><p>Additionally, the latest files are always kept in sync with GitHib and the files no longer need to be uploaded separately. </p><p>The project allows you to visualize more easily the dependencies between database objects. See how a stored procedure connects to a table, view etc. See the table definition as well as procedures, triggers and views.</p><p> Here is the link on <a href="https://www.codeproject.com/Articles/5278677/SPDiscover-Testing-Large-Systems-Built-on-Stored-P" target="_blank">CodeProject</a>:</p><p>and on <a href="https://github.com/PolarisSteve/SPDiscover" target="_blank">GitHub</a>:</p><p><br /></p>The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-61411900625121032632017-10-01T00:30:00.000-05:002017-12-15T13:52:43.396-06:00Working with DocuSign<div>
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: large;">A Starting point</span></span><br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;">Recently my customer made the business decision to streamline their contracting process. Currently a sales agent will create a set of documents which will be downloaded, printed and signed off by the prospect. Once the prospect signs the contract, it is faxed back and countersigned. After countersigning, the document would be scanned and saved for reference.</span></span><br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;"><br /></span></span>
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;">After much due diligence, my customer decided to go with a solution provided by DocuSign. It provides the ability to send documents to recipients for their viewing and / or approval.</span></span><br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;"><br /></span></span>
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif; font-size: large;">Background</span><br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;">I was tasked with incorporating this solution using a set of APIs provided by DocuSign. The API calls are provided as both SOAP and Rest implementations and feature an extensive array of documentation and tools to help with implementation.</span></span><br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;"><br /></span></span>
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;">I made the decision to go forward with the Rest implementation because of the ease of implementation and configuration.</span></span><br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;"><br /></span></span>
<br />
<span style="color: #999999; font-family: "segoe ui" , "arial" , sans-serif;"><span style="font-size: 14px;">DocuSign provides an API Explorer allowing you the opportunity to explore the API without writing any code. Before using the explorer you will need to create a free developer account with which you can explore both the API and the administration portal of your account.</span></span><br />
<a href="https://www.docusign.com/developer-center#form-devaccount" target="_blank">https://www.docusign.com/developer-center#form-devaccount</a><br />
<span style="color: #999999;">Using the portal, you can learn some of the capabilities of DocuSign including creating a new document(s) from uploading an existing document, using a template or getting from a cloud service such as Dropbox or OneDrive. Additionally you will learn to create the email distribution list to send the document(s) to, as well as how to set their order and what each person has to do in the document chain.</span><br />
<span style="color: #999999;"> </span><img src="https://blogger.googleusercontent.com/img/proxy/AVvXsEgRLcmrQ6UGEtUV6ZRQK8eFWZQiZhPk37c2Yt4i2lfoW5B9CD0H4iM8KmJRRp9Kz8btau6465Oo3rVqZifB5b6WOUfSkDA2dOcDm2i7hTmdVHWHV9m4Fgs2pN0jcHkvdLAmGWqcFNXKtGcXc9F2Pw-bFyJDp6l2sJ9pEbw=" /><br />
<span style="color: #999999;">After you create a document and assign a distribution list you will be given the opportunity to annotate the document with fields to be filled in by each email participant in the chain.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;">DocuSign uses the metaphor of “tabs”, like a sticky tag to indicate where input is required. Types of tabs include signature, date, and check box for acceptance of terms.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;">By default you place a “tab” in a location on a document for a recipient. Of course when you’re adding document programmatically you may not have the opportunity to assign to a particular page or location on a page. In that event, DocuSign allows you to search for anchor text to anchor a tab too.</span><br />
<img src="https://blogger.googleusercontent.com/img/proxy/AVvXsEizVibq5z6VyoeTwhLPCUM18XaZHryH8hyL5c-CEfyTz93pE_p5PaBJEMjQFlE8qDHwtBKX0_2xNlhPJ8n8jPGYDL_lDcX2RAQIZN_a10HZsyVA8v6pDCQ3y65V3STAE_npce7QIFTIORQ0OjCZcVqI6K0yegYiggLM7jdJ=" /><br />
<span style="color: #999999;">The customer will sign first, when signed by the customer Polaris Solutions will be emailed the signed document for counter signing. Note you must agree to their terms to continue.</span><br />
<img src="https://blogger.googleusercontent.com/img/proxy/AVvXsEjP8lB14r8djePG2ym_fJC9IsJFVJ22vzl-VbyOHc4nVY8B1J2snwS_8EG4-uKvAkjW8thx9tzEnP5jrdnANQc6isHpGVidduGbS3FIdliPQz3YQnpbIDJwE8WGu-o6zLaEkQzzo-WKG2KTuiBhR09eV0UdM5fKU2S8NJY=" /><br />
<span style="color: #999999;">DocuSign makes an analogy of a physical envelope as to how the document is inserted and sent to a recipient. In my opinion, I would say a better metaphor would be to compare to an inner office envelope (those envelopes used for internal mail and routing) that allow someone who reads the outside of the envelope the instructions to disseminate to a group of people and how returned documents should be subsequently forwarded for further processing</span><br />
<img src="http://www.codeproject.com/KB/webservices/1065905/fourth.png" height="625" style="background-color: white; border: 0px; color: #111111; font-family: "segoe ui", arial, sans-serif; font-size: 14px; margin: 0px; padding: 0px;" width="600" /><br />
<span style="color: #999999;">By default, all downstream actors involved in the email chain are given updates via email as to the status of the envelope. By downstream, I mean that the sender will get notification of the first set of emails sent, whether they have been looked at or signed by the actors etc. Recipients which come later in the process, will not get notifications until the rules (this can be changed, but not default) have been satisfied. If you assign a cc to a document (think agent gets a copy of document sent for signature) that person will also get notified going forward as the documents are signed and subsequently counter signed or rejected.</span><br />
<img src="http://www.codeproject.com/KB/webservices/1065905/fifth.png" height="329" style="background-color: white; border: 0px; color: #111111; font-family: "segoe ui", arial, sans-serif; font-size: 14px; margin: 0px; padding: 0px;" width="600" /><br />
<span style="color: #999999;">At any point, you can see what the history of the documents are</span><br />
<img src="http://www.codeproject.com/KB/webservices/1065905/eigth.png" height="389" style="background-color: white; border: 0px; color: #111111; font-family: "segoe ui", arial, sans-serif; font-size: 14px; margin: 0px; padding: 0px;" width="600" /><br />
<span style="color: #999999;">Using these techniques DocuSign allows you to orchestrate a sophisticated document workflow keeping all players in the loop.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999; font-size: large;">Using the RestAPI</span><br />
<span style="color: #999999;">You get a real feel that the site is using the same API structure that you as a developer would use to do the same work programmatically. I was able to duplicate the same process shown earlier using a single call to their API. The key is creating a single object filled with the data required to describe the documents, the recipients of the documents and their role (signer, CC etc.) and tabs to be signed for each recipient included.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;"><a href="http://iodocs.docusign.com/" target="_blank">http://iodocs.docusign.com/</a></span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;">The first step in the process was to confirm using the API Explorer, this allows you to see that the same steps created in the DocuSign portal can be accomplished via their API. The tool is very simple to use and visually supplies similar features found in the portal. The tool also shows the request and response provided.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;">The API contains well over seventy different API calls but I was able to create everything our client needed with two. The calls are broken down into the following categories:</span><br />
<span style="color: #999999;"><br /></span>
<br />
<ul>
<li>Login</li>
<li>Request Signature</li>
<li>Views</li>
<li>Status</li>
<li>Envelope</li>
<li>Settings</li>
<li>Account</li>
<li>Billing</li>
<li>Connect</li>
</ul>
<br />
<span style="color: #999999;">One call to create the envelope v2/accounts/:accountId/envelopes under Request Signature with all the needed data and another to retrieve information about an existing envelope and modify a recipient email address.</span><br />
<span style="color: #999999;"><br /></span>
<img src="http://www.codeproject.com/KB/webservices/1065905/sixth.png" height="536" style="background-color: white; border: 0px; color: #111111; font-family: "segoe ui", arial, sans-serif; font-size: 14px; margin: 0px; padding: 0px;" width="600" /><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;">You do have the ability as noted by the categories above to request status information about your envelopes. Beware there is a strict limit on polling their servers for status information for an envelope. Each envelope can be polled once every fifteen minutes. Exceeding this, can cause them to discontinue their service. If you need near real time status of your envelopes, they offer an option called DocuSign Connect which is described as a push service that sends realtime envelope and recipient data updates to customer listener applications.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999; font-size: large;">Calling the RestAPI from code</span><br />
<span style="color: #999999;">After you have confirmed that the process meets your requirements, it’s time to implement this same process in code. Since I chose the path of using the Rest API, the next question was how I was going to go forward. Was I going to use a something like Restsharp or roll my own, or was their another option available? As it turns out, DocuSign goes the extra step to create an open source .Net library located in Github, this library provides a simple interface to connect to the Rest API and includes all the classes which represent the data sent to the DocuSign.</span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;"><a href="https://github.com/docusign/docusign-csharp-client" target="_blank">https://github.com/docusign/docusign-csharp-client</a></span><br />
<span style="color: #999999;"><br /></span>
<span style="color: #999999;"></span><br />
<span style="color: #999999;">Using the DocuSign .Net Client is really easy to implement. Start by using nuGet to download the latest from the github repository.</span><br />
<div>
<img src="http://www.codeproject.com/KB/webservices/1065905/seventh.png" height="349" style="background-color: white; border: 0px; color: #111111; font-family: "segoe ui", arial, sans-serif; font-size: 14px; margin: 0px; padding: 0px;" width="600" /></div>
<span style="color: #999999;"><br /></span>
<span style="color: #999999;">To actually recreate what I did through the web portal is rather simple using the DocuSign .Net client library. It is literally accomplished with two calls with the proper objects created with the proper data.</span><br />
<ul>
<li>Step 1, login:</li>
<ul>
<li>Set instance properties on the instance of the RestSettings object:</li>
<ul>
<li>IntegratorKey</li>
<li>Later on, the WebServiceUrl (needed when going to production)</li>
</ul>
<li>Create the Account object. It requires the following items to be set:</li>
<ul>
<li>UserName</li>
<li>Password</li>
<li>Account Email</li>
</ul>
<li>Login using the Account instance.</li>
</ul>
</ul>
</div>
<div>
<pre lang="cs" style="background-color: #fbedbb; border: 1px solid rgb(251, 237, 187); color: black; font-family: consolas, "courier new", courier, mono; font-size: 9pt; font-stretch: normal; line-height: normal; overflow: auto; padding: 6px;">RestSettings.Instance.IntegratorKey = _AccountInfo.IntegratorKey;
RestSettings.Instance.WebServiceUrl = @"<u>https://*********/restapi/v2</u>";
Account dsAccount = new Account() { UserName = _AccountInfo.UserName, Password = _AccountInfo.UserPassword, Email = _AccountInfo.UserEmail };
if (dsAccount.Login())
{
//Envelope created here and sent when login is successful
}</pre>
<ol style="background-color: white; border: 0px; color: #111111; font-family: "segoe ui", arial, sans-serif; font-size: 14px; margin: 10px 0px; padding: 0px 0px 0px 40px;"></ol>
<div style="line-height: normal;">
<br />
<span style="color: #999999;">Step 2, prepare envelope and send if login is successful:</span><br />
<br />
<ul>
<li>Create an Envelope</li>
<li>Set the Status to “sent” (if not set to sent the document will be created as a draft and never be sent)</li>
</ul>
</div>
<pre lang="cs" style="background-color: #fbedbb; border: 1px solid rgb(251, 237, 187); color: black; font-family: consolas, "courier new", courier, mono; font-size: 9pt; font-stretch: normal; line-height: normal; overflow: auto; padding: 6px;">Envelope ev = new Envelope();
ev.Status = "sent";</pre>
<ul style="border: 0px; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; margin: 10px 0px; padding: 0px 0px 0px 40px;">
<li style="border: 0px; line-height: normal; margin: 0px; padding: 0px;"><span style="color: #999999;">Add a new Recipients object to the envelope</span></li>
<li style="border: 0px; line-height: normal; margin: 0px; padding: 0px;"><span style="color: #999999;">Add a list of Signers to the Recipients</span></li>
<li style="border: 0px; line-height: normal; margin: 0px; padding: 0px;"><span style="color: #999999;">For each Signer add the tabs for signature (or other types)</span></li>
</ul>
<div>
</div>
<pre lang="cs" style="background-color: #fbedbb; border: 1px solid rgb(251, 237, 187); color: black; font-family: consolas, "courier new", courier, mono; font-size: 9pt; font-stretch: normal; line-height: normal; overflow: auto; padding: 6px;">ev.Recipients = new DocuSign.Integrations.Client.Recipients();
List<Signer> ls = new List<Signer>();
foreach (signer you want to add)
{
Signer person = new Signer() { name = Name, email = Email, recipientId = Id.ToString(), routingOrder = OrderId.ToString() };
//Add tabs to each person (if needed)
person.tabs = new Tabs();
foreach (tab you want to assign to the person)
{
//We use the AnchorText to have the tab placed next to text specified
ltab.Add(new Tab() { name = tb.Name, anchorString = tb.AnchorText });
//tabs have a list for each type
person.tabs.signHereTabs = ltab.ToArray();
//or
person.tabs.initialHereTabs = ltab.ToArray();
//or
person.tabs.fullNameTabs = ltab.ToArray();
}
ev.Recipients.signers = ls.ToArray();
}</pre>
<ul style="border: 0px; margin: 10px 0px; padding: 0px 0px 0px 40px;">
<li>Optionally add the following to the envelope:</li>
<ul>
<li>Signers of other types (carbon copies)</li>
<li>Notifications (reminders and expiration's<span style="color: #999999;">)</span></li>
</ul>
</ul>
<div>
<br /></div>
<div>
<span style="color: #999999;"><br /></span></div>
<pre lang="cs" style="background-color: #fbedbb; border: 1px solid rgb(251, 237, 187); color: black; font-family: consolas, "courier new", courier, mono; font-size: 9pt; font-stretch: normal; line-height: normal; overflow: auto; padding: 6px;">ev.Notification = new Notification();
ev.Notification.UseAccountDefaults = false;
//Send a reminder email to sign
ev.Notification.Reminders = new Reminders() { ReminderDelay = 1, ReminderEnabled = true, ReminderFrequency = 3};
//Send an email to warn the document will expire and no long be able to be signed
ev.Notification.Expirations = new Expirations() { ExpireEnabled = true, ExpireWarn = 2, ExpireAfter = 3 };</pre>
<ul style="border: 0px; margin: 10px 0px; padding: 0px 0px 0px 40px;">
<li style="border: 0px; line-height: normal; margin: 0px; padding: 0px;"><div style="line-height: normal;">
Add a list of Documents to the envelope</div>
</li>
<li>Attach the Account document to the envelope</li>
<li>Call Create to send the document byte data and document metadata.</li>
<li>If the response is true, the envelopeID will contain the string DocuSign ID, otherwise the Envelope.RestError.message will contain the reason for failure.</li>
</ul>
<div>
</div>
<pre style="background-color: #fbedbb; border: 1px solid rgb(251, 237, 187); color: black; font-family: consolas, "courier new", courier, mono; font-size: 9pt; font-stretch: normal; line-height: normal; overflow: auto; padding: 6px;">//Byte data for each files
List<byte[]> lb = new List<byte[]>();
//Document data for each file.
List<Document> ld = new List<Document>();
int docId = 1;
foreach(file)
{
//in our case all the documents sent were already PDF files.
lb.Add(getFileBytes("filename"));
ld.Add(new Document() { name = fileName, documentId = docId.ToString(), fileExtension = "pdf" });
docId++;
}
ev.Login = dsAccount;
ev.EmailSubject = EmailSubject;
if (ev.Create(lb, ld))
{
return ev.EnvelopeId;
}
else
{
return ev.RestError.message;
}</pre>
<h2 style="font-family: "segoe ui", arial, sans-serif; font-size: 29px; font-weight: 200; line-height: normal; margin: 20px 0px 11px; padding: 0px 0px 10px;">
</h2>
<div>
<span style="color: #999999;"><span style="font-size: large;">Whats Next?</span></span></div>
<div>
<div>
<span style="color: #999999;">After getting your solution to work, you will want to move it into production. DocusSign requires a process to certify your application.</span></div>
<div>
<span style="color: #999999;"><br /></span></div>
<div>
<span style="color: #999999;"><a href="https://www.docusign.com/developer-center/go-live/certification" target="_blank">https://www.docusign.com/developer-center/go-live/certification</a></span></div>
<div>
<span style="color: #999999;"><br /></span></div>
<div>
<span style="color: #999999;">From DocuSign:</span></div>
<div>
<span style="color: #999999;"><br /></span></div>
<div>
<span style="color: #999999;">After you have developed your integration using your Demo Integrator Key, you must get that Integrator Key certified before moving to production. During the Certification process we check your integration and make sure...</span></div>
<div>
<span style="color: #999999;"><br /></span></div>
<div>
<span style="color: #999999;">You have everything you need to be successful technically</span></div>
<div>
<span style="color: #999999;">You are not doing anything strange, like polling the DocuSign service every 5 minutes</span></div>
<div>
<span style="color: #999999;">You have built an integration that puts both DocuSign and our joint solution in the best possible light</span></div>
<div>
<span style="color: #999999;">Any security concerns are addressed</span></div>
<div>
<span style="color: #999999;">The result of Certification is that your Integrator Key will function with API calls in DocuSign's production environment as opposed to just the demo environment (where your developer sandbox operates).</span></div>
<div>
<span style="color: #999999;"><br /></span></div>
<div>
<span style="color: #999999;">Please review the following guide before you start certifying your integration as it will help minimize delays in processing. For most integrations the API Certification process takes 1-2 days.</span></div>
<div>
<span style="color: #999999;"><br /></span></div>
<div>
<span style="color: #999999; font-size: large;">In Closing:</span></div>
<div>
<span style="color: #999999;">I found the DocuSign product and services easy and fast to work with, we went from initial discussions to production in less than three weeks. Using the .Net Client we passed certification on our first try.</span></div>
</div>
<div>
<br /></div>
</div>
<!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2Fwww.codeproject.com%2FKB%2Fwebservices%2F1065905%2Ffirst.png&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://blogger.googleusercontent.com/img/proxy/AVvXsEgRLcmrQ6UGEtUV6ZRQK8eFWZQiZhPk37c2Yt4i2lfoW5B9CD0H4iM8KmJRRp9Kz8btau6465Oo3rVqZifB5b6WOUfSkDA2dOcDm2i7hTmdVHWHV9m4Fgs2pN0jcHkvdLAmGWqcFNXKtGcXc9F2Pw-bFyJDp6l2sJ9pEbw=" --><!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2Fwww.codeproject.com%2FKB%2Fwebservices%2F1065905%2Fsecond.png&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://blogger.googleusercontent.com/img/proxy/AVvXsEizVibq5z6VyoeTwhLPCUM18XaZHryH8hyL5c-CEfyTz93pE_p5PaBJEMjQFlE8qDHwtBKX0_2xNlhPJ8n8jPGYDL_lDcX2RAQIZN_a10HZsyVA8v6pDCQ3y65V3STAE_npce7QIFTIORQ0OjCZcVqI6K0yegYiggLM7jdJ=" --><!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2Fwww.codeproject.com%2FKB%2Fwebservices%2F1065905%2Fthird.png&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://blogger.googleusercontent.com/img/proxy/AVvXsEjP8lB14r8djePG2ym_fJC9IsJFVJ22vzl-VbyOHc4nVY8B1J2snwS_8EG4-uKvAkjW8thx9tzEnP5jrdnANQc6isHpGVidduGbS3FIdliPQz3YQnpbIDJwE8WGu-o6zLaEkQzzo-WKG2KTuiBhR09eV0UdM5fKU2S8NJY=" -->The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com4tag:blogger.com,1999:blog-3911040379536376723.post-53930472093650051502017-08-18T21:47:00.000-05:002017-09-26T20:09:18.965-05:00Getting the CycleGear Bilt Techno 2.0 with Sena DWO-5 headset to work with Sena's Driver software.<b>UPDATE - 9/26/2017 the following announcement was on the Sena Forum:</b><br />
<b><br /></b>
<span style="background-color: white; color: #555555; font-family: "helvetica" , "arial" , sans-serif; font-size: 15px;">A new version of the Cycle Gear Bluetooth Device Manager has been uploaded to the website. This will allow you to update the firmware for the DWO-5.</span><br />
<span style="background-color: white; color: #555555; font-family: "helvetica" , "arial" , sans-serif; font-size: 15px;"><br /></span>
<b>I have confirmed that the software installs correctly AND that is includes the FTD2XX.DLL which was missing from their last version.</b><br />
<br />
In addition to programming as a profession, I really like to ride motorcycles. Recently I bought myself a really nice Bilt Techno 2.0 modular helmet from CycleGear. They have two local shops in the Chicago area and really do a nice job helping you to select the appropriate gear. This helmet includes a Bluetooth radio DWO-5 supplied by Sena. Out of the box everything worked well. Being the tech type of guy I am, I wanted to try the "Device Manager" software which updates the firmware and allows you to set the speed dial numbers.<br />
<br />
This is my experience getting this to actually work and the poor support I got from Sena's technical support.<br />
<br />
The issue began after the installation was done and the following error appeared.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd5wTzC-DNNe-9Nik-C8CchgGVO5gk4JqDTBOz_t8nsGDmPY8XxUQI1e96unzc6uIs7QsIYKNhqU6oyq4CP983IA0QygGwbTWmtGhZy6L8tMctOxrcSvCgqY0lEn73ybTJo4OaFWWRTgw/s1600/sena+error.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="243" data-original-width="588" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd5wTzC-DNNe-9Nik-C8CchgGVO5gk4JqDTBOz_t8nsGDmPY8XxUQI1e96unzc6uIs7QsIYKNhqU6oyq4CP983IA0QygGwbTWmtGhZy6L8tMctOxrcSvCgqY0lEn73ybTJo4OaFWWRTgw/s320/sena+error.PNG" width="320" /></a></div>
<br />
<br />
It only got worse when I contacted their first level technical support, judging from the accents the technicians had I would guess their call center is located in the Philippines. Very personable, but failed me right off the bat in two ways.<br />
<br />
1. Offering typical suggestions such as Anti Virus, Different machine etc.<br />
2. Failing to escalate my concerns to their engineering team for confirmation of issue and a real fix.<br />
<br />
They did generate a ticket and asked me to supply screen shots, I supplied this as well as screen shots of their own forum with related questions (as well as a simple correct fix) from fellow frustrated riders.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYW2qrTs_1SXoAvQnEfdBHzBVLqp3SzNi3vTwPRRdFhZdxgjdYL-pWkYgpMdiT5No8efmfzFZZ-iP0POGk_VhWrT1N_DJWrhooEFNy54EbZ6xg88hLIjy9HQGRFR_bf97TVK9kfOM17yg/s1600/sena+message+error.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="432" data-original-width="637" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYW2qrTs_1SXoAvQnEfdBHzBVLqp3SzNi3vTwPRRdFhZdxgjdYL-pWkYgpMdiT5No8efmfzFZZ-iP0POGk_VhWrT1N_DJWrhooEFNy54EbZ6xg88hLIjy9HQGRFR_bf97TVK9kfOM17yg/s320/sena+message+error.PNG" width="320" /></a></div>
<a href="https://support.sena.com/hc/en-us/community/posts/115000032426/comments/115004639843" target="_blank">https://support.sena.com/hc/en-us/community/posts/115000032426/comments/115004639843</a><br />
<br />
I also contacted CycleGear directly and was told that they were aware of the problem for several months and could not get Sena to correct. Maybe the people at Sena truly believe there is an no issue on their side, or that the problem exists with everyone's anti-virus. I find it hard to believe though because this missing file is supplied with the Sena branded "Device Manager" installation and not in the CycleGear OEM installation.<br />
<br />
Mind you, I don't think I am asking a lot here. Simply create an installer with all the files to work and test that installation before you ship. It's unreasonable to assume people have multiple machines, that they can shut off their ant-virus or that they need a Windows 7 machine.<br />
<br />
It's even more unreasonable to force the people who buy your equipment to actually do their work. I can point to at least one other person who put in a lot of effort to get it to work, another who probably downloaded a virus and still didn't get it to work and the effort on my part to find the root cause of the problem and put together a document which can help others in the same situation.<br />
<br />
Additionally, I think they may learn a thing or two from their forums, which appear not to be on their radar. Many complaints that the forum appears to be not monitored by them. And bye the way, the fix for this problem was first written about in their forums!<br />
<br />
After receiving complaints for over two months, this was posted 8/18/2017<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbg9CjC8IXFNcR2sFIvFdQTbp5A4XZAjDEFQ69NNPxnvj9SKN462GQT-eUaD7PFzIT9MNSzEtRLvJZ9_4gzVaWW5pIgzVm46abGmVejKJ5rngHIGh3TmjXxPF8pYt1U-uCLapmAVJrnaU/s1600/reply.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="190" data-original-width="793" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbg9CjC8IXFNcR2sFIvFdQTbp5A4XZAjDEFQ69NNPxnvj9SKN462GQT-eUaD7PFzIT9MNSzEtRLvJZ9_4gzVaWW5pIgzVm46abGmVejKJ5rngHIGh3TmjXxPF8pYt1U-uCLapmAVJrnaU/s320/reply.PNG" width="320" /></a></div>
<br />
<br />
<br />
<br />
<br />
Finally, here are two known solutions:<br />
<br />
1. Install Non OEM installer, get file installed on disk, copy over to CycleGear location.<br />
<br />
<ul>
<li>Install the Sena Device Manager</li>
<li>Find the missing file at the install directory:</li>
<ul>
<li>C:\Program Files (x86)\Sena Technologies\SenaBluetoothDeviceManager</li>
</ul>
<li>Copy the missing file to the CycleGear install directory:</li>
<ul>
<li>C:\Program Files (x86)\CycleGear\CycleGearBluetoothDeviceManager</li>
</ul>
</ul>
<br />
<br />
2. Go to where Sena gets the DLL (FTDI Chip) - This is from the company who supplies the DLL to Sena.<br />
<br />
<ul>
<li><a href="http://www.ftdichip.com/Drivers/D2XX.htm" target="_blank">http://www.ftdichip.com/Drivers/D2XX.htm</a> </li>
<li>Download the file for your operating system</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAujS_IIA2wKbEYvxlz1QnbgGAfbAMxmg6TF8Q72MX2m16g5q-gF59Zfu_J4_vbSmf6lbrw8EQvxZMhZw0oPqbVbXXyyqwrRvH6k5Y1q7D6MHgEKxLugLWzzyg_Zxc_ccPFKMloBlifwA/s1600/drivers.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="827" data-original-width="1600" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAujS_IIA2wKbEYvxlz1QnbgGAfbAMxmg6TF8Q72MX2m16g5q-gF59Zfu_J4_vbSmf6lbrw8EQvxZMhZw0oPqbVbXXyyqwrRvH6k5Y1q7D6MHgEKxLugLWzzyg_Zxc_ccPFKMloBlifwA/s320/drivers.PNG" width="320" /></a></div>
<ul>
<li>Open the downloaded zip file and find the correct DLL</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnHxeHkHw1Icgp9HHdxjF1FrsP_twZPHvGeqxhb3mDw-DL25s8U_qUqMAuPQY8Ed8NQaK54NsxbUWUWCssOROYzpSrCuJC7-RKsonRLbHeFKI_M3lIyMPIyJpDoLygzFLWUs74VZxt8cU/s1600/driverfilelocation.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="320" data-original-width="1192" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnHxeHkHw1Icgp9HHdxjF1FrsP_twZPHvGeqxhb3mDw-DL25s8U_qUqMAuPQY8Ed8NQaK54NsxbUWUWCssOROYzpSrCuJC7-RKsonRLbHeFKI_M3lIyMPIyJpDoLygzFLWUs74VZxt8cU/s320/driverfilelocation.PNG" width="320" /></a></div>
<div>
<br /></div>
<br />
<ul>
<li>Copy the missing file to the CycleGear install directory:</li>
<ul>
<li>C:\Program Files (x86)\CycleGear\CycleGearBluetoothDeviceManager</li>
</ul>
</ul>
Now that Sena confirms their is an issue, will the fix be around the corner? Let's hope so.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com3tag:blogger.com,1999:blog-3911040379536376723.post-59792768205286667482017-03-05T14:07:00.000-06:002017-03-05T14:07:25.898-06:00<h2>
<b>Extensions and Reflections and Generics oh my!</b></h2>
<div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXn-8-BRfWHMc-b_9XnNvgusmRvHTmHDkOsNhSZmHIs_4mqzoeJDoaIcehxUGSReFJK20bxecAWnbyfv5FQL5w2FlZygDG6NcAFAVwJERiOHv2ypRn-pPjJGeSw3XEO_BSjJsp8ByJj2U/s1600/5291022370_c7cb484ba6_m.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXn-8-BRfWHMc-b_9XnNvgusmRvHTmHDkOsNhSZmHIs_4mqzoeJDoaIcehxUGSReFJK20bxecAWnbyfv5FQL5w2FlZygDG6NcAFAVwJERiOHv2ypRn-pPjJGeSw3XEO_BSjJsp8ByJj2U/s1600/5291022370_c7cb484ba6_m.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Copyright - https://www.flickr.com/photos/dcwriterdawn/</td></tr>
</tbody></table>
<b><br /></b>
<div class="header" style="background-color: white; border: 0px; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; margin: 0px; padding: 0px;">
<div class="summary" id="ctl00_description" style="border: 0px; color: grey; margin: 0px; padding: 30px 0px 0px;">
<b>Creating an extension class for View Models to save public properties using Generics and Delegates, replacing a reflection implementation.</b></div>
<span class="date" content="https://www.codeproject.com/script/Articles/Images/tip100x80.png" id="ctl00_thumbnailUrl" style="border: 0px; margin: 0px; padding: 0px;"></span></div>
<div class="text" id="contentdiv" style="background-color: white; border: 0px; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; margin: 0px; padding: 10px 0px 0px;">
<div style="background: url("/images/draft.gif"); border: 0px; margin: 0px; padding: 0px;">
<h2 style="color: #ff9900; font-size: 29px; font-weight: 200; line-height: normal; margin: 20px 0px 11px; padding: 0px 0px 10px;">
Introduction</h2>
<div style="line-height: normal;">
Recently I was working on an MVC project in which the client wanted to persist a form used to filter data to a data store so that the form could be recalled with any number of saved states. I chose to persist the data in a dictionary object which could be persisted in a Entity Attribute Value table. </div>
</div>
</div>
<b><br /></b>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
I created two extension methods, one which saves model properties to a Dictionary and the other which applies data from a Dictionary and overwrites properties of the model. </div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
The extension method for Getting the properties is called <strong style="border: 0px; margin: 0px; padding: 0px;">GetModelValues </strong>and the other method is <strong style="border: 0px; margin: 0px; padding: 0px;">ValuesToModel</strong>.</div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
Both extensions are defined as a type of T where T is defined as an Interface. This is to ensure that by simply adding a interface to an existing Model class only Model classes would have these extensions.</div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
Originally I used reflection to read and write to the model, but in this article I describe how I changed the approach to using Generics and Delegates to create a more strongly typed implementation.</div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
The original code required you to decorate the models properties you wanted persist using a special attribute. The replacement code is mapped more declaratively.</div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<b>The complete article and sample code is located at codeproject.com</b></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<a href="https://www.codeproject.com/Tips/1174411/Extensions-and-Reflections-and-Generics-oh-my" target="_blank">https://www.codeproject.com/Tips/1174411/Extensions-and-Reflections-and-Generics-oh-my</a></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
For those of you looking for the answer to the extra credit, here is what I did.</div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;">Dictionary<string, string> pass_this_in = new Dictionary<string, string>();</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;">pass_this_in.Add("name", "Pass this in");</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"><br /></span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> ViewModel2015.ValuesToModel((deleg, dict) =></span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> {</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> if (dict.Keys.Contains(nameof(deleg.name)))</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> deleg.name = dict[nameof(deleg.name)];</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> if (dict.Keys.Contains(nameof(deleg.currentdate)))</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> deleg.currentdate = DateTime.Parse(dict[nameof(deleg.currentdate)]);</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> if (dict.Keys.Contains(nameof(deleg.age)))</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> deleg.age = int.Parse(dict[nameof(deleg.age)]);</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"><br /></span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> }, pass_this_in);</span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"><br /></span></span></div>
<div style="background-color: white; line-height: normal;">
<span style="color: #111111; font-family: Segoe UI, Arial, sans-serif;"><span style="font-size: 14px;"> //ViewModel2015.name will equal "Pass this in"</span></span></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; line-height: normal;">
<br /></div>
<b><br /></b></div>
The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-67243139637079323442017-01-19T09:02:00.000-06:002017-01-19T09:04:06.341-06:00<h2>
appSettings Gone Wild</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHQQOeG0kpPnUEXoqaI45kGeiigl1VqTjSLSe6WYNzCMcSrmhNBPMpQ9RkqucDHGw1V1QxTPiHUAlapNGp5vhYcIJuDZD7DHF5oq0sStNF7ppR0P0T24RhyphenhyphenQDUcAOfuAVQXAOtRKv5YyE/s1600/8550674222_084a3234b8_m.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHQQOeG0kpPnUEXoqaI45kGeiigl1VqTjSLSe6WYNzCMcSrmhNBPMpQ9RkqucDHGw1V1QxTPiHUAlapNGp5vhYcIJuDZD7DHF5oq0sStNF7ppR0P0T24RhyphenhyphenQDUcAOfuAVQXAOtRKv5YyE/s1600/8550674222_084a3234b8_m.jpg" /></a></div>
<div>
<br /></div>
<div>
Today I added a tip/trick to code <a href="https://www.codeproject.com/Tips/1166338/appSettings-Gone-Wild" target="_blank">project</a> describing a technique used recently at one of our clients.</div>
<div>
<br /></div>
<div>
The technique involves add a new configuration section to the config file without having to go through the pain of creating your own class inherited from <span style="font-family: "segoe ui" , "lucida grande" , "verdana" , "arial" , "helvetica" , sans-serif; font-size: 13px;">System.Configuration.</span><span style="font-family: "consolas" , "courier" , monospace; font-size: 13px;">ConfigurationSection.</span></div>
<div>
<span style="font-family: "consolas" , "courier" , monospace; font-size: 13px;"><br /></span></div>
<div>
By using the <span style="font-family: "segoe ui" , "lucida grande" , "verdana" , "arial" , "helvetica" , sans-serif; font-size: 13px;">System.Configuration.</span><span style="font-family: "consolas" , "courier" , monospace; font-size: 13px;">NameValueSectionHandler</span> we added new sections to the configuration file which allowed us the same technique of using key value pairs though scoped to the newly defined section.<br />
<br />
Here's a Link:<br />
<a href="https://www.codeproject.com/Tips/1166338/appSettings-Gone-Wild" target="_blank">https://www.codeproject.com/Tips/1166338/appSettings-Gone-Wild</a><br />
<br /></div>
The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-73318961228267933162016-02-01T17:03:00.002-06:002016-02-01T17:03:54.719-06:00The most Complex Hello World program ever written and more HyperboleOK, I know. How is it that you can claim to make the most complex Hello World program ever written? Of course you could take my work add more to it and then you would have the most complex ever, but that's the beauty of Hyperbole!<br />
<br />
The real question is why would anyone make the the first program that most of you have written more complicated?<br />
<br />
Of course the answer is that the focus of this exercise is not to write a program which displays "Hello World" but rather one which illustrates the concepts of dependency injection (DI) and inversion of control (IOC) without the overhead of learning other concepts like MVC which depend on both.<br />
<br />
Hopefully after you have had a chance to step through the code, you will get a better understanding of what is being done and why. And afterwards you would feel more comfortable to include in your own projects.<br />
<br />
I have supplied a complete explanation of my work with supporting projects at the CodeProject site.<br />
<br />
To learn more, go to CodeProject at:<br />
<div style="background-color: white; border: 0px; margin: 0px; padding: 20px 0px 0px;">
<div style="color: #111111; font-family: 'segoe ui', arial, sans-serif; font-size: 14px;">
<a href="http://www.codeproject.com/Articles/1075597/Introduction-to-Dependency-Injection-and-Inversion" target="_blank">http://www.codeproject.com/Articles/1075597/Introduction-to-Dependency-Injection-and-Inversion</a></div>
<div style="color: #111111; font-family: 'segoe ui', arial, sans-serif; font-size: 14px;">
<br /></div>
<br /></div>
<div>
<span class="small-text" editid="ctl00_MC_SlugEdit" empty="My-article" emptyclass="highlight" style="border: 0px; font-size: 12px !important; height: 16px; margin: 0px; padding: 0px; width: 200px;" title="Click to edit"><br /></span></div>
The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-66731564658295737402015-11-19T15:53:00.001-06:002015-11-19T15:54:03.694-06:00October ArticlesThank you to all who voted for two of my articles which where listed in top ten on Codeproject.<br />
<br />
Doing Delegates Differently part 1 and Using HTMLAgility pack and CSS Selectors.The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-47778704427807973772015-10-31T13:53:00.004-05:002015-12-22T19:29:37.803-06:00Doing Delegates Differently<div style="background-color: white; color: #111111; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;">
This is an article about using delegates in a way in which you may not have thought of using them before. As I started to write this it became obvious that this would be a long article, so I am splitting in two. This first part is an overview of delegates and how to group functionality and cross cutting concerns with them. Part two will delve into using delegates to create a validator which can validate any type of object dynamically.</div>
<div style="background-color: white; color: #111111; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;">
<br /></div>
Part 1 - Cross cutting concerns<br />
<a href="http://www.codeproject.com/Articles/1045523/Doing-Delegates-Differently-Part" target="_blank">http://www.codeproject.com/Articles/1045523/Doing-Delegates-Differently-Part</a><br />
<br />
Part 2 - Dynamic Validator<br />
<a href="http://www.codeproject.com/Articles/1051860/Doing-Delegates-Differently-Part">http://www.codeproject.com/Articles/1051860/Doing-Delegates-Differently-Part</a>The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-19151836721233491212015-10-22T09:43:00.000-05:002015-10-22T09:43:00.744-05:00Earlier I wrote that I would include an article for actually getting the data from a web site using mechanisms such as WebClient, HttpWebClient etc.<br />
<br />
Someone beat me to the punch, go to this well written article in code project. He presents using ScrapySharp. Although I don't have much experience with this product, it looks worth exploring. The article shows how I would use Fidler to examine the requests going back and forth and what needs to be sent as parameters etc.<br />
<br />
http://www.codeproject.com/Articles/1041115/Webscraping-with-Csharp<br />
<br />
SteveThe Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-74912432521229765712015-10-10T14:48:00.001-05:002015-10-12T16:53:39.542-05:00Using HtmlAgility pack and CssSelectors<div class="MsoNormal">
To start, I don't claim to be an expert in XPath or Regular
Expressions but the following are some observations I have made while parsing
HTML documents for client projects.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In the following examples I am using HtmlAgility pack (HAP)
to load the HTML into a document object model (DOM) and parse into nodes. Additionaly, there are cases where I have had to parse the
document on elements which are not truly nodes such as comments.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In addition to observations about HAP in general, I’ll point out
extension methods provided by HAP.CSSSelectors package which allow for much easier selection.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Packages for the example will need to be imported using
NuGet. The package descriptions will be loaded in the project but you will need
to set NuGet package manager to restore the libraries.<o:p></o:p></div>
<div class="MsoNormal">
In the project I have included a really simple html file
with examples of issues I have needed to address in my projects. </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To test
without any modifications, you will need to copy the HTML file to the following
drive and directory – C:\testdata<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
HtmlAgility has a number of classes available to it including
classes and enums which represent various parts of the DOM, these classes
include HtmlAttribute, HtmlAttributeCollection, HtmlCommentNode and so on.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The first class we are going to examine is the HtmlDocument
class. This class has the methods to load and parse the document into their
respective parts.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To use, the following line needs to be implemented:<o:p></o:p></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">(Part1)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">HtmlAgilityPack.</span><span style="background: white; color: #2b91af; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">HtmlDocument</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> agpack
= </span><span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">new</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">
HtmlAgilityPack.</span><span style="background: white; color: #2b91af; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">HtmlDocument</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
The next method to call
is the method to load the document. You can load from either a string agpack.<span style="background: white; font-family: Consolas; font-size: 9.5pt;">LoadHtml</span><span style="font-family: Consolas; font-size: 9.5pt;">(Html string) or from a resource - <span style="background: white; mso-highlight: white;">agpack.Load(</span></span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">@"c:\testdata\testdat.htm"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Like a web browser, HAP is forgiving on the Html supplied.
You can query for errors but it will not break.<o:p></o:p></div>
<div class="MsoNormal">
The file include has a missing close on the second font tag
and a misplaced end tag. Works great in browser, does not throw an error in HAP
but can be checked for.<o:p></o:p></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">(Part 2)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">var</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> errors = agpack.ParseErrors;<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
ParseErrors will return a collection and a count of errors.
Interesting enough the closing font tab did not throw an error. But the
misplaced </tr> did.<o:p></o:p></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">Once the Document has been
loaded, the two main methods to use for searching are:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"> SelectNodes(string
XPath) from the Document Node<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt; text-indent: 0.5in;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">GetElementbyId(string Id)</span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt; text-indent: 0.5in;">
<span style="background-color: white; text-indent: 0.5in;"><br /></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt; text-indent: 0.5in;">
<span style="background-color: white; text-indent: 0.5in;">Since there can only be a single ID, getElementById will return a single
node and SelectNodes will bring back a collection of nodes because using XPath
you want to match one or more items.</span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
My client has an
application which will append several files together, delimiting each document
with a start and end comment. The following is how I handle splitting this
document back into its constituent parts. The file I have included has a
section which is delineated with comments, the comments are in the form - <span style="background: white; color: darkgreen; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;"><!-- Start Table: 1234 --> HTML Body <!-- End
Table --></span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"> were 1234 might represent some type of account
number that we need for processing. <o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">(Part 3)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">You could use the following to
get the comment:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">var</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> comment = agpack.DocumentNode.SelectNodes(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"//comment()[contains(., 'Start Table:')]"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">This says from the whole
document (“//”) select comments which contain from the current location (.) the
words Start Table.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">Since this is a comment, it has
no child nodes and the inner text is simply the text of the comment itself.
This is useful if what you want to do is parse the comment to determine a value
in the comment (account number in this case) but doesn’t really help when you
want the text between the comments. To accomplish this, I fall back to Regular
Expressions and grouping.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"><br /></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">(Part 4)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">var</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> html = </span><span style="background: white; color: #2b91af; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">Regex</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">.Match(agpack.DocumentNode.InnerHtml, </span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">@"<!-- Start Table: \d*
-->(?<one>.*)<!-- End Table -->"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">, </span><span style="background: white; color: #2b91af; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">RegexOptions</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">.Singleline).Groups[1];<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">Now, in the
html.Value we have the text between the two tags.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background-color: white;">Moving onto finding
elements in the DOM, the first example is finding the node using getElementById.
There are three tables, but only two have an ID assigned to them. One is ID=”abc”
the other is ID=”table3”</span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">Let’s start with
look</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">ing at table with id=”abc”<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">(Part
5)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">var</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> node = agpack.GetElementbyId(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"abc"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">This will return a
single node representing the table. The InnerHtml will contain all the text
between the <table></table> tags. It will also contain a collection
of nodes representing the DOM structure of the table.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">(Part 6)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">One approach to
getting the row nodes is to use Linq to discover them, such as:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">var</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> rownodes = node.ChildNodes.Where(w => w.OriginalName
== </span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"tr"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">This sort of works,
if you check the count you will see you have three rows. However there are
actually four rows, the first wrapped in a <thead></thead><o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">Another approach is
to use SelectNodes on the node to discover the tr elements.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">rownodes
= node.SelectNodes(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"tr"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">But this also fails
to find all the rows, just finding its immediate children.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">What about node.SelectNodes(</span><span style="background: white; color: #a31515; mso-bidi-font-family: Consolas; mso-highlight: white;">"/tr"</span><span style="background: white;">); This returns nothing. </span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">What
about node.SelectNodes(</span><span style="background: white; color: #a31515; mso-bidi-font-family: Consolas; mso-highlight: white;">"//tr"</span><span style="background: white;">); the good news is that it found the missing row along with all the
rows (12) in the document.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">rownodes =
node.SelectNodes(</span><span style="background: white; color: #a31515; mso-bidi-font-family: Consolas; mso-highlight: white;">"./tr"</span><span style="background: white;">); has the same effect as (“tr”)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">After a little
digging I found the following solution worked.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">rownodes =
node.SelectNodes(node.XPath + </span><span style="background: white; color: #a31515; mso-bidi-font-family: Consolas; mso-highlight: white;">"//tr"</span><span style="background: white;">); this returns all four, this was interesting to me. I think I had assumed
HAP would have been doing the SelectNodes from the current node and “//tr”
would have worked, alas “//” says to search from the root of the document. <o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">***** As I was
researching this article, I discovered another XPath option that works. *****<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">rownodes
= node.SelectNodes(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"descendant::tr"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background-color: white;">http://www.w3schools.com/xsl/xpath_axes.asp</span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">Similarly, we can
find all the td elements of the tr elements using the same procedures. Note
that for table 3 we bring back twelve td elements even though they are children
of tr and font and span elements.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">(Part 7)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">node
= </span><span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">null</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">node
= agpack.GetElementbyId(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"table3"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">var
tdnodes = node.SelectNodes(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"descendant::td"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">Let’s move onto HAP.CSSSelectors<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">This sits on top of HtmlAgility
pack and will in fact ensure that it is installed as part of the NuGet package.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"><br /></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">It allows you to select elements
using CSS selectors rather than XPath. For example:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"> (Part 8)<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">rownodes
= agpack.QuerySelectorAll(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"#abc tr"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;">In this case I did not need to
find from the node, simply selecting from whole document it returned the
expected 4 rows.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; color: blue; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">var</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;"> listTDNodes = agpack.QuerySelectorAll(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"#table3 td"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">Here is an example
of getting only the tds (three) in the second row.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;">listTDNodes
= agpack.QuerySelectorAll(</span><span style="background: white; color: #a31515; font-family: Consolas; font-size: 9.5pt; mso-highlight: white;">"#table3
tr:nth-child(2) td"</span><span style="background: white; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;"><br /></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background-color: white;">This returned 12
items. One thing to note. The QuerySelectorAll method returns as List<node>
rather than a collections of nodes. This is important to know if you plan to
mix and match.</span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;"><o:p><br /></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">In addition to selecting by id(#) you can select by class(.), much easier than looking for an attribute with class using XPath.</span><span style="background: white;"><o:p></o:p></span></div>
<div>
<span style="background-color: white;"><br /></span></div>
<div>
listTDNodes = agpack.QuerySelectorAll(".table");</div>
<div>
<span style="background-color: white;"> </span></div>
<div>
<span style="background-color: white;">Returns the first and third table with the class table.</span></div>
<div>
<span style="background-color: white;"><br /></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white;">In conclusion, the CssSelectors
extension is another useful tool to select elements easily without the need to
dig deep into XPath or iterate through collections. I know I will be looking
forward to implementing some of these findings into my own work<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="background: white; font-family: Consolas; font-size: 9.5pt;"> </span><span style="background: white; mso-bidi-font-family: Consolas; mso-highlight: white;"><o:p></o:p></span></div>
<div class="MsoNormal">
<o:p>The code is posted to: - <a href="http://www.codeproject.com/Articles/1038320/Using-HtmlAgility-pack-and-CssSelectors">http://www.codeproject.com/Articles/1038320/Using-HtmlAgility-pack-and-CssSelectors</a></o:p><br />
<o:p><br /></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0tag:blogger.com,1999:blog-3911040379536376723.post-40486257851381181072015-09-29T16:38:00.001-05:002015-12-22T19:21:37.380-06:00Codeproject submission<div style="background-color: white; color: #111111; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;">
What I really love about consulting is the variety of projects that come my way. Recently I was asked if we could create an application for a client which allowed them to fill in a PDF and store the data in a database. </div>
<div style="background-color: white; color: #111111; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;">
<br /></div>
<div style="background-color: white; color: #111111; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;">
I looked around and found several different libraries for dealing with PDF files, I do not mean to say I did an exhaustive search or trial. Almost all these libraries touted their ease of use in creating PDF documents in code, many offered capabilities of reading the documents but almost all of them failed to load a document created with Adobe Acrobat, opening only older unencrypted PDF files.</div>
<div style="background-color: white; color: #111111; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;">
The one exception I found was iTextSharp, a library from <a href="http://itextpdf.com/" style="border: 0px; color: purple; margin: 0px; padding: 0px; text-decoration: none;">http://itextpdf.com/</a> which offers both a commercial version with iText support provided and an open source version available with a copy left AGPL license.</div>
<br />
Read more at <a href="http://www.codeproject.com/Articles/1032782/Reading-Acrofields-from-PDF-files" target="_blank">http://www.codeproject.com/Articles/1032782/Reading-Acrofields-from-PDF-files</a><br />
<br />
This project explains how to read PDF form fields from a PDF document<br />
<br />
<a href="http://www.polarissolutions.com/" target="_blank">www.PolarisSolutions.com</a>The Old man and the c#http://www.blogger.com/profile/17009818581407959396noreply@blogger.com0