I found a solution that works!
On the page I add a CustomValidator, like this:
<asp:CustomValidator id="vldLoginFailed" runat="server" ErrorMessage="Login failed. Please check your username and password." ValidationGroup="loginControl" Visible="false"></asp:CustomValidator>
I also have a ValidationSummary that looks like this:
<asp:ValidationSummary id="ValidationSummary" ValidationGroup="loginControl" runat="server" DisplayMode="BulletList" CssClass="validationSummary" HeaderText="Please check the following"></asp:ValidationSummary>
On my login control I add a method to OnLoginError, so it looks like this:
<asp:Login ID="loginControl" runat="server" VisibleWhenLoggedIn="false" OnLoginError="loginControl_LoginError">
In my codebehind I create a method that is triggered when there's a login error and it looks like this:
protected void loginControl_LoginError(object sender, EventArgs e)
{
CustomValidator vldLoginFailed = (CustomValidator)loginControl.FindControl("vldLoginFailed");
vldLoginFailed.IsValid = false;
}
So when there's a login error the method loginControl_LoginError will be called. It finds the CustomValidator and sets IsValid to false. Since the CustomValidator belongs to the validation group "loginControl" its error message will be displayed in the ValidationSummary.