Wednesday, June 15, 2011

Using Batch File or Command Line to recursively find all files that have a filename that contains a particular string

It sounds like a tall order at first, but then it also sounds like something that should be built into Windows. In particular, I am using Windows 7, but I assume it works on XP as well.

The solution is actually quite simple and is indeed built into Windows 7.

dir /s /b *my string*

This will give you something similar to:

c:\temp\This is a test of my string.txt

c:\mydocs\My string could be found here.txt

As shown above it will search the current directory and all directories below the current one for files that contain ‘my string’. You can also specify something like this to specify the directory you want to start searching in regardless of your current directory.

dir /s /b “c:\mydocs\*my string*”

You can use all the standard wild card syntax such as the following to search only .html files.

dir /s /b “c:\mydocs\*my string*.html”

In case you are wondering the /s is for recursive and the /b is to return just the path\filename (no file info such as date, size, etc).

Easy Way to remove Visual Studio 2010 project from Visual SourceSafe

Visual SourceSafe creates .scc files when you add a Visual Studio 2010 (VS2010) project to Visual SourceSafe. The easiest way I know to remove a project from Visual SourceSafe version control (while still leaving the actual files in SourceSafe) is to do the following. You might want to do this if you want to make a copy of your project to use as a starting point for your new project or maybe want to share the project with someone else or maybe you are changing version control systems. Regardless, below are the steps to do this.

  1. Make a copy of your project using copy and paste in Windows Explorer.
  2. Recursively Search the project directory for all files that end in .scc. You can do this using the Search in Windows 7 or Search Companion (I think it was called in XP).
  3. Delete the results found.
  4. Open solution in VS2010. It will complain and eventually ask if you want to permanently remove the bindings. Do so for each project in your solution. You can go to the File menu | Source Control | Change Source Control… If there are any projects that are still bound, you can unbind them.

That’s it. Relatively easy actually.

Compiler error with VS2010 SP1 and SL 4 with WCF RIA Services and Entity Framework 4

Did you have a VS2010 project (before installing VS2010 SP1 or RIA Service Pack 1) that used Silverlight 4 and WCF RIA Services and Entity Framework 4 (EF4) and now that VS2010 Service Pack 1 is installed (or maybe some other framework) your Silverlight Project no longer compiles. Chances are if you are using RIA Services you may have created files that you want to share (called Shared Code)on the Server and the SL Client. This is done in RIA by using the .shared.cs file extension on a filename.

After I installed either VS2010 SP1 or RIA Services SP1 and then I tried to compile my project and got a compiler error similar to:

error CS0102: The type 'MyApp.Model.MyClassHere' already contains a definition for 'MyPropertyHere'

I learned that this may be a RIA Services SP1 regression bug from this forum. The beta had issues also.

It appears that this MAY be fixed now, but I’m not sure since I don’t have an issue exactly like what they had. I only have the issue if I have to shared files and one the classes (in one of the files) has a property or method that has a return type that is the other class type. If I get rid of that dependency and just return the object type instead then it compiles fine. While returning the type object, I have to cast every time I use it and that is silly and kind of a pain / ugly.

I hope that helps someone. If anyone else has this issue I’d love to know about it.

Friday, June 3, 2011

How to Download File from inside an UpdatePanel in ASP.NET

PartialPageRendering when used with the UpdatePanel is a great thing in general. One problem comes when you want to mess with the Response and call things like End which is required when you return want to return a file instead of the page you were on. An example of this is a GridView with a Download button below it. If you are not using the UpdatePanel then there is no problem and clicking the Download button will all you to download the file as expected. There are lots of posts on the web on how to do this so I won’t go into the code. However, it involves calling things like Response.Clear(), Response.ContentType, Response.AddHeader(), Response.End().
Now let’s assume you put the GridView in an UpdatePanel. All those Response calls noted above are things that the PartialPageRendering and UpdatePanel don’t know what to do with because they were expected something like the page that made the request. So we need to work around the issue. I found some solutions on the internet, but didn’t really find them very easy or straight forward. So, I came up with my own.

Solution 1

One solution is to put the Download button outside the UpdatePanel. This tends to work best, but is not always possible.

Solution 2

If you can’t do solution 1, then consider faking it. What I mean is move the Download button which in my case is implemented as a LinkButton outside the UpdatePanel and hide it using css. Then add a Hyperlink inside the UpdatePanel. Now, on your page load you can set the hyperlink to have the same postback url as the LinkButton that is outside the UpdatePanel. That way when you click the hyperlink inside the UpdatePanel you are effectively doing the same thing as if you clicked the hidden LinkButton that is outside the UpdatePanel. Fairly simple.
Here is the snippets of code to help you see what I am talking about.
Here is the HyperLink that is inside the UpdatePanel and is also the button that you will be clicking in the Browser.
<asp:HyperLink ID="btnExportToExcelClicker" runat="server" Text="Export all rows to Excel" />

Here is the LinkButton that is invisible to the user and is outside the UpdatePanel
<asp:LinkButton ID="btnExportToExcel" runat="server" onclick="btnExportToExcel_Click" Text="Export all rows to Excel" style="display:none;" />

Here is the code-behind. In my code btnExportToExcelClicker is the button that is in the UpdatePanel and btnExportToExcel is the button that is outside the UpdatePanel. You will actually be clicking btnExportToExcelClicker because btnExportToExcel is hidden.
protected void Page_Load(object sender, EventArgs e)
{
    btnExportToExcelClicker.NavigateUrl = Page.ClientScript.GetPostBackClientHyperlink(btnExportToExcel, null);
}

This technique uses GetPostBackClientHperlink and can be expanded for other purposes and other controls. In some cases you may need to consider using GetPostBackEventReferences, though I haven’t tried it.

UPDATED: November 5, 2013

Solution 3

This is my new favorite solution due to its simplicity. This involves creating a new page in your application. Call it ReportDownload.aspx (or whatever you desire). It is critical that it not have an update panel. Best choice is usually to not even use a master page for this. The reason is that we are not going to use anything on the ReportDownload.aspx file. We will however change the ReportDownload.aspx.cs file. On the page load just put your download streaming code that would be something similar to the following:

var response = Page.Response;
response.Clear();
response.AddHeader("content-disposition", string.Format("attachement; filename=\"{0}\"", "Report.pdf"));
response.ContentType = "application/octet-stream";
response.WriteFile(@"c:\temp\DB.pdf");
response.End();

You just need to put this in the Load or PreRender events. To test this, just go to the new page you created in your favorite browser. You should see the file you specified in the code above. Now that you know that going to the url works, it is just a matter of using that url anywhere on a page (including in an UpdatePanel). I used it in LinkButton or HyperLink. You can pass a code (i.e. a record id, etc) in the url to generate the url. The nice thing is you can also incorporate security on the ReportDownload.aspx as well.

Thursday, May 26, 2011

Programmatically Setting Header on ASP.NET GridView

It is actually very frustrating to change the header text on an ASP.NET GridView if you don’t do it in the right place. At first, I thought I would do it in the PreRender event as I figured that was plenty late in the cycle and should stick. Then I noticed it would not stick when I would sort for example.

In the end, it was very easy once I figured out what the proper event in the page lifecycle to use. You answer to this riddle is the RowCreated event. My GridView is called GridView1 in this example. I added onrowcreated=”GridView1_RowCreated” to my GridView in my .aspx file. In my code behind this is the code I used.

protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.Header && e.Row.Cells.Count > SOME_COLUMN_INDEX)
    {
        e.Row.Cells[SOME_COLUMN_INDEX].Text = "New Header Text here";
    }
}

Some things to know. You can’t reference the HeaderRow because it is still null at this point. It is there, but not accessible via the GridView1.HeaderRow property. You need to use e.Row instead. You can be guaranteed that this is not null since we are its event handler. You may also want to make sure the cell you are trying to set is not out of bounds. As long as you check to make sure you are in the header row you should be ok though. An example of when you would not have cells is when you get the EmptyData row which is displayed only when there are no rows to display. In that case, there are no headers. So, in general, I found it safe to always check to make sure the item I assume is in the array is actually in the array.

Wednesday, May 18, 2011

Programmatically adding Query Parameter to ASP.NET DomainDataSource

I love the new ASP.NET DomainDataSource because it allows me to talk to my Domain Service.

You could do this declaratively like the following:

Declaratively

<asp:DomainDataSource ID="dsDetails" runat="server">
<QueryParameters>
        <asp:ControlParameter ControlID="GridView1" PropertyName="SelectedValue" Name="id" Type="Int32" />
</QueryParameters>
</asp:DomainDataSource>

In this example, this a DomainDataSource that I have connect to a FormView. I want the FormView to update when a GridView called GridView1.SelectedValue changes. I am setting the QueryName and the DomainServiceTypeName in my Page_Init so you don’t see it in the declaration above, but you could put it there as well.

In this scenario, the Query in my Domain Service is expecting a parameter called id. This must the Name property of the ControlParameter control.

Programmatically (Good Option)

We can do the same thing with the parameters, but this time do it programmatically. The declaration would now look like this:

<asp:DomainDataSource ID="dsDetails" runat="server">
</asp:DomainDataSource>

Notice there is not QueryParameters declared, so we need to do this in code. The best place is in the Page_Init() method since it is very early in the page lifecycle.

Add the following line to the Page_Init() method:

dsDetails.QueryParameters.Add(new ControlParameter("id", TypeCode.Int32, "GridView1", "SelectedValue"));

This will do exactly the same as doing it declaratively, but now you nave more control of all the values passed since you are in code. One thing to point out is that when the GridView1.SelectedValue changes, the dsDetails datasource executes the query again automatically. That is why I like this method.

Programmatically (Okay Option)

The above is identical and great and my first choice. This approach here works best if you are getting your value from something that doesn’t change like the QueryString. If you need to set the current user, or some other value (like a querystring value) and don’t care that the dsDetails DomainDataSource will NOT be updated automatically and you will instead need to tell it to databind using something like SelectedItemChanged in the GridView1, you can use the Querying event on the DomainDataSource. In this case you would do the following.

<asp:DomainDataSource ID="dsEditor" runat="server"  
    onquerying="dsEditor_Querying" >
</asp:DomainDataSource>

protected void dsDetails_Querying(object sender, Microsoft.Web.UI.WebControls.DomainDataSourceQueryingEventArgs e)
{
    e.QueryParameters.Clear();
    e.QueryParameters.Add("id", GridView1.SelectedValue);
}

Wednesday, May 4, 2011

Why aren’t my ForeignKeys being loaded on my Custom Dynamic Data page?

This is one entry in a series of blog entries:

If you look at Part I and Part II, you may have noticed that the Product Category is a textfield when it should be a Drop Down List of the Product Categories. If this were a page that was in the DynamicData\CustomPages directory then you would not be having this problem. The reason we are having this problem is because we are calling EnableDynamicData() on our FormView which is great to get basic dynamic data functionality, but it doesn’t know how to load all the metadata for the Product entity. If you debug you will see that the ProductCategory DynamicControl column is not of type MetaForeignKeyColumn. After futher debugging you will see that much of the other metadata from the model is actually missing as well. Thankfully, the fix is very easy.

Change from:

protected void Page_Init(object sender, EventArgs e)
{
     FormView1.EnableDynamicData(typeof(MyDynamicDataExample.Models.Product));
}

to this:

protected void Page_Init(object sender, EventArgs e)
{
    // to get all the goodness like foreign-keys, etc we need to tell it to use our model
    FormView1.SetMetaTable(Global.DefaultModel.GetTable(typeof(MyDynamicDataExample.Models.Product)));
}

Also, from what I can tell the AutoLoadForeignKeys=”true” attribute of the DynamicDataManager has no bearing on whether the Drop Down List is populated or not.

You may have also noticed that the ProductCategory field when in ReadOnly mode shows a link with an number showing instead of the nice text description of the product category.  Luckily, this is an easy fix as well. In the AWDomainService.cs find we need to change the GetProducts() method to include the ProductCategory navigation property. This will cause the SQL that is generated to bring in the ProductCategory table.

Change from:

public IQueryable<Product> GetProducts()
{
    return this.ObjectContext.Products.OrderBy(o => o.Name);
}

to this:

public IQueryable<Product> GetProducts()
{
    return this.ObjectContext.Products.Include("ProductCategory").OrderBy(o => o.Name);
}

The change above will do the trick. However, if you are using this Domain Service with a Silverlight Client, you should also update the AWDomainService.metadata.cs such that the ProductCategory property on the ProductMetadata class has the Include() attribute. If you don’t have both of these Silverlight will not populate the ComboBox (assuming that is what you are using in Silverlight).

Change from:

[Display(Name = "Product Category")]
public ProductCategory ProductCategory { get; set; }

to this:

[Include()]
[Display(Name = "Product Category")]
public ProductCategory ProductCategory { get; set; }

You can download the complete source here.