controls | Dmytro Shteflyuk's Home https://kpumuk.info In my blog I'll try to describe about interesting technologies, my discovery in IT and some useful things about programming. Tue, 08 Sep 2015 00:00:29 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 Generating content for the Facebook’s setFBML method in ASP.NET https://kpumuk.info/asp-net/generating-content-for-the-facebooks-setfbml-method-in-aspnet/ https://kpumuk.info/asp-net/generating-content-for-the-facebooks-setfbml-method-in-aspnet/#comments Thu, 13 Sep 2007 22:43:30 +0000 http://kpumuk.info/facebook/generating-content-for-the-facebooks-setfbml-method-in-aspnet/ In my current project we decided to build a Facebook application. This is really great platform with many interesting ideas inside, which usually means that you will spend a much time to make your application working as expected. Today I wanna talk about user profiles. Any Facebook application could add some action links, which will […]

The post Generating content for the Facebook’s setFBML method in ASP.NET first appeared on Dmytro Shteflyuk's Home.]]>
In my current project we decided to build a Facebook application. This is really great platform with many interesting ideas inside, which usually means that you will spend a much time to make your application working as expected. Today I wanna talk about user profiles. Any Facebook application could add some action links, which will be displayed under the user’s picture, and some content for wide or narrow column. Of course, you can use FBML syntax, especially fb:if-... tags set to choose which content to show on specific profiles to concrete users.

For the beginning, I’ll post a few key points about user profiles. If you want to add some content to the profile of specific user, you should call profile.setFBML routine. For users that you have not called profile.setFBML for, the actions are read from the content in “Default FBML” section of your application settings. For the most part, this will apply to any user who has not added your application. What is “Default FBML” itself? If you have added application, you will see “Default FBML” on all profiles of users that you have not called profile.setFBML for, and it does not matter, if they have added your application or not (good place to put “Invite” link). The same behavior you would see, if you would call profile.setFBML for all users (and if you are crazy enough.)

Please note, “Default FBML” is cached indefinitely, so wait some time to get your content on profiles. Another thing — you can add only action links to user profiles, that have not added your application, and only your application user will see them. This is most important part of the documentation, and you should completely understand it. More detailed description could be found in the documentation for fb:profile-action tag and profile.setFBML routine.

FBML-content for user profiles

There are 4 profile-specific FBML tags exist:

  • fb:profile-action is used to add action links under the user picture.
  • fb:subtitle will be shown right under the title of the your application box.
  • fb:wide specifies content to be shown when your application box placed in wide column.
  • fb:narrow specifies content to be shown when your application box placed in narrow column.

Easy, right? So let’s examine these tags more precisely.

As I said early, fb:profile-action is used to place action links on the user’s profile. Usually you will add one link:

1
2
3
<fb:profile-action url="http://www.mysite.com/action/">
    Perform Action
</fb:profile-action>

What about following scenario: I want to see the link “View my products” when I’m looking my own profile, “View Taisia’s products”, if I’m looking profile of my wife Taisia (and she has added application too), and “Invite Roman to MyApp” when I’m looking profile of my friend Roman, and he has not added application. Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<fb:if-is-own-profile>
    <fb:profile-action url="http://apps.facebook.com/myapp/Products.aspx">View my products</fb:profile-action>
    <fb:else>
        <fb:if-is-app-user uid="profileowner">
            <fb:profile-action url="http://apps.facebook.com/myapp/Products.aspx">
                View <fb:name uid="profileowner" firstnameonly="true" possessive="true" /> products
            </fb:profile-action>
            <fb:else>
                <fb:profile-action url="http://apps.facebook.com/myapp/Invite.aspx">
                    Invite <fb:name uid="profileowner" firstnameonly="true" /> to MyApp
                </fb:profile-action>
            </fb:else>
        </fb:if-is-app-user>
    </fb:else>
</fb:if-is-own-profile>

Please note: this example is similar to example on the fb:profile-action documentation page, but has one big difference — it’s working: fb:if-is-app-user uid="profileowner" (in documentation example uid takes default value “loggedinuser”, but we need uid of the profile owner.)

This code will work completely only if you will put it into the “Default FBML” section of your application settings, or if you will call profile.setFBML for user, that has not added your application (but this is madness, what I have talked about early): I have specified action “Invite someone to MyApp” in section, which will be shown only on profile of user that has not added application.

BTW, do not forget to remove all line breaks before updating “Default FBML”, because Facebook replaces them with <br/>.

Another interesting thing you could see from working example: Facebook adds parameter id for all links in profile-action, and it equals to owner’s of the profile ID. In my previous example, if I will navigate to Roman’s profile, I will see hyperlink with URL http://apps.facebook.com/myapp/Invite.aspx?id=603839739. Great!

This example is simple, so let’s move ahead. We have two columns on the profile: left (narrow) and right (wide). You can specify in application settings which one will be default. To put content in wide column, use fb:wide, in narrow column — fb:narrow. Quite clear, right?

One more interesting issue. You can specify as many fb:wide and fb:narrow tags as you wish. All content, specified in fb:wide tags will be shown if you application box placed in wide column, all content from fb:narrow tags — when application box placed in narrow column. You can add content outside one of these tags, and it will be shown in both cases — when application in wide or narrow column. If no content specified for one of column, Facebook will show “No content to display.” text.

When to generate profile FBML?

Your application box on the user’s profile should reflect latest changes related to him. Facebook does not know when to update profile information, so you need to do it by yourself (anyway, only you as application developer know when something changes). So, usually you would call profile.setFBML after some changes in your application (for example, user or his friends added some data), depending on which information you are rendering on the profile. Sometimes it’s a good idea to set default data after user has added your application.

Creating profile FBML content with ASP.NET

For profile content generating I propose to use UserControls:

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
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ProfileFBML.ascx.cs" Inherits="ProfileFBML" %>
<fb:if-is-own-profile>
    <fb:profile-action url="http://apps.facebook.com/myapp/Products.aspx">View my products</fb:profile-action>
    <fb:else>
        <fb:if-is-app-user uid="profileowner">
            <fb:profile-action url="http://apps.facebook.com/myapp/Products.aspx">
                View <fb:name uid="profileowner" firstnameonly="true" possessive="true" /> products
            </fb:profile-action>
            <fb:else>
                <fb:profile-action url="http://apps.facebook.com/myapp/Invite.aspx">
                    Invite <fb:name uid="profileowner" firstnameonly="true" /> to MyApp
                </fb:profile-action>
            </fb:else>
        </fb:if-is-app-user>
    </fb:else>
</fb:if-is-own-profile>

<asp:XmlDataSource runat="server" ID="xdsCountries"
    DataFile="~/App_Data/CountryCodeList.xml" />

<fb:wide>
    <asp:Repeater runat="server" DataSourceID="xdsCountries" ID="rptCountriesWide">
        <ItemTemplate>
            <div>
                <%# XPath("CountryCoded") %> - <%# XPath("CountryName") %>
            </div>
        </ItemTemplate>
    </asp:Repeater>
</fb:wide>

<fb:narrow>
    <asp:Repeater runat="server" DataSourceID="xdsCountries" ID="rptCountriesNarrow">
        <ItemTemplate>
            <div>
                <%# XPath("CountryCoded") %>
            </div>
        </ItemTemplate>
    </asp:Repeater>
</fb:narrow>

How to get string to put it into the profile.setFBML? Here is the code:

1
2
3
4
5
6
7
8
StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
HtmlTextWriter hw = new HtmlTextWriter(tw);
Control c = LoadControl("~/ProfileFBML.ascx");
Controls.Add(c);
c.RenderControl(hw);
Controls.Remove(c);
string fbml = sb.ToString();

I’ve added control to the Controls collection to get events fired. If you would call this snippet from OnLoad, only OnInit would be fired in ProfileFBML, so do not forget to call DataBind method to force data binding from OnInit in this case.

The post Generating content for the Facebook’s setFBML method in ASP.NET first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/asp-net/generating-content-for-the-facebooks-setfbml-method-in-aspnet/feed/ 18
GridView with custom Digg-like pagination https://kpumuk.info/asp-net/gridview-with-custom-digg-like-pagination/ https://kpumuk.info/asp-net/gridview-with-custom-digg-like-pagination/#comments Mon, 27 Aug 2007 05:42:03 +0000 http://kpumuk.info/asp-net/gridview-with-custom-digg-like-pager/ GridView is a great highly customizable ASP.NET control. Today I want to show, how to create derived control, which allows to add Digg-style pagination to your application. First, we will create derived control and add property UseCustomPager, which will define whether or not to use Digg-style pagination: 123456789101112131415161718using System; using System.Globalization; using System.Reflection; using System.Web.UI; […]

The post GridView with custom Digg-like pagination first appeared on Dmytro Shteflyuk's Home.]]>
GridView is a great highly customizable ASP.NET control. Today I want to show, how to create derived control, which allows to add Digg-style pagination to your application.

First, we will create derived control and add property UseCustomPager, which will define whether or not to use Digg-style pagination:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Globalization;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace App_Code
{
    public class GridViewWithPager : GridView
    {
        public bool UseCustomPager
        {
            get { return (bool?) ViewState["UseCustomPager"] ?? false; }
            set { ViewState["UseCustomPager"] = value; }
        }
    }
}

GridView has virtual method InitializePager which could be overridden to create our custom pager:

1
2
3
4
5
6
7
protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
    if (UseCustomPager)
        CreateCustomPager(row, columnSpan, pagedDataSource);
    else
        base.InitializePager(row, columnSpan, pagedDataSource);
}

Now let’s create our custom pager:

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
protected virtual void CreateCustomPager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
    int pageCount = pagedDataSource.PageCount;
    int pageIndex = pagedDataSource.CurrentPageIndex + 1;
    int pageButtonCount = PagerSettings.PageButtonCount;

    TableCell cell = new TableCell();
    row.Cells.Add(cell);
    if (columnSpan > 1) cell.ColumnSpan = columnSpan;

    if (pageCount > 1)
    {
        HtmlGenericControl pager = new HtmlGenericControl("div");
        pager.Attributes["class"] = "pagination";
        cell.Controls.Add(pager);

        int min = pageIndex - pageButtonCount;
        int max = pageIndex + pageButtonCount;

        if (max > pageCount)
            min -= max - pageCount;
        else if (min < 1)
            max += 1 - min;

        // Create "previous" button
        Control page = pageIndex > 1
                        ? BuildLinkButton(pageIndex - 2, PagerSettings.PreviousPageText, "Page", "Prev")
                        : BuildSpan(PagerSettings.PreviousPageText, "disabled");
        pager.Controls.Add(page);

        // Create page buttons
        bool needDiv = false;
        for (int i = 1; i <= pageCount; i++)
        {
            if (i <= 2 || i > pageCount - 2 || (min <= i && i <= max))
            {
                string text = i.ToString(NumberFormatInfo.InvariantInfo);
                page = i == pageIndex
                        ? BuildSpan(text, "current")
                        : BuildLinkButton(i - 1, text, "Page", text);
                pager.Controls.Add(page);
                needDiv = true;
            }
            else if (needDiv)
            {
                page = BuildSpan("&hellip;", null);
                pager.Controls.Add(page);
                needDiv = false;
            }
        }

        // Create "next" button
        page = pageIndex < pageCount
                ? BuildLinkButton(pageIndex, PagerSettings.NextPageText, "Page", "Next")
                : BuildSpan(PagerSettings.NextPageText, "disabled");
        pager.Controls.Add(page);
    }
}

private Control BuildLinkButton(int pageIndex, string text, string commandName, string commandArgument)
{
    PagerLinkButton link = new PagerLinkButton(this);
    link.Text = text;
    link.EnableCallback(ParentBuildCallbackArgument(pageIndex));
    link.CommandName = commandName;
    link.CommandArgument = commandArgument;
    return link;
}

private Control BuildSpan(string text, string cssClass)
{
    HtmlGenericControl span = new HtmlGenericControl("span");
    if (!String.IsNullOrEmpty(cssClass)) span.Attributes["class"] = cssClass;
    span.InnerHtml = text;
    return span;
}

Several pager settings used in this code:

  • PagerSettings.PreviousPageText — text to be shown on the “Previous” button.
  • PagerSettings.NextPageText — text to be shown on the “Next” button.
  • PagerSettings.PageButtonCount — how many pages to show before and after the current page.

You could see, that in BuildLinkButton method I have used custom control PagerLinkButton. This is just descendant of the LinkButton control which simplifies usage inside our GridViewWithPager:

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
using System;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace App_Code
{
    public class PagerLinkButton : LinkButton
    {
        public PagerLinkButton(IPostBackContainer container)
        {
            _container = container;
        }

        public void EnableCallback(string argument)
        {
            _enableCallback = true;
            _callbackArgument = argument;
        }

        public override bool CausesValidation
        {
            get { return false; }
            set { throw new ApplicationException("Cannot set validation on pager buttons"); }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            SetCallbackProperties();
            base.Render(writer);
        }

        private void SetCallbackProperties()
        {
            if (_enableCallback)
            {
                ICallbackContainer container = _container as ICallbackContainer;
                if (container != null)
                {
                    string callbackScript = container.GetCallbackScript(this, _callbackArgument);
                    if (!string.IsNullOrEmpty(callbackScript)) OnClientClick = callbackScript;
                }
            }
        }

        #region Private fields

        private readonly IPostBackContainer _container;
        private bool _enableCallback;
        private string _callbackArgument;

        #endregion
    }
}

Our control is almost done. All we need is to define method ParentBuildCallbackArgument. As you could see from GridView sources, this method is used for serializing page index, sort direction and sort expression, but for some reason, it has been defined as internal. I don’t like hacks, but in this case I think that I can cheat a little:

1
2
3
4
5
6
7
private string ParentBuildCallbackArgument(int pageIndex)
{
    MethodInfo m =
        typeof (GridView).GetMethod("BuildCallbackArgument", BindingFlags.NonPublic | BindingFlags.Instance, null,
                                    new Type[] {typeof (int)}, null);
    return (string) m.Invoke(this, new object[] {pageIndex});
}

BTW, as you could see, I have not added any comments to methods. It is done specially, because I don’t want to create custom controls library, just sharing my experience :-)

And now I’ll show you example of usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<asp:XmlDataSource runat="server" ID="xdsCountries"
    DataFile="~/App_Data/CountryCodeList.xml" />

<ac:GridViewWithPager runat="server" UseCustomPager="true" AllowPaging="true"
    DataSourceID="xdsCountries" PageSize="10" AutoGenerateColumns="false">
    <PagerSettings PreviousPageText="&laquo; previous"
        NextPageText="next &raquo;" PageButtonCount="3" />
    <Columns>
        <asp:TemplateField HeaderText="Code">
            <ItemTemplate><%# XPath("CountryCoded") %></ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Name">
            <ItemTemplate><%# XPath("CountryName") %></ItemTemplate>
        </asp:TemplateField>
    </Columns>
</ac:GridViewWithPager>

And a screenshot:

Digg-style pagination

Full source code could be downloaded here.

The post GridView with custom Digg-like pagination first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/asp-net/gridview-with-custom-digg-like-pagination/feed/ 13