Thursday, May 26, 2016

Part of Cookie is Invalid (CookieContainer)

Given the code below when scraping a website, you might encounter an error which is stated in the title of the post.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
WebRequest request = WebRequest.Create("http://your_url_here");
WebResponse webResponse = request.GetResponse();          
 
if (webResponse.Headers["Set-Cookie"] != null)
{
    Cookie m_ccCookies = new Cookie();
    CookieContainer ccContainer = new CookieContainer();
    ccContainer = new CookieContainer();
    ccContainer.SetCookies(webResponse.ResponseUri, webResponse.Headers["Set-Cookie"]);
    ccContainer.Add(ccContainer.GetCookies(webResponse.ResponseUri));
}   

The issue could be that the url domain cookie value which is passed to SetCookies method have unrecognized characters that needs to be encoded. So, the workaround is to encode the cookie value first and then assign the appropriate encoding before saving it to the cookie container object.
1
2
ccContainer.SetCookies(webResponse.ResponseUri, 
        HttpUtility.UrlEncode(webResponse.Headers["Set-Cookie"], Encoding.GetEncoding("iso-8859-1"))); 

Friday, May 20, 2016

Get Total Hours and Minutes from total minutes using TimeSpan class

Given total number of minutes as input and you need to extract the total number of hours and remaining minutes out from the input, you need to use the TimeSpan class specifically FromMinutes() method.

C# Code
1
2
3
4
TimeSpan span = TimeSpan.FromMinutes(1506);
var hours = (int) span.TotalHours;
var minutes = span.Minutes;
Console.WriteLine(hours + ":" + minutes.ToString("D2"));

VB.NET Code
1
2
3
4
Dim span As TimeSpan = TimeSpan.FromMinutes(1506)
Dim hours = CInt(span.TotalHours)
Dim minutes = span.Minutes
Console.WriteLine(hours + ":" + minutes.ToString("D2"))

Wednesday, May 18, 2016

ASP.NET MVC ListBoxFor() with optgroup Tag support

Here's a simple ListBoxFor helper that supports optgroup tag. See versions for C# and VB.NET.
C# Code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public static class HtmlExtensions
{
    public static IHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TProperty>> expression, Dictionary<string, IEnumerable<SelectListItem>> selectList, 
        object htmlAttributes = null)
    {
        var select = new TagBuilder("select");
        select.Attributes.Add("name", ExpressionHelper.GetExpressionText(expression));
 
        if (htmlAttributes != null)
        {
            RouteValueDictionary routeValues = new RouteValueDictionary(htmlAttributes);
            if(!routeValues.ContainsKey(("size").ToLower()))
            {
                select.Attributes.Add("size", selectList.Sum(x=> x.Value.Count()).ToString());
            }
 
            foreach (var item in routeValues)
            {
                select.Attributes.Add(item.Key, item.Value.ToString());
            }
        }
        else
            select.Attributes.Add("size", selectList.Sum(x => x.Value.Count()).ToString());
 
        var optgroups = new StringBuilder();
 
        foreach (var kvp in selectList)
        {
            var optgroup = new TagBuilder("optgroup");
            optgroup.Attributes.Add("label", kvp.Key);
 
            var options = new StringBuilder();
 
            foreach (var item in kvp.Value)
            {
                var option = new TagBuilder("option");
 
                option.Attributes.Add("value", item.Value);
                option.SetInnerText(item.Text);
 
                if (item.Selected)
                {
                    option.Attributes.Add("selected", "selected");
                }
 
                options.Append(option.ToString(TagRenderMode.Normal));
            }
 
            optgroup.InnerHtml = options.ToString();
 
            optgroups.Append(optgroup.ToString(TagRenderMode.Normal));
        }
 
        select.InnerHtml = optgroups.ToString();
 
        return MvcHtmlString.Create(select.ToString(TagRenderMode.Normal));
    }
}

VB.NET Code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Public Module HtmlExtensions
 
    <Extension()>
    Public Function ListBoxFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), _
        selectList As Dictionary(Of String, IEnumerable(Of SelectListItem)), Optional htmlAttributes As Object = Nothing) As IHtmlString
 
        Dim selectTag = New TagBuilder("select")
        selectTag.Attributes.Add("name", ExpressionHelper.GetExpressionText(expression))
 
        If htmlAttributes IsNot Nothing Then
            Dim routeValues As New RouteValueDictionary(htmlAttributes)
            If Not routeValues.ContainsKey(("size").ToLower()) Then
                selectTag.Attributes.Add("size", selectList.Sum(Function(x) x.Value.Count()).ToString())
            End If
 
            For Each item In routeValues
                selectTag.Attributes.Add(item.Key, item.Value.ToString())
            Next
        Else
            selectTag.Attributes.Add("size", selectList.Sum(Function(x) x.Value.Count()).ToString())
        End If
 
        Dim optgroups = New StringBuilder()
 
        For Each kvp In selectList
            Dim optgroup = New TagBuilder("optgroup")
            optgroup.Attributes.Add("label", kvp.Key)
 
            Dim options = New StringBuilder()
 
            For Each item In kvp.Value
                Dim optionTag = New TagBuilder("option")
 
                optionTag.Attributes.Add("value", item.Value)
                optionTag.SetInnerText(item.Text)
 
                If item.Selected Then
                    optionTag.Attributes.Add("selected", "selected")
                End If
 
                options.Append(optionTag.ToString(TagRenderMode.Normal))
            Next
 
            optgroup.InnerHtml = options.ToString()
 
            optgroups.Append(optgroup.ToString(TagRenderMode.Normal))
        Next
 
        selectTag.InnerHtml = optgroups.ToString()
 
        Return MvcHtmlString.Create(selectTag.ToString(TagRenderMode.Normal))
 
    End Function
End Module

Screenshot Detailed usage of the custom helper: ASP.NET MVC ListBoxFor() with optgroup Tag support
Source codes:
C# Version
VB.NET Version

Saturday, May 14, 2016

Failed to load resource: the server responded with a status of 404 (WebResource.axd)

I downloaded a GridView custom control with cool features on search and filtering that was developed on Visual Studio 2010 and ASP.NET 4.0. After playing around with the control, I decided to migrate the files to Visual Studio 2012 ASP.NET 4.5. Upon running the ASP.NET Project, the resource to be embedded on the GridView control in which case a JavaScript file was not recognized/found. And thus, returned a 404 status. After doing some research, I came up with the solution below.

Steps to fix the issue:
1. Set Build Action of the Resource/JavaScript file to Embedded Resource. In my project, the file to be embedded is EnhancedGridView.js
2. Add WebResourceAttribute to the GridView custom control to embedded a JavaScript file as Resource in an assembly. Make sure that the namespace of the file is correct.
1
2
3
4
5
[assembly: WebResource("GridViewFilter.EnhancedGridView.js", "text/javascript")]
public partial class EnhancedGridView : GridView
{
   //.....
}
3. In the control's PreRender event, I updated the namespace of where the JavaScript file is located. In my app, it's GridViewFilter which is the Root namespace.
1
2
3
4
5
6
7
protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    string resourceName = "GridViewFilter.EnhancedGridView.js";
    ClientScriptManager cs = this.Page.ClientScript;
    cs.RegisterClientScriptResource(typeof(CustomGridView), resourceName);
}

References:
Walkthrough: Embedding a JavaScript File as a Resource in an Assembly
WebResourceAttribute Class

Tuesday, May 10, 2016

Remove formatting of last selected text in a WPF RichTextBox control

Here's how to remove the formatting of last selected text in a WPF RichTextBox control from it's SelectionChanged event.
C# Code
1
2
3
4
5
6
private void rtbEditor_SelectionChanged(object sender, RoutedEventArgs e)
{
    //Clear previous selections.
    TextRange textRange = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd);
    textRange.ClearAllProperties();
}

Output

Saturday, May 7, 2016

Create XmlElement using XmlSerializer in .NET

Given the XML data below, the product properties are generated below the products node. What I want is to structure the product properties inside an element Product while serializing.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?xml version="1.0" encoding="utf-8"?>
<Products xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <Code>12345</Code>
    <Name>Samsung Galaxy</Name>
    <Model>Galaxy</Model>
    <Manufacturer>Samsung Ltd</Manufacturer>
    <OperatingSystem>Jelly Bean</OperatingSystem>
    <Distributor>Junrex</Distributor>
    <Version>7.0</Version>
</Products>

To achieve that, I have modified the model classes by separating the properties into another class and in the Products class I created a property of type List with XML Attribute Product.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Products
{
    [XmlElement("Product")]
    public List<ProductProperties> MobileProperties;
}
 
public class ProductProperties
{
    public string Code;
    public string Name;
    public string Model;
    public string Manufacturer;
    public string OperatingSystem;
    public string Distributor;
    public string Version;
}

Here's the code to generate the XML file.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Products product = new Products();
product.MobileProperties = new List<ProductProperties>();
product.MobileProperties.Add(new ProductProperties()
{ 
     Code = "12345",
     Distributor = "Junrex",
     Manufacturer = "Samsung Ltd",
     Model = "Galaxy",
     Name = "Samsung Galaxy",
     OperatingSystem = "Jelly Bean",
     Version = "7.0"
});
 
XmlSerializer myserial = new XmlSerializer(typeof(Products));
StreamWriter swriter = new StreamWriter("ProductData.xml");
myserial.Serialize(swriter, product);
swriter.Close();
StreamReader reader = new StreamReader("ProductData.xml");
RichTextBox1.Text = reader.ReadToEnd();
reader.Close();

As you can see from the output below, the properties are now inside the Product node.

Wednesday, May 4, 2016

Parse html table using HTML Agility Pack

Below is a simple code to parse a table using HTML Agility Pack. Make sure to reference the HTML Agility Pack namespace in your program.
1
using HtmlAgilityPack;

Parse Table
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
HtmlWeb web = new HtmlWeb();
HtmlDocument doc = web.Load("http://your_sample_url");
 
// Get all tables in the document
HtmlNodeCollection tables = doc.DocumentNode.SelectNodes("//table");
 
// Iterate all rows in the first table
HtmlNodeCollection rows = tables[0].SelectNodes("tr");
for (int i = 0; i <= rows.Count - 1; i++)
{
    // Iterate all columns in this row
    HtmlNodeCollection cols = rows[i].SelectNodes("td");
    if (cols != null)
    {
        for (int j = 0; j <= cols.Count - 1; j++)
        {
            // Get the value of the column and print it
            string value = cols[j].InnerText;
            Console.WriteLine(value);
        }
    }
}

Tuesday, May 3, 2016

Removing hyperlink(LinkButton) in a GridViewColumn based from a condition or value

There was a question raised from the forum on how to remove a hyperlink control in a GridViewColumn if a given condition is met. In the example below, the hyperlink(LinkButton) is disabled if Units In Stock is zero (0).
A solution is to disable the LinkButton in the RowDataBoundEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
protected void gvUnitSummary_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        if (Convert.ToInt32(e.Row.Cells[3].Text) == 0)
        {
            LinkButton lnkUnitID = (LinkButton)e.Row.FindControl("lnkProducts");
            lnkUnitID.Enabled = false;
        }
    }
}

Another solution is to change the column from BoundField to TemplateField. The TemplateField contains a LinkButton and a Label control. Set the Visibility property of the controls accordingly such as LinkButton will be shown if Units In Stock is greater than 0. Otherwise, show the Label control instead.
1
2
3
4
5
6
7
8
<asp:TemplateField HeaderText="Product ID">
    <ItemTemplate>
        <asp:LinkButton ID="lnkProducts" runat="server" CausesValidation="False" 
            Text='<%#Eval("ProductID")%>' Visible='<%# Convert.ToInt32(Eval("UnitsInStock").ToString()) > 0 %>' />
        <asp:Label ID="Label1" runat="server" Text='<%# Eval("ProductID")%>' 
            Visible='<%# Convert.ToInt32(Eval("UnitsInStock").ToString()) <= 0 %>' />
    </ItemTemplate>
</asp:TemplateField>