|
| 1 | +# PageService |
| 2 | + |
| 3 | +## Background |
| 4 | + |
| 5 | +In ASP.NET Web Forms, the `Page` object provided a central place for page-level properties and functionality. Key properties included `Page.Title`, `Page.MetaDescription`, and `Page.MetaKeywords`, which allowed developers to programmatically set page metadata that appears in the browser and search engine results. |
| 6 | + |
| 7 | +```csharp |
| 8 | +// Web Forms code-behind |
| 9 | +protected void Page_Load(object sender, EventArgs e) |
| 10 | +{ |
| 11 | + Page.Title = "My Dynamic Page Title"; |
| 12 | + Page.MetaDescription = "Description for search engines"; |
| 13 | + Page.MetaKeywords = "keyword1, keyword2, keyword3"; |
| 14 | +} |
| 15 | + |
| 16 | +protected void UpdateButton_Click(object sender, EventArgs e) |
| 17 | +{ |
| 18 | + Page.Title = "Title Updated - " + DateTime.Now.ToString(); |
| 19 | + Page.MetaDescription = GetDescriptionFromDatabase(); |
| 20 | +} |
| 21 | +``` |
| 22 | + |
| 23 | +This pattern was essential for: |
| 24 | +- Setting page titles dynamically based on data or user actions |
| 25 | +- Implementing SEO-friendly titles and descriptions for content pages |
| 26 | +- Providing context-specific metadata in master page scenarios |
| 27 | +- Improving search engine visibility and social media sharing |
| 28 | + |
| 29 | +## Web Forms Usage |
| 30 | + |
| 31 | +In Web Forms, the `Page` object was automatically available in all code-behind files: |
| 32 | + |
| 33 | +```aspx |
| 34 | +<%@ Page Language="C#" Title="Static Title" |
| 35 | + MetaDescription="Page description" |
| 36 | + MetaKeywords="keyword1, keyword2" %> |
| 37 | +``` |
| 38 | + |
| 39 | +```csharp |
| 40 | +// Code-behind (.aspx.cs) |
| 41 | +public partial class MyPage : System.Web.UI.Page |
| 42 | +{ |
| 43 | + protected void Page_Load(object sender, EventArgs e) |
| 44 | + { |
| 45 | + // Dynamically set page metadata |
| 46 | + Page.Title = GetTitleFromDatabase(); |
| 47 | + Page.MetaDescription = GetDescriptionFromDatabase(); |
| 48 | + Page.MetaKeywords = GetKeywordsFromDatabase(); |
| 49 | + } |
| 50 | + |
| 51 | + private string GetTitleFromDatabase() |
| 52 | + { |
| 53 | + // Fetch from database |
| 54 | + return "Dynamic Title from DB"; |
| 55 | + } |
| 56 | + |
| 57 | + private string GetDescriptionFromDatabase() |
| 58 | + { |
| 59 | + return "This is a dynamic description for SEO"; |
| 60 | + } |
| 61 | + |
| 62 | + private string GetKeywordsFromDatabase() |
| 63 | + { |
| 64 | + return "blazor, webforms, migration, seo"; |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +These properties would automatically update the HTML `<title>` and `<meta>` elements in the rendered page. |
| 70 | + |
| 71 | +## Blazor Implementation |
| 72 | + |
| 73 | +BlazorWebFormsComponents provides `IPageService` and `PageService` to replicate this functionality in Blazor. The service is registered as a scoped service (one instance per request/render cycle) and can be injected into any component or page. |
| 74 | + |
| 75 | +### Key Components |
| 76 | + |
| 77 | +1. **IPageService Interface** - Defines the contract for page-level services |
| 78 | +2. **PageService Class** - Default implementation providing `Title`, `MetaDescription`, and `MetaKeywords` properties |
| 79 | +3. **Page Component** - Blazor component that renders the dynamic `<PageTitle>` and `<meta>` tags |
| 80 | + |
| 81 | +### Registration |
| 82 | + |
| 83 | +The service is automatically registered when you call `AddBlazorWebFormsComponents()`: |
| 84 | + |
| 85 | +```csharp |
| 86 | +// Program.cs |
| 87 | +builder.Services.AddBlazorWebFormsComponents(); |
| 88 | +``` |
| 89 | + |
| 90 | +This registers `IPageService` as a scoped service that can be injected into components. |
| 91 | + |
| 92 | +### Usage in Blazor |
| 93 | + |
| 94 | +**Step 1: Add the Page component to your page or layout** |
| 95 | + |
| 96 | +```razor |
| 97 | +@inject IPageService Page |
| 98 | +
|
| 99 | +<BlazorWebFormsComponents.Page /> @* This renders the dynamic <PageTitle> and meta tags *@ |
| 100 | +``` |
| 101 | + |
| 102 | +**Step 2: Set page properties programmatically** |
| 103 | + |
| 104 | +```razor |
| 105 | +@code { |
| 106 | + protected override void OnInitialized() |
| 107 | + { |
| 108 | + Page.Title = "My Dynamic Page Title"; |
| 109 | + Page.MetaDescription = "Description for search engines"; |
| 110 | + Page.MetaKeywords = "blazor, webforms, migration"; |
| 111 | + } |
| 112 | + |
| 113 | + private void UpdateMetadata() |
| 114 | + { |
| 115 | + Page.Title = "Updated Title - " + DateTime.Now.ToString(); |
| 116 | + Page.MetaDescription = "Updated description based on user action"; |
| 117 | + } |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +### Complete Example |
| 122 | + |
| 123 | +```razor |
| 124 | +@page "/MyPage" |
| 125 | +@inject IPageService Page |
| 126 | +
|
| 127 | +<BlazorWebFormsComponents.Page /> |
| 128 | +
|
| 129 | +<h1>My Page</h1> |
| 130 | +
|
| 131 | +<div> |
| 132 | + <label>New Title:</label> |
| 133 | + <input @bind="newTitle" /> |
| 134 | + <label>New Description:</label> |
| 135 | + <textarea @bind="newDescription"></textarea> |
| 136 | + <button @onclick="UpdatePageMetadata">Update Metadata</button> |
| 137 | +</div> |
| 138 | +
|
| 139 | +@code { |
| 140 | + private string newTitle = ""; |
| 141 | + private string newDescription = ""; |
| 142 | + |
| 143 | + protected override void OnInitialized() |
| 144 | + { |
| 145 | + Page.Title = "My Page - BlazorWebFormsComponents"; |
| 146 | + Page.MetaDescription = "A sample page demonstrating PageService"; |
| 147 | + Page.MetaKeywords = "blazor, sample, demo"; |
| 148 | + } |
| 149 | + |
| 150 | + private void UpdatePageMetadata() |
| 151 | + { |
| 152 | + if (!string.IsNullOrWhiteSpace(newTitle)) |
| 153 | + { |
| 154 | + Page.Title = newTitle; |
| 155 | + Page.MetaDescription = newDescription; |
| 156 | + } |
| 157 | + } |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +## Migration Path |
| 162 | + |
| 163 | +### Before (Web Forms) |
| 164 | + |
| 165 | +```aspx |
| 166 | +<%@ Page Language="C#" MasterPageFile="~/Site.Master" |
| 167 | + MetaDescription="Customer details page" |
| 168 | + MetaKeywords="customer, details, crm" %> |
| 169 | +
|
| 170 | +<script runat="server"> |
| 171 | + protected void Page_Load(object sender, EventArgs e) |
| 172 | + { |
| 173 | + var customerName = GetCustomerName(); |
| 174 | + Page.Title = "Customer Details - " + customerName; |
| 175 | + Page.MetaDescription = $"View details for {customerName}"; |
| 176 | + } |
| 177 | +</script> |
| 178 | +``` |
| 179 | + |
| 180 | +### After (Blazor) |
| 181 | + |
| 182 | +```razor |
| 183 | +@page "/customer/{id:int}" |
| 184 | +@inject IPageService Page |
| 185 | +
|
| 186 | +<BlazorWebFormsComponents.Page /> |
| 187 | +
|
| 188 | +<h1>Customer Details</h1> |
| 189 | +
|
| 190 | +@code { |
| 191 | + [Parameter] |
| 192 | + public int Id { get; set; } |
| 193 | + |
| 194 | + protected override async Task OnInitializedAsync() |
| 195 | + { |
| 196 | + var customerName = await GetCustomerName(Id); |
| 197 | + Page.Title = $"Customer Details - {customerName}"; |
| 198 | + Page.MetaDescription = $"View detailed information for {customerName}"; |
| 199 | + Page.MetaKeywords = "customer, details, crm"; |
| 200 | + } |
| 201 | +} |
| 202 | +``` |
| 203 | + |
| 204 | +## Features |
| 205 | + |
| 206 | +### Title Property |
| 207 | + |
| 208 | +- **Get/Set**: Read and write the page title dynamically |
| 209 | +- **Event-Driven**: `TitleChanged` event fires when title is updated |
| 210 | +- **Reactive**: The `Page` component automatically updates the browser title when the property changes |
| 211 | + |
| 212 | +### MetaDescription Property |
| 213 | + |
| 214 | +- **Get/Set**: Read and write the meta description dynamically |
| 215 | +- **SEO-Friendly**: Appears in search engine results (recommended 150-160 characters) |
| 216 | +- **Event-Driven**: `MetaDescriptionChanged` event fires when description is updated |
| 217 | +- **Reactive**: The `Page` component automatically updates the meta tag when the property changes |
| 218 | + |
| 219 | +### MetaKeywords Property |
| 220 | + |
| 221 | +- **Get/Set**: Read and write the meta keywords dynamically |
| 222 | +- **SEO Support**: Helps categorize page content for search engines |
| 223 | +- **Event-Driven**: `MetaKeywordsChanged` event fires when keywords are updated |
| 224 | +- **Reactive**: The `Page` component automatically updates the meta tag when the property changes |
| 225 | + |
| 226 | +### Future Extensibility |
| 227 | + |
| 228 | +The `IPageService` interface can be extended in future versions to support additional `Page` object features: |
| 229 | + |
| 230 | +- Open Graph meta tags for social media |
| 231 | +- Page-level client script registration |
| 232 | +- Page-level CSS registration |
| 233 | +- Custom meta tags |
| 234 | + |
| 235 | +## Key Differences from Web Forms |
| 236 | + |
| 237 | +| Web Forms | Blazor with PageService | Notes | |
| 238 | +|-----------|------------------------|-------| |
| 239 | +| `Page.Title` property | `Page.Title` property | Same API! Just inject `IPageService` as `Page` | |
| 240 | +| `Page.MetaDescription` property | `Page.MetaDescription` property | Available in Web Forms .NET 4.0+ | |
| 241 | +| `Page.MetaKeywords` property | `Page.MetaKeywords` property | Available in Web Forms .NET 4.0+ | |
| 242 | +| Available automatically | Must inject `IPageService` as `Page` | Standard Blazor DI pattern | |
| 243 | +| Synchronous | Synchronous | No change needed | |
| 244 | +| Scoped to request | Scoped to render cycle | Similar lifecycle | |
| 245 | + |
| 246 | +## Moving On |
| 247 | + |
| 248 | +While `PageService` provides familiar Web Forms compatibility, consider these Blazor-native approaches: |
| 249 | + |
| 250 | +### For Static Metadata |
| 251 | + |
| 252 | +Use the built-in components directly: |
| 253 | + |
| 254 | +```razor |
| 255 | +@page "/about" |
| 256 | +
|
| 257 | +<PageTitle>About Us - My Company</PageTitle> |
| 258 | +<HeadContent> |
| 259 | + <meta name="description" content="Learn about our company" /> |
| 260 | + <meta name="keywords" content="about, company, team" /> |
| 261 | +</HeadContent> |
| 262 | +
|
| 263 | +<h1>About Us</h1> |
| 264 | +``` |
| 265 | + |
| 266 | +### For Dynamic Metadata |
| 267 | + |
| 268 | +The `PageService` approach is appropriate when: |
| 269 | +- Metadata depends on data loaded asynchronously |
| 270 | +- Metadata changes based on user actions |
| 271 | +- Metadata is set in response to events |
| 272 | +- You want Web Forms-style programmatic control |
| 273 | + |
| 274 | +For simpler scenarios, you can use built-in components with bound variables: |
| 275 | + |
| 276 | +```razor |
| 277 | +<PageTitle>@currentTitle</PageTitle> |
| 278 | +<HeadContent> |
| 279 | + <meta name="description" content="@currentDescription" /> |
| 280 | +</HeadContent> |
| 281 | +
|
| 282 | +@code { |
| 283 | + private string currentTitle = "Default Title"; |
| 284 | + private string currentDescription = "Default description"; |
| 285 | + |
| 286 | + private void UpdateTitle(string newTitle) |
| 287 | + { |
| 288 | + currentTitle = newTitle; |
| 289 | + } |
| 290 | +} |
| 291 | +``` |
| 292 | + |
| 293 | +## Best Practices |
| 294 | + |
| 295 | +1. **Set Title Early**: Set the title in `OnInitializedAsync` or `OnParametersSet` to ensure it's available before first render |
| 296 | +2. **SEO Considerations**: Provide meaningful, descriptive titles for better search engine optimization |
| 297 | +3. **User Context**: Include relevant context in the title (e.g., customer name, product name) |
| 298 | +4. **Length**: Keep titles under 60 characters for optimal display in browser tabs and search results |
| 299 | +5. **Consistent Pattern**: Use a consistent title format across your application (e.g., "Page Name - Site Name") |
| 300 | + |
| 301 | +## See Also |
| 302 | + |
| 303 | +- [Live Sample](https://blazorwebformscomponents.azurewebsites.net/UtilityFeatures/PageService) |
| 304 | +- [Microsoft Docs: Page.Title Property](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.page.title?view=netframework-4.8) |
| 305 | +- [Blazor PageTitle Component](https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing#page-title) |
0 commit comments