Useful tidbits related to software development, that I think might be of use or interest to everyone else (or to me when I forget what I did!)
Private Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing GridView1.EditIndex=e.NewEditIndex BindGrid() End SubWhen the grid rebinds, it would now be in edit mode on the selected index. Sometimes you handle the RowEditing event and do something other than put the row into edit mode, such as showing a custom popup window as the editor. Previously this meant intentionally not adding the above line which meant when the grid was rebound it would not be in edit mode. ASP.NET 4 is now automatically setting the EditIndex property, which is affecting backward compatibility in converted projects. The only way to prevent the grid from now going into edit mode on a row is to set 'e.Cancel=True' in the 'RowEditing' event e.g.:
Private Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing 'we only want the unique ID of the data in the row Dim primaryKey As Integer = CInt(GridView1.DataKeys(e.NewEditIndex).Value) 'show a popup window to edit the data for this row popEditWindow.CurrentRecordId = primaryKey popEditWindow.DataBind() mpeEditPopup.Show() e.Cancel = True 'we now need to add this line to prevent the row from going into edit mode End SubA quick search on Google shows that this problem has already been reported to Microsoft which can be tracked here.
.ajax__calendar_title {width:150px; margin:auto; }
<asp:Calendar ID="calCollectionDate" runat="server" SelectionMode="Day" WeekendDayStyle-CssClass="disabledDay"></asp:Calendar>Use Javascript to find the 'disabledDay' items and remove the links:
<script type="text/javascript" language="javascript"> <!-- var disabledDays=getElementsByClassName('disabledDay', '.*', document.getElementById('<%=calCollectionDate.ClientID %>')); for(var i=0;i<disabledDays.length;i++) { disabledDays[i].innerHTML="<span>"+disabledDays[i].innerHTML.replace(/\<a.*?\>(.*?)\<\/a\>/ig,'$1')+"</span>"; disabledDays[i].style.color='#8e8e8e'; } --> </script>This script relies on a third party script for 'getElementByClassName' which can be downloaded from www.robertnyman.com
If your site runs on an ASP.NET server and does not support the classic SSI method of including files for output.
i.e.
<!--#include virtual="/myinclude.asp" -->
Then you may wish to implement your own version of SSI.
In my particular example, I am reading from a HTML file on the server and processing the content before rendering it to the client. I don't see any reason however, why this technique could not be applied in the 'Render' phase of any ASP.NET page.
Essentially, the following function will look for any server side include code in the HTML it is about the render. It will then attempt to process the files that have been included and then swap out the content in the original HTML.
Public Function RunSSI(ByVal htmlContent As String) As String 'look for SSI While htmlContent.Contains("<!--#include virtual=") Dim startOfSSI As Integer = htmlContent.IndexOf("<!--#include virtual=") Dim LengthOfSSI As Integer = htmlContent.IndexOf("-->", startOfSSI) + "-->".Length - startOfSSI Dim ssiText As String = htmlContent.Substring(startOfSSI, LengthOfSSI) Dim ssiFile As String = ssiText.Substring(ssiText.IndexOf("virtual=""") + "virtual=""".Length, ssiText.IndexOf("""", ssiText.IndexOf("virtual=""") + "virtual=""".Length) - (ssiText.IndexOf("virtual=""") + "virtual=""".Length)) 'execute the file Dim contentResponse As String Try Dim wc As New Net.WebClient() Dim URL As String = "" If ssiFile.startsWith("/") Then URL = Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, ssiFile) Else URL = ssiFile End If contentResponse = wc.DownloadString(URL) Catch ex As Exception contentResponse = "<!--SSI Processing Error: " & ex.Message & " " & """" & URL & """-->" End Try 'now replace the include with the response htmlContent = htmlContent.Remove(startOfSSI, LengthOfSSI) htmlContent = htmlContent.Insert(startOfSSI, contentResponse) End While Return htmlContent End Function
You will notice, that the SSI markup should be the 'virtual' version and that the code will only work with an absolute path (either http://... or /...).
For the avoidance of a permaloop and to help debugging any SSI which is not successfully processed is replaced with a comment stating the error and URL which it attempted to process.
Namespace UserControls Partial Public Class ExampleUserControl Inherits System.Web.UI.UserControl Public Event ButtonClick(ByVal sender As Object, ByVal e As System.EventArgs) Private Sub btnCompare_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnClickMe.Click RaiseEvent ButtonClick(Me, EventArgs.Empty) End Sub End Class End NamespacePage codeinfront:
<%@ Register src="/UserControls/ExampleUserControl.ascx" tagname="ExampleUserControl" tagprefix="MyCtrls" %> <asp:Repeater ID="rptExample" runat="server"> <ItemTemplate> <MyCtrls:ExampleUserControl ID="ExampleUserControl1" runat="server" OnButtonClick="ButtonClickHandler"></MyCtrls:ExampleUserControl> </ItemTemplate> </asp:Repeater>Page codebehind:
Protected Sub ButtonClickHandler(ByVal sender As Object, ByVal e As EventArgs) 'handle the user control event End SubOf course, you need to have 'AutoEventWireup=True' in the web.config or @Page directive for this to work.
Dim SIZEOF_HEADER As Integer = &H16 Dim SKIP_BYTES_AFTER_ASMNAME As Integer = 5 Dim oldNamespace As String = "CwCms.DataTypes" Dim newNamespace As String = "CwCms.ControlDataTypes" Dim sizeof_ASMname As Integer = rawCmsRecord.Value(SIZEOF_HEADER) Dim start_of_classname_struct As Integer = SIZEOF_HEADER + 1 + sizeof_ASMname + SKIP_BYTES_AFTER_ASMNAME Dim newRecord((rawCmsRecord.Value.Length - 1) + newNamespace.Length - oldNamespace.Length) As Byte Dim offset As Integer = 0 For i As Integer = 0 To rawCmsRecord.Value.Length - 1 'copy the bytes to the new record If i <> start_of_classname_struct Then newRecord(i + offset) = rawCmsRecord.Value(i) Else 'weve reached the start of the classname struct. inject the custom stuff Dim sizeof_classname As Integer = rawCmsRecord.Value(start_of_classname_struct) Dim oldclassname As String = "" For i2 = i + 1 To i + sizeof_classname oldclassname &= ChrW(rawCmsRecord.Value(i2)) Next oldclassname = Right(oldclassname, oldclassname.Length - oldNamespace.Length) Dim newFQCN As String = newNamespace & oldclassname offset = newFQCN.Length - sizeof_classname 'overwrite the new length newRecord(i) = CByte(newFQCN.Length) 'put the new classname on the end Dim ctr As Integer = 0 For ctr = 1 To newFQCN.Length newRecord(i + ctr) = CByte(AscW(newFQCN(ctr - 1))) Next i += sizeof_classname End If Next rawCmsRecord.Value = newRecord rawCmsRecord.Save()As the format is not documented by Microsoft the format may change without notice and as I have mentioned this is only a quick interpretation of what I saw, to get the job done. Before implementing this code for yourself, check it will not break your data, I am not responsible!
Using the built in .NET validation, you can assign a 'validation group' to a set of validation controls, submit buttons and validation summary.
This creates a separation between different sets of controls on the page, so that only the relevant controls are validated for a specific submit button.
When you design your application as separate user control components, you may want to set a different validation group on the validators of the user control, depending on the context.
To allow this, I have exposed a 'ValidationGroup' property on the user control. This subsequently makes a recursive to all its child controls to set the validation group of all the validators.
This way, you can set the validation group of the user control instance as you would any other control and it will assign all of its contained validators to that group.
Code:
Public Property ValidationGroup() As String Get Return CStr(ViewState("ValidationGroup")) End Get Set(ByVal value As String) SetValidationGroupOnChildren(Me, value) ViewState("ValidationGroup") = value End Set End Property Private Sub SetValidationGroupOnChildren(ByVal parent As Control, ByVal validationGroup As String) For Each ctrl As Control In parent.Controls If TypeOf ctrl Is BaseValidator Then CType(ctrl, BaseValidator).ValidationGroup = validationGroup ElseIf TypeOf ctrl Is IButtonControl Then CType(ctrl, IButtonControl).ValidationGroup = validationGroup ElseIf ctrl.HasControls() And ctrl.Visible = True Then SetValidationGroupOnChildren(ctrl, validationGroup) End If Next End Sub
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace YourSolutionNamespace.Test { [TestClass] public static class InitTestEnv { [AssemblyInitialize] public static void Initialize(TestContext context) { //configure the BLL BLL.Configuration.EnableCaching = false; //connect it to a datasource if (!BLL.DataProviders.HasValidDatasource()) BLL.DataProviders.SetDatasource(BLL.DataProviders.ProviderList.SqlDataProvider("connection string")); } } }Normally, I would reference the ConfigurationManager classes and then share my 'connectionStrings.config' and 'appSettings.config' files from my UI layer to keep the tests in sync with the settings that will be deployed, but in the above example I have left this out.