February 2008 - Posts
The DataContext object supports using the TransactionScope object, defined in the System.Transactions namespace. You can easily do the following in your code:
using (TransactionScope ts = new TransactionScope())
{
myContext.SubmitChanges();
ts.Complete();
}
If there is an issue, it's raised as an exception and the changes are not submitted in that case.
Whenever you need to figure out what's changed, use the DataContext.GetChangeSet method, which returns a ChangeSet object. This object has inserts, updates, and deletes collection of items that this action is being performed for. It's an object collection because its got every LINQ class that fits into that category lumped into one collection.
When trying to get a panel to display dynamically in conjunction with the HoverMenuExtender, there are some issues with displaying the content while forcing a scrollbar using the overflow-x and overflow-y CSS styles. As a personal recommendation, to implement a vertical scrollbar for a panel, set the width and height for the panel appropriately, and set overflow-x: hidden and overflow-y:scroll in your style. This is what it took to implement scrolling for one of my projects.
When getting references to DOM object in ASP.NET AJAX script, you can use the $get() mechanism. $get is a shortcut for document.getElementById, and it works well, but there is something you have to understand about ASP.NET server controls. For instance, if you have the control:
<asp:Button id="btn1" runat="server" Text="Click Me" />
In an ASP.NET page, the underlying button may render an HTML element with an ID of "btn1", but if this button is in a user control, or if there is a master page, controls have to create a client side unique name for the butto,which looks something like this:
ctl100$ContentPlaceholder$MyUserControl$btn1
Because the ID's can change, you shouldn't reference it this way. Instead, you can leverage the server-side ClientID property to get a reference in the control. In ASP.NET AJAX script, you can do this:
<script ..>
function pageLoad()
{
var element = $get('<%= btn1.ClientID %>');
}
</script>
This will return the longer ID notation to the $get method, getting the correct reference. However, in certain code you can't do this. For instance, I tried this approach in an onclick handler for a server control, like so:
<asp:CheckBox .. onclick="someHandler('<%= chk1.ClientID %>');" />
Onclick maps over to the client-side onclick, but does not input the ClientID, but renders the value directly. The approach I often take is to embed it on the server-side:
function Page_Load(..)
{
chk1.Attributes["onclick"] = "someHandler('" + chk1.ClientID + "');";
}
When working with WPF, there is an adjustment with how objects are created that you may not be used to in other environments. Suppose you have the following window:
<
Grid>
<StackPanel>
<WrapPanel Margin="5">
<ComboBox SelectionChanged="Combo_Selected" SelectedIndex="0">..</ComboBox>
<Button Click="Button_Click">Click Me</Button>
</WrapPanel></Grid>
This window processes in hierarchial order. First, the combo box is processed. Whenever the combo box is instantiated, the SelectionChanged event handler is fired when it sees that the selected index is zero. In some other contexts, setting the index on load does not raise the event, but in WPF it does. If your code accesses in the selection changed handler any other element below it, it will be a null reference.
To prevent this, check the IsInitialized property on the form, and if not initialized, exit the method as such:
if (!this.IsInitialized)
return;
That way, the initial firing of the event, when the window is initialized, is prevented. This is only needed when accessing other elements.
When working on the client side with AJAX controls and trying to set property values client-side, you may run into issues with asynchronous postbacks with the update panel. The reason is that this approach invokes the event lifecycle of the client-side of the page (init, load, etc.), which your scripts may reside in. If you want certain actions to avoid asychronous postbacks, then you can make use of the page request manager.
This class resides in the Sys.WebForms namespace, and makes use of the getInstance() static method to return an instance to the caller. This class manages the partial lifecycle of the page, and the get_isInAsyncPostBack property to determine whether the current postback is asynchronous. You can use this to prevent your code from doing anything for partial postbacks.
For the GridView, the ItemStyle and HeaderStyle classes have a Width property that you can set in the GridView class. However, if you run your application, you may see that it ignores the width that you specify. At first, I thought it was a bug, but as I read about it on the internet, it appears there is a deeper issue. It seems that the issue is a limitation of the HTML table itself, and how widths seemingly are ignored unless a width is specified on the table itself. I applied a width to the GridView control, and this resolved my issue, though I'm not certain that's the only factor involved.
I had been looking at doing something special with the tab control, so I was trying to learn some of the events in preparation for what I was doing, and am going to post some of what you can do with the tab controls and control extenders that are a part of the AJAX Control Toolkit. This particular control I was testing was the tab container, which fires a client-side active tab changed event. The tab container below uses a javascript event handler defined in a script, as we'll get to soon.
<
ajax:TabContainer ID="tc" runat="server" ActiveTabIndex="0" OnClientActiveTabChanged="Tab_SelectionChanged">
<ajax:TabPanel ID="t1" runat="server" HeaderText="Test1">
<ContentTemplate>
Some content
</ContentTemplate>
</ajax:TabPanel>
<ajax:TabPanel ID="t2" runat="server" HeaderText="Test2">
<ContentTemplate>
<asp:TextBox ID="txt" runat="server" />
</ContentTemplate></ajax:TabPanel>
</
ajax:TabContainer>
In order for this to work, a client-side script needs rendered, which can be added to the script manager. The script file has our client event handler as discussed above. This is the middle step, to register the script file.
<
asp:ScriptManager ID="asm" runat="server">
<Scripts>
<asp:ScriptReference Path="~/Scripts.js" />
</Scripts>
</asp:ScriptManager>
Lastly, the following script accesses the tab container. It simply outputs information about the control raising the event:
/// <reference name="MicrosoftAjax.js"/>
function Tab_SelectionChanged(sender,e)
{
var msg = "Header: " + sender.get_activeTab().get_headerText() + "\n";
msg +=
"Active Index: " + sender.get_activeTabIndex() + "\n";msg += "ID: " + sender.get_element().id + "\n";msg += "Page Url: " + sender.get_element().ownerDocument.location.href + "\n";
alert(msg);
}
The script above uses a sender property; this is a reference to the tabcontainer AJAX javascript class; note that you can access certain properties of it. For instance, the get_activeTab is defined on the AJAX javascript class, returning a reference to the AJAX TabPanel javascript class. The next property is the active tab index, returns a numeric value representing the index.
Third, each javascript class defines a get_element property that returns a reference to the underlying html element. The last one uses the ownerDocument property that gains reference to the underlying URL (this is not an add-on to javascript).
The following gets emitted:
Header: Test2
Active Index: 1
ID: tc
Page: http://localhost/ajaxexample/default.aspx
What you may not realize if you open up a converted VS 2008 project and get no intellisense is that the designer utilizes sort of a namespace inheritance approach. For instance, if you create a new AJAX client behavior javascript file, you will see this at the top:
/// <reference name="MicrosoftAjax.js"/>
This lets VS know to use the objects defined in this file, similar to how you use "using System.Web.UI.WebControls". There are a variety of options available, and if you look through the AJAX Control Toolkit code, you will find some different files you can import into your scripts.
A co-worker of mine was using LINQ to SQL and trying to write a query that connected across multiple data context objects connecting to different databases. However, this was an issue because LINQ doesn't allow this to happen. The LINQ object "knows" that it is part of a different data context, and a detailed message specifies that this isn't allowed to happen. So, what is the solution? One of the things we did was to pull back the first data source in a query, and call the ToList() option. We were pulling back one field anyway, so the ToList() option was a good option to separate the single value from the data context.
I was developing today and got a StackOverflowException, and I quickly realized what it was. One of the ways you get this is if you do "too much" in the constructor; that is, you can do limited things in the constructor. If you get this from something you are doing in the constructor, that means you can't do it.
I also get this if you get a circular reference, meaning that you have a property like this:
public string Text
{
get { return this.Text; }
}
This is a recursive, infinite loop when accessed, when you really meant to do this:
public string Text
{
get { return _text; }
}
So watch out for those things, and know that this exception may mean you have an infinite loop in your code.
When doing queries, sometimes the parameters are null. If you didn't know already, the way to handle null parameters in a select stored procedure is:
select * from Customers
where (@CustomerName is null or CustomerName = @CustomerName)
If the @CustomerName parameter is null when passed into the stored procedure, the first option hits, which means all the records are returned. Otherwise, actual processing on the name occurs. I first encountered this when looking at the ASP.NET profile stored procedures in the ASPNETDB.mdf file.
You can do something similar with LINQ. For instance, you can query as such:
var data = from c in this.Context.Customers
where (string.IsNullOrEmpty(customerName) || c.CustomerName == customerName)
select c;
Or, if you have a series of parameters that check certain parameter types, you can filter by:
var data = from c in this.Context.Customers
where (isCheckingForCustomerName && c.CustomerName == text)
|| (isCheckingForCity && c.City == text)
|| (isCheckingForState && c.State == text)
|| (isCheckingForAccountNumber && c.AccountNumber.ToString() == text)
select c;
So you have your options in LINQ to do what you want to do in SQL, without having to write a lot of if statements.
I was wondering if it was possible to change the AJAX tab container and its child TabPanel controls. The reason is; the white background conflicted with my color scheme. In doing some research, I found this: http://www.msdner.net/asp.net-archived/118/34-1022-1187396.shtm
See, the tabcontainer control defines some in-line styles that it uses, and it's possible to override them by specifying that particular name (like ajax__tab_body) as a child class such as this:
.MyStyle .ajax__tab_body
{
<!-- Some style -->
}
This MyStyle style can be applied to the tabcontainer, and the body will be overridden. I tried that, and overrode all of my styles, including the image tabs! There wasn't any header. So, I ended up scrapping it for now, but maybe some of this may make some sense to you. Maybe I set it up wrong, and I hope that maybe you can have better success than me.