If you have been working with WPF and have had issues with your ComboBox or any list control binding then you can stop pulling your hair out. The problem occurs when you try to bind the SelectedValue property of the control to a Nullable Int (int?). I am currently working with Microsoft to resolve the root problem, but here is a work around for now:
1. Create an IValueConveter:
public class NullIntToNegOneConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return -1;
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int i;
if (int.TryParse(value.ToString(), out i))
{
if (i == -1)
{
return null;
}
else
{
return value;
}
}
else
{
return value;
}
}
}
2. Add a -1 value to the list that your control is bound to:
Person nullPerson = new Person();
nullPerson.Id = -1;
nullPerson.Name = "None";
people.Insert(0, nullPerson);
3. Bind to the Selected Value using the Converter:
<ComboBox ItemsSource="{Binding Source={x:Static Application.Current},Path=People}"
SelectedValuePath="Id" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="False"
Margin="3,3,3,3" HorizontalAlignment="Right" FontSize="18"
SelectedValue="{Binding Path=PersonId, UpdateSourceTrigger=Explicit, Converter={StaticResource NullIntToNegOneConverter}}"/>
You should be all set. If your id is null then you get the -1 value that you inserted in the list because the convert converts the null value to -1. However, when you go to save it to the database, the convert also converts the -1 back to a null value, so you won’t have any -1 values in your database.
I have recently been working on a WPF touch screen application that doesn’t lend it self well to normal scroll bars. Luckly this is no problem with WPF because you have the ability to easily modify how a control is rendered. Below is a rough template that puts a button above and below scrollable content and when clicked they provide the same functionality as the default scroll bar.
<ControlTemplate TargetType="{x:Type ScrollViewer}"
x:Key="ButtonOnlyScrollViewer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
< SPAN>Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
< SPAN>Grid.ColumnDefinitions>
<RepeatButton Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch"
Content="ScrollUp"Command="ScrollBar.LineUpCommand"/>
<ScrollContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"
Grid.Column="0" ScrollViewer.VerticalScrollBarVisibility ="Auto"
Height="Auto" Margin="{TemplateBinding Margin}"/>
<RepeatButton Grid.Row="2" Grid.Column="0" Height="20"
Content="ScrollDown"
Command="ScrollBar.LineDownCommand"/>
< SPAN>Grid>
< SPAN>ControlTemplate>
Email me at justin.finch@parivedasolutions.com for a copy of the dll that contains the control used in the example.
Google recenlty introduce Google Checkout. “Google Checkout is a checkout process that you integrate with your website, enabling your customers to buy from you quickly and securely, using a single username and password. Once they do, you can use Google Checkout to charge their credit cards, process their orders, and receive payment in your bank account.”
They currently have no ASP.NET examples posted on their site so I have created an ASP.NET 2.0 Custom Web Control that will allow you to easily integrated with their service by simply dropping a control on a page and specifying a few parameters. So here it is…
Step 1: Modify the Web.Config file
Add the following entry to the web.config file. This will allow you to use the control on any page with out having to add the control directive to each page. Also, don’t for get to add the dll to the bin directory of your website.
<system.web>
<pages maintainScrollPositionOnPostBack="true">
<controls>
<add tagPrefix="Google" namespace="Google.Web.UI.WebControls"
assembly="Google.Web"/>
< SPAN>controls>
< SPAN>pages>
Step 2: Add the control to the page.
This is pretty simple, but just make sure you add it outside to the form tags on the page. Specify your MerchantId and MerchantKey that you received for Google and tell the control if it should use the sandbox (test) or the production service. You will need to register for both through Google.
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page< SPAN>title>
< SPAN>head>
<body>
<form id="form1" runat="server">
<div>
Place info here...
< SPAN>div>
< SPAN>form>
<Google:CheckoutButton MerchantId="101156456465"
MerchantKey="234lk4j53452lkj34" UseSandBox="true"
runat="server" ID="btnGoogleCheckout"/>
< SPAN>body>
< SPAN>html>
Step 3: Set the Shopping Cart XML Property
In the code behind for the page set the ShoppingCartXml property. For testing purpose I simply Desearlized Google's sample xml into a ShoppingCart object. The sample xml can be found on their site and here is a great article on how to create a C# object base on the XSD schema file provided by Google. http://www.netomatix.com/Products/Ecomm/GoogleCheckOut.aspx
protected void Page_Load(object sender, EventArgs e)
{
btnGoogleCheckout.CartXml = GetCartXML();
}
private string GetCartXML()
{
string xmlFile = "checkout-shopping-cart-SIMPLE.xml";
XmlDocument xml = new XmlDocument();
xml.Load(Server.MapPath(xmlFile));
return xml.InnerXml;
}
You are all set. Just click the button and you will see the shopping cart appear on the Google site. Let me know if you have any questions or problems with the control.
For more information visit the following link to the developers guide.
http://code.google.com/apis/checkout/developer/index.html
If you have ever had to display large a dataset on the web, you will soon realize they can bring your application to a halt. A solution to this problem is paging. Paging allows smaller results set to be returned and displayed on the client, which greatly improves performance.
Introduction
For this example I will user Pariveda’s custom Pagable/Sortable repeater control which will soon be included in the Pariveda.Web dll. I will demonstrate how to:
- Create a Stored Procedure that allows for Paging and Sorting
- Write a Business Controller to call the stored procedure
- Add the custom control to a web page
- Write code to hook into the controls events in order to call the Stored Procedure and return the proper result set
Create the Stored Procedure
I used the infamous Northwind database for this example. I will select Customer records based on the current page and sort information from the control.
First, you must declare parameters that will be needed for paging:
CREATE PROCEDURE dbo.Northwind_CustomersGetPaged
@startRow int
, @endRow int
, @sortColumn varchar(100)
, @sortDirection char
Next, either a Table Variable or Temp Table is needed to hold you queried results. In this example I have chosen to use a Table Variable. If you use a temp table make sure to drop it at the end of the procedure. The Row ID will be used to determine which rows (Page) of data to return. This is based on the information passed in from the control that I will discuss later.
DECLARE @results TABLE
(
rowId int identity
,CustomerID nchar(5)
,CompanyName nvarchar(40)
,Address nvarchar(60)
,City nvarchar(15)
,Phone nvarchar(24)
)
After we have declared the results table we must populate it with data based on the sort criteria received from the control. Notice the order by is a case statement. This is done in order to transform the @sortColumn and @sortDirection into a SQL order by statement.
INSERT INTO @results
(
CustomerID
,CompanyName
,Address
,City
,Phone
)
SELECT
CustomerID
,CompanyName
,Address
,City
,Phone
FROM
dbo.Customers
ORDER BY
CASE
WHEN @sortColumn = 'Customer ID' and @sortDirection = 'D' then CustomerID
WHEN @sortColumn ='Company Name' and @sortDirection = 'D' then CompanyName
WHEN @sortColumn ='Address' and @sortDirection = 'D' then Address
WHEN @sortColumn ='City' and @sortDirection = 'D' then City
WHEN @sortColumn ='Phone' and @sortDirection = 'D' then Phone
END DESC,
CASE
WHEN @sortColumn = 'Customer ID' and @sortDirection = 'A' then CustomerID
WHEN @sortColumn ='Company Name' and @sortDirection = 'A' then CompanyName
WHEN @sortColumn ='Address' and @sortDirection = 'A' then Address
WHEN @sortColumn ='City' and @sortDirection = 'A' then City
WHEN @sortColumn ='Phone' and @sortDirection = 'A' then Phone
END ASC,
CompanyName
Finally, 2 results sets will need to be returned; one with a total count (“Virtual Count”) of all records that meet our criteria, and another with only the rows for the current page of the control.
SELECT count(1) AS NumRows FROM @results
SELECT * FROM @results
WHERE rowID >= @startRow
AND rowID <= @endRow
Writing the Business Controller
Calling the procedure is pretty straight forward. For the example the stored procedure only accepts the Paging parameters, but you could add any additional parameters for your particular application. One quick note, I used Microsoft’s Application Blocks to access the database. The first results set returns the total number of customers and the next results set contains the specific customer data for the given page. Both result sets are read into a Customer Object which is returned to the UI where it will be bound to the paging control.
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using Microsoft.ApplicationBlocks.Data;
using PagedRepeaterExample.DataObject.Customer;
namespace PagedRepeater.Business
{ /// <summary>
/// Summary description for CustomerController.
/// </summary>
public class CustomerController
{private static readonly string CONNECTION_STRING =
ConfigurationSettings.AppSettings.Get("ConnectionString");
public CustomerController()
{ }
public static Customers GetCustomers(int startRow,
int endRow,
string sortColumn,
string sortDirection)
{ Customers customers = new Customers();
CustomerCollection customerColleciton = new CustomerCollection();
SqlParameter[] parameters = new SqlParameter[]
{ new SqlParameter("@startRow", startRow), new SqlParameter("@endRow", endRow), new SqlParameter("@sortColumn", sortColumn), new SqlParameter("@sortDirection", sortDirection) };
try
{using(SqlDataReader reader =
SqlHelper.ExecuteReader(CONNECTION_STRING,CommandType.StoredProcedure,
"NorthWind_CustomersGetPaged",parameters))
{ while(reader.Read())
{ customers.VirtualCount = (int)reader["NumRows"];
}
if(reader.NextResult())
{ while(reader.Read())
{ Customer customer = new Customer();
customer.CustomerId = reader["CustomerId"].ToString();
customer.CompanyName = reader["CompanyName"].ToString();
customer.City = reader["City"].ToString();
customer.Address = reader["Address"].ToString();
customer.Phone = reader["Phone"].ToString();
customerColleciton.Add(customer);
}
}
}
}
catch(Exception ex)
{ return null;
}
customers.CustomerCollection = customerColleciton;
return customers;
}
}
}
Add the Control
Adding the control to the page is similar to adding a regular repeater control. The first thing is to add a page directive for the Pariveda.Web dll.
<%Register TagPrefix="pvs" Assembly="Pariveda.Web" namespace="Pariveda.Web.UI.WebControls"%>
The next step is to add the custom control to the page. The “IsSortable” property of the repeater should be set to True and the “PageControllerContainerId” should be set to the id of the Place holder located in the header template. The Place holder is used to hold the paging controls and can be place in either the Header Template of the Footer Template. The SortLinkButton’s are used, of course, for sorting. The only thing to watch with these is to make sure that the SortType property matches the Order By clause in the stored procedure.
<form id="Form1" method="post" runat="server">
<table width="100%">
<pvs:PagedRepeater id="rptCustomers" Runat="server"
IsSortable="true"
PagerControllerContainerId="CustomerPagerControls">
<HEADERTEMPLATE>
<TR >
<TD colspan="5" align="right">
<asp:PlaceHolder id="CustomerPagerControls" Runat="server"></asp:PlaceHolder>
</TD>
</TR>
<TR bgcolor="Gainsboro">
<TD >
<pvs:SortLinkButton ID="Sortablelinkbutton1"
Runat="server"
SortType="Customer ID"
AscImage="images/ArrowAsc.gif"
DecImage="images/ArrowDec.gif">Customer ID</pvs:SortLinkButton>
</TD>
<TD>
<pvs:SortLinkButton ID="Sortablelinkbutton2"
Runat="server"
SortType="Company Name"
AscImage="images/ArrowAsc.gif"
DecImage="images/ArrowDec.gif">Company Name</pvs:SortLinkButton>
</TD>
<TD>
<pvs:SortLinkButton ID="Sortlinkbutton1"
Runat="server"
SortType="Address"
AscImage="images/ArrowAsc.gif"
DecImage="images/ArrowDec.gif">Address</pvs:SortLinkButton>
</TD>
<TD>
<pvs:SortLinkButton ID="Sortlinkbutton2"
Runat="server"
SortType="City"
AscImage="images/ArrowAsc.gif"
&n