Sunday, December 28, 2014

Adding Visual Studio Extensions into source control

The Problem

Extensions, are a very great way to add third party stuff into your Visual Studio, and your development environment, just like the Visual Studio NuGets, that provide you with great tools with just 2 clicks. This is extremely OK when you are working in a project on your own.
The real pain come when you are working on a project with a team, sharing code on a source control, or any way of files sharing. What typically happens is, someone on your team adds an extension into his Visual Studio, references the extension in a piece of code, and bom! compilation errors everywhere on everybody else's machines. So you have to ask everyone to install the right extension with the right version number that you decided to install. What makes it even worse is when you decide to install an update to the extension for some reason. This does really a huge mess, and wastes time for everybody trying to figure out what is breaking the build.
Unlike NuGets, Visual Studio has the option to automatically recover the missing NuGets when building the solution, extensions doesn't have this option, as it's not related to a project/solution but to the development environment and visual studio.

Solution - The Idea

To use an extension, all you have to do to reference the extension, is open the "Add Reference" dialog of a project, then select the Extensions from the left column, and you will get access to all the installed extensions in Visual Studio.
If you noticed when hovering over any of the extensions, you will find the location of the extension files,

The idea here, is to move the extension to a source controlled folder, and let the project file look for the extension in this folder, instead of the one installed in Program files folder. This will let everybody in your team get immediate access to the extension, the moment you check in the extension, and the project files. 

How To

In order to do this change, you should first copy the extension into your source controlled project folder, but not exactly a copy of the extension from the program files folder, but in a special folders' hierarchy. Next you have to do some changes into your project file to point to the new extensions folder.

Folder's structure

Create a folder, and name it "libs". Create the following nested folders as shown below, in the following order: Windows>v8.1>ExtensionSDKs>"SDKName">"SDKVersionNumber.



Now copy the extension contents from the default installation folder to the target folder under the SDKVersionNumber folder as shown below.


Project File Changes

Add the following lines at the end of the project file inside the Project main node:
 
    $(SolutionDir)\libs;$(SDKReferenceDirectoryRoot)
 


Notice the highlighted part above, is the location of the "libs" folder that has your extensions, relative to your solution directory. You should change this as per your own reference. For instance, I have my libs folder in the same folder with my solution file, so I used $(SolutionDir)\libs.

If all the previous steps were done correctly, open the "Add References" dialog, and you will find duplicate entries of extensions in this list; one of the extension in its default install location, and the other in your newly created source controlled folder of your project. You will probably need to unselect the default location entry, and select the entry located in the newly folder.


Wednesday, February 1, 2012

Set WebPart Icon in Site Definition.

It’s a very common scenario to create a site definition with pages, features, lists, web parts…etc

Capture

I have tried to change the web part icon as per the information in MSDN page, by using the “ImageUrl” property, but this didn’t work.

<View List="$Resources:core,lists_Folder;/Discussions" ImageUrl=”/_layouts/????????.png”  BaseViewID="0" WebPartZoneID="Left" Name="$Resources:Strings,List_Instance_Title_Discussions_Forum;" WebPartOrder="1" ShowHeaderUI="True"/>

So I decided to catch the webpart and set the ImageUrl programmatically. So the only way to do this is to implement this in the FeatureActivated event in the FeatureReceiver class.

   1: public override void FeatureActivated(SPFeatureReceiverProperties properties)
   2: {
   3:     base.FeatureActivated(properties);
   4:     SetWebPartIcon(properties)
   5: }
   6:  
   7: private static void SetWebPartIcon(SPFeatureReceiverProperties properties)
   8: {
   9:     try
  10:     {
  11:         SPSecurity.RunWithElevatedPrivileges(() =>
  12:         {
  13:             SPWeb site = properties.Feature.Parent as SPWeb;
  14:             SPLimitedWebPartManager webPartManager = 
  15:                 site.GetLimitedWebPartManager(site.ServerRelativeUrl + "/default.aspx", PersonalizationScope.Shared);
  16:             SPLimitedWebPartCollection webParts = webPartManager.WebParts;
  17:             if (webParts != null && webParts.Count > 0)
  18:             {
  19:                 foreach (System.Web.UI.WebControls.WebParts.WebPart webPart in webParts)
  20:                 {
  21:                     if (string.Compare(webPart.Title, "Web Part Name", false) == 0)
  22:                     {
  23:                         // Set the Image URL
  24:                         webPart.TitleIconImageUrl = "*****";
  25:                         webPartManager.SaveChanges(webPart);
  26:                     }
  27:                 }
  28:             }
  29:         });
  30:     }
  31:     catch (Exception ex)
  32:     {
  33:         // Catch the Exception
  34:     }
  35: }

Friday, August 6, 2010

Adjusting Reporting Services Report width into A4 size.

I always had issues regarding exporting the reports into PDF, would result in pages split horizontally into 2 pages. The trick is simply changing the report width, and margins in the report properties.

Report > Report Properties

Choose the layout tab, and change the values as following:

Columns: 1
Spacing: 0.5
Page Width: 8.27 in
Page Height: 11 in
Left Margin: 0.1 in
Right Margin: 0.1 in
Top Margin: 1 in
Bottom Margin: 1 in

SSRS AhmedIG

That would do it for you.

Thursday, December 31, 2009

Drilling 2 steps in Reports Viewer 2005

Imagine a situation where you have a report that has a link to another report. The other report also has a link to another third report. This sounds logical and easy, and it works perfectly in the report server viewer. But when you try to deploy your reports and view them using the report viewer you have in Visual Studio 2005, it throws an exception:

The path of the item "(null)" is not valid. The path must be less than 260 characters long and must start with slash. Other restrictions apply. (rsInvalidItemPath)

As far as I know, this problem is only available in the report viewer of Visual studio 2005. I worked out a work around for this issue.

The Idea:

You have to handle the event of drilling and tell the report viewer to cancel the event, collect the parameters of the new report and set them, and finally set the report path of the report viewer to the new path. Now you are done. Have a look at the code below.

   1: protected void ReportViewer_Drillthrough(object sender, DrillthroughEventArgs e)
   2: {
   3:     /// This version of the report viewer doesnt support the nested reports
   4:     /// this section is used to rerender the whole thing.
   5:     string[] values = null;
   6:     
   7:     // Get All possible parameters that this report takes
   8:     ReportParameterInfoCollection reportParms = e.Report.GetParameters();
   9:     // Create an empty list of parameters that i will send
  10:     List<ReportParameter> parms = new List<ReportParameter>();
  11:     foreach (ReportParameterInfo i in reportParms)
  12:     {
  13:         // if the parameter value in nullable, and I didnt choose to send a value
  14:         if (i.Values.Count == 0)
  15:             continue;
  16:  
  17:         else if (i.Values.Count > 1)
  18:         {
  19:             values = new string[i.Values.Count];
  20:             i.Values.CopyTo(values, 0);
  21:             parms.Add(new ReportParameter(i.Name, values));
  22:         }
  23:         else
  24:         {
  25:             parms.Add(new ReportParameter(i.Name, i.Values[0]));
  26:         }
  27:     }
  28:  
  29:     // Now reset the viewer, as if its a new request
  30:     ReportViewer.Reset();
  31:     // Cancel the drilling action
  32:     e.Cancel = true;
  33:     // Reset the ReportServerURL, this is optional.
  34:     // If you dont know what to write here, try removing it.
  35:     ReportViewer.ServerReport.ReportServerUrl = new Uri(Session["SSRS Server"].ToString());
  36:     // Set the new report that will be displayed, and set its parameters
  37:     ReportViewer.ServerReport.ReportPath = e.ReportPath;
  38:     ReportViewer.ServerReport.SetParameters(parms);
  39: }

One draw back of this solution, is that the back button of the report viewer is not effective anymore. On the other hand, the browser's back navigation button is effective, and can take you to the previous parent report. However, if you clicked any other link in the parent report, it will throw an exception:



Execution 'vzlnjkfdkub0oan53dig5g55' cannot be found


I thought of a solution, which I don't know is acceptable for you or not, which is disabling the browser's back button using JavaScript (see below), and adding a navigation expression in my report. i.e. adding a textbox in the report, with text something like "<< Back" which jumps to the parent report, and sending the needed parameters.



   1: <script language="javascript" type="text/javascript" >
   2:     history.forward();
   3: </script>

Monday, September 21, 2009

Setting A report's Data Source to a Shared Data Source Programmatically

One of the things that you will surely need when adding reports to the reporting service programmatically, is setting its data source to the shared data source you have in the reporting server. I personally had this problem and I thought that "I can create a new DataSource Object, then use the SetItemDataSources method to attach this data source to the report". That's what I actually did, but I always had an exception telling me that, The data source ABC is not found, although am sure that the data source name is the same like the shared data source's name.

What I found out later is, the SetItemDataSources method searches the report's data sources for the name you have set, i.e this name is not the shared data sources' name, but the data sources that is in the rdl file (in the <DataSources> tag).
So in order to fix that, we need to tell the reporting service that the report's data source with the name X (which we will see how to find it out) is still there, but it will reference to the shared data source.
so now lets get our hands dirty with some code.

How to

//Get the Shared Data Source
DataSource[] ds = GetSharedDataSource(service, reportsFolder, reportName);
// Set Report's DataSource
service.SetItemDataSources(@"/" + reportsFolder + @"/" + reportName, ds);

public static DataSource[] GetSharedDataSource(ReportingService2005 service, string reportsFolder, string reportName)
        {
            DataSourceReference reference = new DataSourceReference();
            DataSource ds = new DataSource();
            reference.Reference = "/" + reportsFolder + "/" + "SharedDataSource";
            ds.Item = (DataSourceDefinitionOrReference)reference;
            // Get original report Data Source Name
            DataSource[] reportDataSource = service.GetItemDataSources(@"/" + reportsFolder  + @"/" + reportName);
            ds.Name = reportDataSource[0].Name;
            return new DataSource[] { ds };
        }

Friday, May 29, 2009

Building your ASP.NET MVC application with Subsonic 3.0

Subsonic really rocks... I loved Subsonic 2.0 and took the risk of starting a huge application using Subsonic a year ago while it wasn't that popular when comparing it to other DAL generators like .NetTiers template of codesmith. But to be honest, I loved subsonic since then very much, being so easy, light, straight forward, not complex, and what is the most important, getting things done just the way I want them.

Suddenly, Rob Conery introduced the new Subsonic 3.0 which is targeting the ASP.NET MVC technology. This sounds really great, and I liked the idea so much, but the question is.. Where can I learn it from? So far, all screen casts that Rob did are kind of "showing off" the new subsonic. Nothing published so far showing you how to create an application from scratch using Subsonic on Database called XYZ for instance like he used to do with earlier versions. Maybe he is waiting for the final release of Subsonic 3.0 to do so.

I liked Rob Conery's comment that he added in Preview 2's package. It was something like "This is a Preview version.. so BE GENTLE".


Anyway, I am really waiting for this version of subsonic, I guess it will be a good plus for using ASP.NET MVC.

Now lets talk about Subsonic, and how to get your application working using subsonic 3.0. Please note, that Subsonic 3.0 is a brand new tool, and am not that good in it. I learnt that from trial errors, so don't hesitate to correct me if am wrong in anything am posting.

Prerequisites

  • Install MVC into your visual studio from here.
  • Install Subsonic Template from here.

Installation of the MVC on Visual Studio is a straight forward issue. You have to run the msi file and you are done.

For subsonic, Place the zipped file you downloaded into the following directory (My Documents\Visual Studio 2008\Templates\ProjectTemplates\Visual Web Developer\Web). Don't extract the zip file, but keep it there as it is.

Starting your application

Open Visual Studio. Then from the files menu, Create a new Project.

As shown below. Choose the Subsonic MVC Template, then press ok.

1

The solution structure will look exactly like that:

SolutionStructure

Now, open the web.config file, and edit the connection strings. This is the main part, as everything is based upon the DB you are using. The whole DAL will be generated according to the Database. Dont forget to change the Database name as well. We will later make the subsonic get to know the new name.

<connectionStrings>     <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>     <add name="Chinook" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|Chinook.mdf;User Instance=true" providerName="System.Data.SqlClient"/> </connectionStrings>

Now here we come to the tricky part, that caused me a lot of confusions. I used to miss this part and end up all the time hitting my head against the wall.

Open the Model/_settings.tt file. This is one of the T4 templates subsonic is using. Edit this part:

const string Namespace = "SubSonic.Web.Models"; const string ConnectionStringName="Chinook";

Now you can change the ConnectionStringName to the name you defined in the web.config section, then save the file.

Creating the DAL

Right click on the _settings.tt file, and select the "Run Custom tool" option as shown below. Repeat this step with the 4 Template files in the Models directory.

RunCustomTool

You will find some warning messages in the error list while running the template files; just neglect them. Always try to remember Rob's phrase I stated earlier; "Be Gentle, this is a preview version" ;) So I guess it's better to choose not to show the warning messages in the error list :)

Now time to build. Oops, 3 errors? Let me tell you that now you can say "I successfully generated my DAL using Subsonic!!" They are as simple as adding this line "using SubSonic.Web.Models;" into HtmlExtensions.cs and SubsonicRepository.cs files.

errorList

Now we are done.

You can learn how to create the Models, Views, and controllers using subsonic's tools, I recommend watching Rob's screencast. Although he showed everything quickly and without any explanations, but I think it doesn't need any.

Correct me if am wrong in any of the tips I stated. I told you, no one knows better than the other when it comes to beta editions of software ;)

Sunday, April 5, 2009

Creating your own site's search Engine

Setting up you search page for your web application is as easy as having a DB table where you retrieve data from by a simple query. I will illustrate the steps of setting up the configurations needed in this post, and will write another one for detailed stuff in custom search page. So lets start.


Setting up your Search Index

  1. start>run> type mmc
  2. in the new window, File> Add Remove Snap-in, or press CTRL + M as a shortcut
  3. Press Add, and choose Indexing Service from the List box as shown below
    clip_image001
    Press the Add button.
  4. A dialog box will appear asking you about the Computer name you want to apply the Indexing on, choose the local computer for this Demo.
    clip_image002
  5. Close all dialogs you have on your screen until you reach your Console Window, with Indexing Service running.
  6. Right Click on the Indexing Service node under the Console Root, and choose New>Catalog.
    Make sure that the Start option in the menu is activated before doing this step, otherwise, you have to stop the service first.
    clip_image003
  7. Give the new Catalog any Name. I will call it PP4.
    Browse to a directory where the Catalog file will be stored in. This is not the directory which you want to Index.
    clip_image004
  8. You will find a new node called PP4 just appeared under the Indexing Service node.
    Now we want to tell the new Catalog to search in your site. Right click on the PP4 Catalog, and choose New>Directory
    clip_image005
  9. Now fill in the Data as below
    clip_image006
    The Path, is the Physical Path of the website you'd like to Index. Finally press OK.
  10. Now right click on the PP4 Catalog, and choose Properties. Go to the tracking tab, and change the WWW server to your Default Website.
    clip_image007
  11. Now go to the Generation tab, and uncheck the checkbox "Inherit above settings from Service".
    clip_image008
    It Enables by default the "Generate abstracts" checkbox. This tells the search index, to get some text from the searched pages, just like Google.com for instance when they get you a sample text under each item in the search result. The default is not generate any abstracts. This text is by default the first 320 characters in the page. You can customize that just by adding some text in the "description" meta tag in the HTML's head section.
  12. Now start the Service.
    clip_image009

Now we are done with setting up the search index configurations. Next you have to create a page that calls this catalog for searching. But before doing this, lets try to figure out whether everything is working fine or not.

 

Querying your Search Index through the built in page

Under the PP4 Catalog we have just created, you will find a node called "Query the Catalog". Press on that node and a page will load up at the right part of the window as shown below. Here you can try by typing "Partners" for instance, and have a look at the query result. As you can see, the query includes .cs, .vb, .css files and many other types of unwanted files. you can control that by creating your own page, using your own code.

clip_image010

 

Querying your Search Index through a custom page using Query Language

I will go through the main parts of the query, and you can figure out the rest. It's as if you are querying a simple SQL Database and binding the results to a Repeater.

Connection:

    OleDbConnection odbSearch = new OleDbConnection( "Provider=\"MSIDXS\";Data Source=\"PP4\";");

    Please note that the Data Source is the Catalog name you specified in the Search Index configurations.

Command:

    cmdSearch.Command Text = "select doctitle, filename, vpath, rank, characterization from scope() where FREETEXT(Contents, '"+ searchText +"') order by  rank desc ";

Where the "searchText" is the text you typed in the textbox for searching.

The rest is as easy as executing the query and binding the results to a repeater. I have made one on my own and took a snap shot of the running program below:

clip_image011