RSS

Tag Archives: jquery

Advance paging in SharePoint 2010 using Client Object Model and jQuery


In this tutorial I will show you new way of creating custom paging that work with SharePoint 2010 or SharePoint 2013 by using Cleint Object Model and jQuery.

Demo

Description

This demo is using custom paging approach but I’m injecting data on same page instead of navigating between pages by using jQuery.

Note: This code should work not only on lists but it can work with any different data source like document library ,Image Library and so on

Advantages

The are many advantages of this Advance Custom paging as follow

  • Work great with large lists because you do not need to navigate between pages.
  • It’s javascript which means is cleint side ,so need for post back each unlike standard custom paging.
  • Clean HTML tags because I’m using Table- less table by using DIV tag

Prerequisites

To run this code properly you need the following

  • Create Custom List and name it “Products”
  • Create New Cloumn in Products list and name it Company,so you have now two column Title and Company
  • Try to fill Products list with some data
  • How it work

    The idea is sample ,you just need to work with SharePoint Client Object Model but the most important thing to note is ListItemCollectionPosition and row limit as well.

     <script>
    var listItems;
    var query;
    var targetList;
    var clientContext;
    
    function LoadData() {
    
    $('#divPostsLoader').html('<img src="../../../_layouts/Images/AdvancePaging/ajax-loader.gif">');
    clientContext = new SP.ClientContext();
    targetList = clientContext.get_web().get_lists().getByTitle('Products');
    query = new SP.CamlQuery();
    var RowCount = 3;
    //Specifying the RowLimit will determine how many items will be fetched in one call to the server.
    query.set_viewXml("<View><RowLimit>"+RowCount+"</RowLimit></View>");
    listItems = targetList.getItems(query);
    clientContext.load(listItems);
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
    }
    
    function onQuerySucceeded() {
    
    var title, company;
    var listEnumerator = listItems.getEnumerator();
    //Create Header
    $("#Result").append("<div class='div-table-row'><div class='div-table-col'>Title</div><div class='div-table-col'>Name</div>");
    $("#divPostsLoader").empty();
    while (listEnumerator.moveNext()) {
    title = listEnumerator.get_current().get_item("Title");
    company = listEnumerator.get_current().get_item("Company");
    //Create tableless using DIV
    $('#Result').append("<div class='div-table-row'>");
    $('#Result').append("<div class='div-table-col'>" + title + "</div><div class='div-table-col'>" + company + "</div>");
    $('#Result').append("</div>");
    }
    }
    
    function onQueryFailed(sender, args) {
    alert('Request failed. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
    }
    
    //Call the function after the sp.js is loaded.
    ExecuteOrDelayUntilScriptLoaded(LoadData, "sp.js");
    function loadMoreData() {
    $('#divPostsLoader').html('<img src="../../../_layouts/Images/AdvancePaging/ajax-loader.gif">');
    
    //Gets the id of the last element from the returned collection along with the query.
    var position = listItems.get_listItemCollectionPosition();
    
    if (position != null) {
    $("#LoadBtn").attr("disabled", "disabled");
    query.set_listItemCollectionPosition(position);
    listItems = targetList.getItems(query);
    clientContext.load(listItems);
    //Call the other function to load  data from list
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onLoadQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
    }
    else {
    $('#divPostsLoader').empty();
    $("#LoadBtn").attr("value", "No more data found");
    $("#LoadBtn").attr("disabled", "disabled");
    }
    }
    function onLoadQuerySucceeded() {
    var title, company;
    var listEnumerator = listItems.getEnumerator();
    while (listEnumerator.moveNext()) {
    title = listEnumerator.get_current().get_item("Title");
    company = listEnumerator.get_current().get_item("Company");
    $("#divPostsLoader").empty();
    //Create tableless using DIV
    $('#Result').append("<div class='div-table-row'>");
    $('#Result').append("<div class='div-table-col'>" + title + "</div><div class='div-table-col'>" + company + "</div>");
    $('#Result').append("</div>");
    }
    $("#LoadBtn").removeAttr("disabled");
    
    }
    
    </script>
    

    as you can see you need to specify the row list (3,4,..etc) then loading data.After that when you click the button it will return next rows and so on until position equal null.

    Source Code

    The Source code of this article can be found on the following link

    http://code.msdn.microsoft.com/Advance-paging-in-81934f2b

 
1 Comment

Posted by on September 29, 2012 in jquery, SharePoint

 

Tags: , , , , , , , ,

Infinite Scroll images Like Bing and Google


Introduction

One of the must annoying thing when working with large data is how to loading this data to your page?

The common solution is paging but paging itself will not help too much you can end with hundred or thousands of page numbers.So new solution now is on the surface and it’s called “Infinite Scroll”.Infinite Scroll allow you to load chunk of data when you scroll down of the page and inject it inside the page, it will load data each time you scrolling down on the page.

Note:Before you start you can download code from here ( Please do not forget to rate it)

Infinite Scroll images Like Bing and Google

Demo

(Click on the image to see the result)

IScroll

Description

As I told you in the introduction Infinite Scroll is becoming more and more popular it’s in everywhere starting with Bing,Google,Facebook,Twitter,Linkedin.etc.

The idea of infinite scrolling is so simple and it can be summarized in the following diagram which is part of Scott Hanselmen Article

Infinite Scroll WebSites via AutoPagerize – Hacky, but the beginning of something cool

My Sample will show you how to Display a list of images like Bing and Google but this is not the only thing,you can take the advantage of idea behind infinite scrolling and implement the same concept everywhere.

The following code snippet will be called when you scroll to the last of the page

[WebMethod]
public static string LoadImages(int Skip, int Take)
{
System.Threading.Thread.Sleep(2000);
StringBuilder GetImages = new StringBuilder();
string Imagespath = HttpContext.Current.Server.MapPath("~/Images/");
string SitePath = HttpContext.Current.Server.MapPath("~");
var Files = (from file in Directory.GetFiles(Imagespath) select new { image = file.Replace(SitePath, "") }).Skip(Skip).Take(Take);
foreach (var file in Files)
{
var imageSrc = file.image.Replace("\\","/").Substring(1); //Remove First '/' from image path
GetImages.AppendFormat("<a>");
GetImages.AppendFormat("<li>");
GetImages.AppendFormat(string.Format("<img src='{0}'>", imageSrc));
GetImages.AppendFormat("</li>");
GetImages.AppendFormat("</a>");

}
return GetImages.ToString();
}

VB.Net part

<WebMethod()> _
Public Shared Function LoadImages(Skip As Integer, Take As Integer) As String
System.Threading.Thread.Sleep(2000)
Dim GetImages As New StringBuilder()
Dim Imagespath As String = HttpContext.Current.Server.MapPath("~/Images/")
Dim SitePath As String = HttpContext.Current.Server.MapPath("~")
Dim Files = (From file In Directory.GetFiles(Imagespath) Select New With { _
Key .image = file.Replace(SitePath, "") _
}).Skip(Skip).Take(Take)
For Each file As Object In Files

Dim imageSrc = file.image.Replace("\", "/").Substring(1) 'Remove First '/' from image path
GetImages.AppendFormat("<a>")
GetImages.AppendFormat("<li>")
GetImages.AppendFormat(String.Format("<img src='{0}'/>", imageSrc))
GetImages.AppendFormat("</li>")
GetImages.AppendFormat("</a>")
Next
Return GetImages.ToString()
End Function

JavaScript part

$(document).ready(function () {
var Skip = 49; //Number of skipped row
var Take = 14; //
function Load(Skip, Take) {
$('#divPostsLoader').html('&lt;img src="ProgressBar/ajax-loader.gif"&gt;');

//send a query to server side to present new content
$.ajax({
type: "POST",
url: "Grid.aspx/LoadImages",
data: "{ Skip:" + Skip + ", Take:" + Take + " }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {

if (data != "") {
$('.thumb').append(data.d);
}
$('#divPostsLoader').empty();
}

})
};
//Larger thumbnail preview

//When scroll down, the scroller is at the bottom and fire the Load ()function
$(window).scroll(function () {

if ($(window).scrollTop() == $(document).height() - $(window).height()) {
Load(Skip, Take);

//Any number you want
Skip = Skip + 14;
}
});
})

Summery :

Infinite scroll is every where now ,this sample show give good starting point.It’s not only loading images you can load any thing you want start from images,text data and even load pages inside the page.

I hopefully you like the sample and really appreciate your comments and feedback
.

Regards.

 
2 Comments

Posted by on February 26, 2012 in ASP.NET, C#, jquery, VB.NET

 

Tags: , , , , , , , , ,

Building a Database Driven Hierarchical Menu using ASP.NET and SooperFish Jquery Plugin


Click on the Image To see the Demo


Introduction

In this article I will show you how to build database driven hierarchical multi-column dropdown menus using SooperFish jquery Plugin. But why SooperFish Plugin ?
Feature of SooperFish PlugIn jquery
•  Automatic dual or triple columns based on number of child menu items
•  Optional delay before hiding menu on mouse-out
•  Optional automatic indicator arrows (in black or white)
•  Configurable show AND hide animations
•  Custom easing supported
•  Works with jQuery backlava plugin (optionally)
•  Works fine with Javascript disabled
•  Comes with several free themes to demonstrate styling
•  3.65kb uncompressed
•  2.01kb minified

To me the preceding feature very nice but why SooperFish

Because to build animated meun using SooperFish you need only three HTML Tags (ul,li and a), so that’s way I like very much SooperFish PlugIn .See  image 2

Steps to build a Database Driven Hierarchical Menu using ASP.NET and SooperFish Jquery Plug in

Note: Thanks to Michael Libby for his nice article Building a Database Driven Hierarchical Menu using ASP.NET 2.0

Step 1 – create self-join Table and fill it with some data


The simplest way to build hierarchical data is create  self-join table  which parent Menu and Child Menu in the same table .the child menu will use ParentID  to establish a relationship with MenuID in parent Row
Figure 3:  self-join Table

Self Join

Fill the table with some data see –Figure 4


Figure 4 : Parent, Child Relationships

Step 2 : retrieve Data and Create Nested Relationship using DataSet


The DataSet() is perfect  for  retrieve the  data and create relational data and convert it into xml format see the following code

public string GenerateXmlFormat()
    {
        string SqlCommand;
        DataSet DbMenu;
        DataRelation relation;

        using (SqlConnection conn = new SqlConnection(ConnectionString))
        {

            SqlCommand = "Select MenuID, Name,Url, ParentID from MenuTable";

            DbMenu = new DataSet();

            SqlDataAdapter Adapter = new SqlDataAdapter(SqlCommand, conn);

            Adapter.Fill(DbMenu);

            Adapter.Dispose();
        }

        DbMenu.DataSetName = "Menus";

        DbMenu.Tables[0].TableName = "Menu";

        //create Relation Parent and Child
        relation = new DataRelation("ParentChild", DbMenu.Tables["Menu"].Columns["MenuID"], DbMenu.Tables["Menu"].Columns["ParentID"], true);

        relation.Nested = true;

        DbMenu.Relations.Add(relation);

        return DbMenu.GetXml();
    }

The most important points in the previous code are
•  The DataRelation Class, which allow you to create relation between Parent and Child Column.
•  Nested property  ,which allow you to build Hierarchical data .
•  GetXml() function ,represent the retrieved  data as xml format.
The result after calling DbMenu.GetXml()  shown in the next Figure

Figure 5 : Hierarchical data in Xml Format after calling GetXml() method.

Step 3 – using XSLT to convert XML data to HTML format


After generating data as xml (Figure 5) we need to convert this XML or reformate it to HTML Markups .So we need an XSLT to convert xml format to HTML .
First of all create new XSLT file and then write this code

<!-- Find the root node called Menus then convert it to <UL> </UL> HTMLTags
       and call MenuListing for its children -->
  <xsl:template match="/Menus">
    <ul>
      <xsl:attribute name="class">
        <xsl:text>sf-menu</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="id">
        <xsl:text>nav</xsl:text>
      </xsl:attribute>
      <xsl:call-template name="MenuListing" />
    </ul>
  </xsl:template>

  <!-- Allow for recusive child node processing -->
  <xsl:template name="MenuListing">
    <xsl:apply-templates select="Menu" />
  </xsl:template>

  <xsl:template match="Menu">
    <li>
      <a>
      <!-- Convert Menu child elements to <li> <a> html tags  and attributes inside a tag -->
      <xsl:attribute name="href">
        <xsl:value-of select="Url"/>
      </xsl:attribute>
        <xsl:value-of select="Name"/>
      </a>
      <!-- Recursively call MenuListing for child menu nodes -->

      <xsl:if test="count(Menu) > 0">
        <ul>
        <xsl:call-template name="MenuListing" />
        </ul>
      </xsl:if>
    </li>
  </xsl:template>
</xsl:stylesheet>

The XSLT code will do the following
1.Find the root Node Called Menus and convert it to ul tags with 2 attributes class name and id (class will be used by SooperFish PlugIn and ID important for stylesheet).
2.Call MenuListing Template for nested or children Menu
3.Find the node Called Menu and convert it to li and a html tags with href attribute for a tag with Name or title (the value of href attribute come from Url node and same thing with title for a).
4.Recursively call MenuListing for child menu node but the most important thing we add <ul> tag before Calling MenuListing Template
Now the result will be as following

<ul class="sf-menu" id="nav">
      <li>
          <a href="#">Products</a>
            <ul>
             <li>
              <a href="#">Office</a>
            <ul>
                 <li>
                   <a href="#">Offiice2003</a>
                 </li>
                 <li>
                   <a href="#">Office2007</a>
                 </li>
                  <li>
                   <a href="#">Office2010</a>
                  </li>
           </ul>
     </li>
....
....
</ul>

Step 4 – Convert XML to HTML using XSLT in ASP.NET


To apply XSLT transformation we need an XML data returned by the  GenertateXmlFormat() Method also we need an XSLT file after that we will convert XML TO HTML Format and return the new format as string

public string ExecuteXSLTransformation()
    {
        string HtmlTags,XsltPath;
        MemoryStream DataStream = default(MemoryStream);
        StreamReader streamReader = default(StreamReader);

        try
        {
            //Path of XSLT file
            XsltPath = HttpContext.Current.Server.MapPath("XsltFormatFolder/TransformXSLT.xsl");

            //Encode all Xml format string to bytes
            byte[] bytes = Encoding.ASCII.GetBytes(GenerateXmlFormat());

            DataStream = new MemoryStream(bytes);

            //Create Xmlreader from memory stream

            XmlReader reader = XmlReader.Create(DataStream);

            // Load the XML
            XPathDocument document = new XPathDocument(reader);

            XslCompiledTransform XsltFormat = new XslCompiledTransform();

            // Load the style sheet.
            XsltFormat.Load(XsltPath);

            DataStream = new MemoryStream();

            XmlTextWriter writer = new XmlTextWriter(DataStream, Encoding.ASCII);

            //Apply transformation from xml format to html format and save it in xmltextwriter
            XsltFormat.Transform(document, writer);

            streamReader = new StreamReader(DataStream);

            DataStream.Position = 0;

            HtmlTags = streamReader.ReadToEnd();

            return HtmlTags;
        }
        catch (Exception ex)
        {
            ErrorMsg = ex.Message;
            return ErrorMsg;
        }
        finally
        {
            //Release the resources

            streamReader.Close();

            DataStream.Close();
        }

    }

Step 5 –Tied everything together


We are now in the last step ,we need just to add new aspx page and add the necessary jquery files including SooperFish Plugin  and style sheet  files as following

<link rel="stylesheet" type="text/css" href="Styles/sooperfish.css" media="screen"/>

<link rel="stylesheet" type="text/css" href="Styles/sooperfish-theme-large.css" media="screen"/>

<script type="text/javascript" src="Jquery/jquery-1.4.2.min.js"></script>

<script type="text/javascript" src="Jquery/jquery.easing-sooper.js"></script>

<script type="text/javascript" src="Jquery/jquery.sooperfish.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $('ul.sf-menu').sooperfish();
        });
    </script>

After that add Literal control in your page and assign html format programmatically Page_Load as following

if (!IsPostBack)
 {
 SooperFish spoorfishMenu = new SooperFish();
 Literal1.Text = spoorfishMenu.ExecuteXSLTransformation();
 }

Advance SooperFish effects


SooperFish Allow you to animate your menu in many different ways see the following code

<script type="text/javascript">
    $(document).ready(function() {
      $('ul.sf-menu').sooperfish({
    dualColumn  : 6, //if a submenu has at least this many items it will be divided in 2 columns
    tripleColumn  : 8, //if a submenu has at least this many items it will be divided in 3 columns
    hoverClass  : 'sfHover',
    delay    : 500, //make sure menus only disappear when intended, 500ms is advised by Jacob Nielsen
    animationShow  : {width:'show',height:'show',opacity:'show'},
    speedShow    : 750,
    easingShow      : 'easeOutBounce',
    animationHide  : {width:'hide',height:'hide',opacity:'hide'},
    speedHide    : 300,
    easingHide      : 'easeInOvershoot',
    autoArrows  : true
      });
    });
    </script>

aslo when you download the plugin files you will find two more stylsheet with some html pages show you some other effects

Note : you can know more about SooperFish here SooperFish Multi-Column Animated Drop-down

Download

AspSooperFish

Summary

This article show you how to build nice menus using SooperFish Jequery Plugin instead of using normal asp.net menu which generate table tags .All you have to do is create self-join table and generate xml using DataSet() after that convert it to html (ul,li and a) tags using XSLT.

Fill free to tell me about  AspSooperFishMenu

References

Building a Database Driven Hierarchical Menu using ASP.NET 2.0

What Is XSLT?

 
38 Comments

Posted by on September 14, 2010 in ASP.NET

 

Tags: , , , , , , ,

برنامج مستعرض الصور MyAlbum2 بإستخدام Asp.Net and Jquery


بسم الله الرحمن الرحيم
في هذا الدرس سوف نستعرض معا مستعرض الصور الإصدار الثاني وهو باستخدام مكتبة (Jquery) مع لغة (Asp.Net) والهدف من المستعرض هو عرض الصور من مصدر بيانات (DataSource) سواء قواعد بيانات مثل (Sql Server) او ملفات (XML)
صورة من مستعرض الصور

مميزات مستعرض الصور
• دعم كامل لمصادر البيانات المختلفة
فيمكنك أن تخزن الصور في قواعد بيانات او تقوم بتخزينها في ملفات (Xml)
• عرض صورة جاري التقدم (Progress Image)
كما ترى في الصورة التي في الأعلى فأن الصورة التي لم تظهر بعد يكون مكانها صورة جاري التحميل (جميل جدا ,صح)
• إستخدام (Linq To Sql) وذلك في إضافة الصور الى قاعدة البيانات وإستعرض الصور من قواعد البيانات
• إستخدام كترول (Asp.net Repeater) والغرض من ذلك انه عن عرض الصفحة فإن الأوسم (Tags) التي سوف تظهر هي عبارة عن (Div)
• (Custom Paging) بإستخدام (Linq) والمقصود هنا التنقل المخصص بين الصفحات من الخلف الى الأمام أو من ألامام إلى الخلف وهذا يتم عن طرق الشفرة(Code) لأن (Asp.net Repeater) لا تدعم التنقل الإفتراضي (Default Paging).
طبعا مستعرض الصور يستخدم مكتبة (Jquery) وذلك لعمل التأثيرات مثل الظهور (Fade out) والإختفاء (Fade In) مع إمكانية تحديد السرعة للظهور والإختفاء
كان غرضي من المشروع او المعرض الخاص بالصور هو أن أجمع أكثر من فائدة في نفس الوقت فأردت أن أريك القوة الحقيقة لمكتبة (Jquery) وايضا القوة الحقيقة لتطبيق لغة الإستعلام المدمجة (LINQ) وكيف قامت في الحقيقة بتبسيط الأشياء لنا . لنبدء بالشرح على بركة الله.
أولا الفصائل (Classes)
طبعا برنامج مستعرض الصور يحتوي على فصيلة واحدة إسمها (ImageManger) وجميع محتوياتها ساكنة (static) أي بمعنى أنه لاداعي لإنشاء كائن (New Object) من هذه الفصيلة والغرض من هذه الفصيلة هو عرض البيانات (Select statement) ومعالجة التنقل بين الصفحات بناء على حجم او عدد الصور في الصفحة الواحدة أنظر الصورة التالية


والان نأتي للشرح عمل البرنامج ببساطة
طبعا عند تحميل الصفحة فسوف نقوم باسترجاع عدد معين من الصفوف بناء على ماتحدده انت خمسة صفوف, ستة صفوف كما تشاء وايضا العدد الإجمالي للصفوف حتى نعرف كما هو عدد الصفحات الموجودة كالتالي

protected void Page_Load(object sender, EventArgs e)
{
     if (!IsPostBack)
      {
        //Start From 1 Thats mean the first page
        ViewState["Counter"] = 1;
      //Get the number of pages and save it in the ViewState
      ViewState["ImageRow"] = ImageManager.GetRowNumber();
     //Get the number of images (5,6,...) As you want
      Repeater1.DataSource = ImageManager.GetImages();
     Repeater1.DataBind();
  }
}

بعد ذلك يوجد هنالك زران الأول يقوم بنقلك الى الصفحة التالية والزر الاخر يقوم بنقلك الى الصفحة السايقة
اولا عند الانتقال الى الصفحة التالية نقوم بتحديد عدد الصفوف المراد تجاوزها وزيادة عدد الصفحات مرة واحدة وايضا التحقق من إذا كان في الصفحة الاخيرة اما لا كما هو موضح في التالي

 protected void Forward_Click(object sender, EventArgs e)
    {
        //Enable Backword button
        Backward.Enabled = true;
        //Increase the number of page based on the current number
        //number of rows that will be skipped
        int Row_Index = ImageManager.IncreaseRows((int)ViewState["Counter"]);
        //Check if this page is last page or not
        if ((int)ViewState["Counter"] < (int)ViewState["ImageRow"])
        {
            //Get the number of images based on the page
            SetSource(ImageManager.GetImages(Row_Index));
            //Save the current page in the ViewState counter
            ViewState["Counter"] = ImageManager.IncreaseValue((int)ViewState["Counter"]);
            //Check if we are in the last page then disable the foreword control
            if (ImageManager.IsLast((int)ViewState["Counter"],(int) ViewState["ImageRow"]))
            {
                Forward.Enabled = false;
            }
        }

    }

ثانيا عند الى الانتقال الى الخلف نقوم بنفس الخطوات السابقة ولكن نتحقق اذا كان في الصفحة الاولى اما لا كما هو موضح في التالي

protected void Backword_Click(object sender, EventArgs e)
    {
        //Enable the ForeWord button
        Forward.Enabled = true;
        //number of rows that will be skipped
        int Row_Index = ImageManager.DecreaseRows((int)ViewState["Counter"]);
        //check if this page is the first page or not
        if ((int)ViewState["Counter"] > 0)
        {
            //decrease the number of pages
            ViewState["Counter"] = ImageManager.DecreasValue((int)ViewState["Counter"] );
            //number of skipped row ,this number will be use in skip method
            int SkipRows = ImageManager.SkippedRow(Row_Index);
            SetSource(ImageManager.GetImages(SkipRows));
            //Check if we are in the first page then disable the foreword control
            if (ImageManager.IsFirst((int)ViewState["Counter"]))
            {
                SkipRows = 0;
                Backward.Enabled = false;
            }

        }
    }

ملاحظة
طبعا هنالك صفحة أخرى خاصة برفع الصور وحفظها في مجلد على السيرفر وفي الحقيقة لم أتطرق لموضوع كيفية تخزين الصور او طريقة تصميم الجدول في قاعدة البيانات لك حرية الإختيار في أضافة وتعديل ماتراه مناسبا في البرنامج مفتوح المصدر

في الختام
اريد في ختام هذا الموضوع ان اوضح لكم الغرض الاساسي من هذا المستعرض حيث انني اجد في الكثير من المنتديات من
يريد طريقة عمل مستعرض للصور لذلك قررت بعد التوكل على الله أن اقوم ببناء هذا البرنامج البسيط واردت ان اجمع فيها
أكثر من فائدة مثل Jquery,Linq To Sql وأردتها ان تكون باسلوب ممتع
لذلك اعتذر ان وجود احدا من كم خطا في ماكتبته واتمنى ان يصحح لي  واتمنى أن يكون نقاشنا بناء حتى نتعلم سويا ونكون قادرين على الانتاج وبناء مجتمع عربي تقني قادر على المنافسة
والله الموفق
رابط التحميل MyAlbum2.0

 
9 Comments

Posted by on June 19, 2010 in ASP.NET

 

Tags: , , , , , ,