July 2007 - Posts
I've been working with a complex site, a site for my church that also has many different ministries under the root. Rather than make each of these sites a virtual directory, I instead made everything a subfolder, but only have one virtual for the main site. This makes it easier with resources like the bin directory, themes, etc. I have one web.sitemap at the root, which has all of the links within. Each ministry section has a site map provider, like such:
<asp:SiteMapDataSource ID="smsSitemap" runat="server" ShowStartingNode="false" StartingNodeUrl="~/redirect.aspx?ministry=sb" />
The sitemap looks something like this:
<siteMapNode title="Ministry" url="~/redirect.aspx?ministry=name">
<siteMapNode title="1" url="page1.aspx" />
<siteMapNode title="2" url="page2.aspx" />
<siteMapNode title="3" url="page3.aspx" />
</siteMapNode>
Because I don't want to show the root page, but want all of the sublinks to show (otherwise, I believe all of the links would be children of the root menu item; I want each sub page (1,2,3) to be its own root item), I don't show the starting node. The starting node needs a url to start with. After playing around with it, I found that it would take query string parameters as part of that uniqueness. One way to ensure a unique value is using a redirect page.
Because I don't want to show the root page, but want all of the sublinks to show (otherwise, I believe all of the links would be children of the root menu item; I want each sub page (1,2,3) to be its own root item), I don't show the starting node. The starting node needs a url to start with. After playing around with it, I found that it would take query string parameters as part of that uniqueness. One way to ensure a unique value is using a redirect page.
Grids work a little differently than tables do, and I was surprised as how it worked. I think it's a better construct, but it can be a little more verbose than to a table-like counterpart. Let's look at it in more detail and you will see why.
A grid consists of rows and columns. To setup this, use the following definition:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
A Grid has RowDefinitions and ColumnDefinitions that specify the height (specific to the row), the width (specific to the column), and possibly other dimensional aspects. Normally, you would expect to see a combination of row/cell or row/column objects to lay out the grid; however, WPF works differently. Each control has a Grid.Row and Grid.Column, which specifies the row/column to render in.
For instance, a <TextBox Grid.Row="1" Grid.Column="1" /> Renders in row 1, column 1 of the grid. Multiple controls can render in a grid in this way. This may seem a little odd at first, but in actuality it is very practical to setup the grid this way. Controls must be within the grid to setup them in the grid though, so make sure they are setup as children.
Grids can also setup column/row spanning, as shown in the button at the bottom of the next complete example. This works like in HTML, with the colspan or rowspan attributes. A complete setup example is shown below:
<Label Grid.Row="0" Grid.Column="0" Width="50">Item 1:</Label>
<TextBox Name="Item1" Grid.Row="0" Grid.Column="1" Width="50" />
<Label Grid.Row="1" Grid.Column="0" Width="50">Item 2</Label>
<TextBox Name="Item2" Grid.Row="1" Grid.Column="1" Width="50" />
<Button Name="ConnectButton" Content="Connect" Grid.ColumnSpan="2" Grid.Row="2" Click="OnConnectButtonClick" />
And within this, we have a form within the grid. It's that simple; however, it gets confusing determining whether a 1 goes here, or a 0 goes there, and so setting it up takes a bit of thought. In addition, you can see how someone used to HTML could be a little taken aback by this new approach; however, I believe it is an overall improvement.
The popup control is a new primitive control in the WPF framework. It allows a container control within the popup to suddenly appear when some action is taken (such as a button click). For instance, below is an instance of a popup:
<Popup x:Name="ConnectPopup" Grid.Row="0" PopupAnimation="Fade" PlacementTarget="{Binding ElementName=TabLayout}" Placement="Bottom" PlacementRectangle="20,25,40,30">
<Grid />
</Popup>
Within the popup control can be a container control, such as a grid, with whatever content within that grid control it so chooses. The popup can also control the animation used to show/hide itself. To show/hide a popup, use the following code:
this.ConnectPopup.IsOpen = true;
this.ConnectPopup.StaysOpen = false;
IsOpen determines whether a popup is displayed, and by setting this value to true it displays it. StaysOpen determines whether it can be closed by some means.
Notice one other thing in the markup above. Placement determines the overall placement of the popup, and Bottom means at the bottom of the control that is in reference via the PlacementTarget. So what it is saying is place it in relativity to the TabLayout control, which is the placement target. See this for more information about how all of this works with a placement rectangle: http://msdn2.microsoft.com/en-us/library/system.windows.controls.primitives.popup.placementtarget.aspx
I'm running XP, VS 2005, and have SQL Server 2005 installed, specifically SQL Server Express database in a test project that I am using. I keep running into the problem where I get build errors because it can't copy the database over. I delete the reference in VS, I ensure SQL Server doesn't have an instance running, pretty much trying to kill any reference that I know about it. But, no matter what, I still get that error.
To free that up, what I've been doing is going into task manager, and looking for the sqlserver.exe process that is running under my user account, not the system account ones of course. Once I do that, it builds successfully. Even longer builds that took a while run much, much faster leading me to think that I really didn't realize that it may be a problem for a few things like that.
Hopefully, this helps someone else out.
I've been trying to figure out how I could work in both ASP.NET and Unit Testing environments. For instance, if the user clicks a Register button, that raises a click event. But in a unit testing environment, this doesn't work like this. So what could replace it? It may be possible through reflection, by using Reflection to invoke the method that would raise the event. Another alternative, one that could work in an API, is to have a PerformClick method in the user control class, which could work like this:
public abstract class ClickableUserControlBase : System.Web.UI.UserControl
{
public event EventHandler Click;
protected virtual void OnClick(EventArgs e)
{
if (Click != null)
Click(this, e);
}
protected override void OnLoad(EventArgs e)
{
//Attach to the button click event
}
public void PerformClick()
{
this.OnClick(EventArgs.Empty);
}
void Button_Click(object sender, EventArgs e)
{
this.PerformClick();
}
}
This way, it is possible to call the button click through code. Note that this approach is a more MVC-based approach, where this base class is defined in a class library, and not directly in a web project. Now, there are two ways to perform a click, one through an API call, and one through an actual button click.
This can work with selections as well; a custom page/user control class can have a means to Select or Deselect rows in a data source or a table. The selected item could be passed through an event argument, making it easy to work with selected items.
I thought that wrapping text in a datagridview would be a problem, but it turned out to be really simple. All you have to do is to change a setting in the DefaultCellStyle. This property represents a style of the cell, and has a WrapMode object, which by setting to true, means that text within the cell will automatically wrap according to the width specified. Pretty easy.
Despite what some may believe, you can work with ASP.NET in a unit-testing environment, though it is somewhat limited. Certain objects that ASP.NET is able to utilize aren't available when unit-testing. This is because a series of modules defined in the <httpModules> element in the master web.config setup these objects, which do not run during unit-testing.
How can this be countered? Certain objects, like viewstate, and certain properties, like Page.IsPostback, work well in the unit-testing environment. However, certain collections, like Session or Cache, do not. To get around this, it is better to avoid it, or to use an alternative caching method like Enterprise Library's caching block. In addition, you could create a method within a custom page class that reads/writes from the session/cache, which when in a unit testing environment, reads/writes to a local dictionary. It could look like this:
public abstract class PageBase : Page
{
protected void SetSessionValue(string key, object value)
{
if (HttpContext.Current != null)
Session[key] = value;
else
{
if (this.LocalSession.ContainsKey(key))
this.LocalSession[key] = value;
else
this.LocalSession.Add(key, value);
}
}
}
LocalSession is a localized Dictionary<string, object> collection that works in the unit testing environment, and survives postbacks because there are no postbacks in unit testing, which is a challenge. More on this later.
I was trying to figure out how to pass a null into a queried parameter, which renders as a drop down. The only solution I found, after looking around a bit, was from a forum post I made:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1789337&SiteID=1. The solution is to create a query that includes a null parameter, and a default message. I created a series of queries that included two values, one for the display and one for the underlying value. I did this even when the display/value were the same.
I was trying to display a message in my report, that when no records were found from the underlying query, a message would be displayed like "no records found" or something like that. I couldn't find the answer until I came across a forum post, and so I include it in case it helps someone else: https://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1659610&SiteID=1