Skip to content

Commit 9fc6dd9

Browse files
Copilotcsharpfritz
andauthored
Add DataBinding support to AdRotator component (#317)
* Initial plan * Update AdRotator to support DataBinding features (DataSource, DataMember, OnDataBound) Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> * Add DataBinding tests, documentation, and samples for AdRotator Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> * Fix AdRotator sample page to resolve Playwright integration test failures Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com>
1 parent d417520 commit 9fc6dd9

6 files changed

Lines changed: 436 additions & 14 deletions

File tree

docs/EditorControls/AdRotator.md

Lines changed: 160 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,166 @@
1-
The AdRotator component is meant to emulate the `asp:Rotator` control in markup and is defined in the [System.Web.UI.WebControls.AdRotator class](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.adrotator?view=netframework-4.8)
1+
The AdRotator component is meant to emulate the `asp:AdRotator` control in markup and is defined in the [System.Web.UI.WebControls.AdRotator class](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.adrotator?view=netframework-4.8)
22

33
## Blazor Features Supported
44

5-
- `AdvertismetFile` the path to an XML file that contains advertisement information.
6-
- `Target` the name of the browser window or frame that displays the contents of the Web page linked to when the AdRotator component is clicked.
5+
### XML File-Based Advertisement
6+
- `AdvertisementFile` - The path to an XML file that contains advertisement information.
7+
- `Target` - The name of the browser window or frame that displays the contents of the Web page linked to when the AdRotator component is clicked.
8+
- `KeywordFilter` - Filter advertisements by keyword.
9+
- `AlternateTextField` - The field name in the data source that provides alternate text for the ad image (default: "AlternateText").
10+
- `ImageUrlField` - The field name in the data source that provides the image URL (default: "ImageUrl").
11+
- `NavigateUrlField` - The field name in the data source that provides the navigation URL (default: "NavigateUrl").
12+
13+
### Data Binding
14+
- `DataSource` - Bind to a collection of `Advertisment` objects programmatically. When using DataSource, you can provide a `List<Advertisment>` or other collection types.
15+
- `DataMember` - Specifies the data table within a DataSet to bind to when using complex data sources.
16+
- `OnDataBound` - Event fired after the data has been bound to the control.
17+
18+
### Events
19+
- `OnAdCreated` - Event fired when an advertisement is created, allowing you to modify ad properties before rendering.
20+
21+
### Styling
22+
- Standard Web Forms styling properties: `BackColor`, `ForeColor`, `BorderColor`, `BorderStyle`, `BorderWidth`, `CssClass`, `Font`, `Height`, `Width`.
23+
24+
## Usage Examples
25+
26+
### Using XML Advertisement File
27+
28+
```html
29+
<AdRotator AdvertisementFile="Ads.xml" Target="_blank" />
30+
```
31+
32+
XML File Format (Ads.xml):
33+
```xml
34+
<Advertisements>
35+
<Ad>
36+
<ImageUrl>~/images/ad1.jpg</ImageUrl>
37+
<NavigateUrl>http://www.example1.com</NavigateUrl>
38+
<AlternateText>Visit Example 1</AlternateText>
39+
<Width>468</Width>
40+
<Height>60</Height>
41+
<Keyword>technology</Keyword>
42+
<Impressions>80</Impressions>
43+
</Ad>
44+
<Ad>
45+
<ImageUrl>~/images/ad2.jpg</ImageUrl>
46+
<NavigateUrl>http://www.example2.com</NavigateUrl>
47+
<AlternateText>Visit Example 2</AlternateText>
48+
<Width>468</Width>
49+
<Height>60</Height>
50+
<Keyword>sports</Keyword>
51+
<Impressions>20</Impressions>
52+
</Ad>
53+
</Advertisements>
54+
```
55+
56+
### Using DataSource with List of Advertisements
57+
58+
```razor
59+
@code {
60+
private List<Advertisment> _ads = new()
61+
{
62+
new Advertisment
63+
{
64+
ImageUrl = "/images/tech-ad.jpg",
65+
NavigateUrl = "http://www.techsite.com",
66+
AlternateText = "Visit Tech Site",
67+
Width = "468",
68+
Height = "60",
69+
Keyword = "technology"
70+
},
71+
new Advertisment
72+
{
73+
ImageUrl = "/images/sports-ad.jpg",
74+
NavigateUrl = "http://www.sportssite.com",
75+
AlternateText = "Visit Sports Site",
76+
Width = "468",
77+
Height = "60",
78+
Keyword = "sports"
79+
}
80+
};
81+
}
82+
83+
<AdRotator DataSource="@_ads" Target="_blank" />
84+
```
85+
86+
### Using KeywordFilter
87+
88+
Filter advertisements by keyword:
89+
90+
```html
91+
<AdRotator AdvertisementFile="Ads.xml" KeywordFilter="technology" Target="_blank" />
92+
```
93+
94+
Or with DataSource:
95+
96+
```html
97+
<AdRotator DataSource="@_ads" KeywordFilter="technology" Target="_blank" />
98+
```
99+
100+
### Handling OnAdCreated Event
101+
102+
Modify advertisement properties before rendering:
103+
104+
```razor
105+
<AdRotator AdvertisementFile="Ads.xml" OnAdCreated="HandleAdCreated" />
106+
107+
@code {
108+
private void HandleAdCreated(AdCreatedEventArgs e)
109+
{
110+
// Modify the ad before it's rendered
111+
e.AlternateText = $"Special Offer: {e.AlternateText}";
112+
113+
// Access all ad properties
114+
var imageUrl = e.ImageUrl;
115+
var navigateUrl = e.NavigateUrl;
116+
117+
// Access additional properties via AdProperties dictionary
118+
var keyword = e.AdProperties["Keyword"];
119+
}
120+
}
121+
```
122+
123+
### Handling OnDataBound Event
124+
125+
Execute code after data binding:
126+
127+
```razor
128+
<AdRotator DataSource="@_ads" OnDataBound="HandleDataBound" />
129+
130+
@code {
131+
private void HandleDataBound(EventArgs e)
132+
{
133+
// Perform actions after data is bound
134+
Console.WriteLine("AdRotator data bound successfully");
135+
}
136+
}
137+
```
138+
139+
### Using with Styling Properties
140+
141+
```html
142+
<AdRotator
143+
AdvertisementFile="Ads.xml"
144+
Target="_blank"
145+
CssClass="ad-banner"
146+
BorderWidth="1px"
147+
BorderColor="#cccccc"
148+
BorderStyle="Solid" />
149+
```
150+
151+
## Notes
152+
153+
- When both `DataSource` and `AdvertisementFile` are specified, `DataSource` takes priority.
154+
- The AdRotator randomly selects an advertisement from the available pool each time it renders.
155+
- The `Impressions` field in the XML affects the probability of an ad being shown (ads with higher impression values appear more frequently).
156+
- Custom field names can be specified using `AlternateTextField`, `ImageUrlField`, and `NavigateUrlField` properties.
157+
158+
## Web Forms Features NOT Supported
159+
160+
- `DataSourceID` - DataSource controls are not used in Blazor. Use the `DataSource` property instead to bind data programmatically.
161+
- Lifecycle events like `OnInit`, `OnLoad`, `OnPreRender`, `OnUnload` - These are part of the Web Forms page lifecycle and don't apply to Blazor components.
162+
- `EnableTheming` / `SkinID` - Blazor uses CSS for theming.
163+
- `EnableViewState` - Blazor doesn't use ViewState.
7164

8165
## Web Forms Declarative Syntax
9166

samples/AfterBlazorServerSide/Components/Pages/ControlSamples/AdRotator/Index.razor

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,58 @@
1010
<code>
1111
&lt;AdRotator AdvertisementFile="Ads.xml" Target="_top"&gt;&lt;/AdRotator&gt;
1212
</code>
13+
14+
@* DataSource examples commented out - uncomment to see DataBinding examples
15+
<hr />
16+
17+
<h3>Using DataSource with List of Advertisements</h3>
18+
<AdRotator DataSource="@_advertisements" Target="_blank" CssClass="data-source-ad" />
19+
20+
<hr />
21+
22+
<h3>Using OnAdCreated Event</h3>
23+
<AdRotator DataSource="@_advertisements" Target="_blank" OnAdCreated="HandleAdCreated" />
24+
<p>Last ad alternate text: <strong>@_lastAdText</strong></p>
25+
26+
@code {
27+
private List<Advertisment> _advertisements = new()
28+
{
29+
new Advertisment
30+
{
31+
ImageUrl = "https://via.placeholder.com/468x60/0000FF/FFFFFF?text=Ad+1",
32+
NavigateUrl = "https://www.example1.com",
33+
AlternateText = "Advertisement 1",
34+
Width = "468",
35+
Height = "60",
36+
Keyword = "example"
37+
},
38+
new Advertisment
39+
{
40+
ImageUrl = "https://via.placeholder.com/468x60/FF0000/FFFFFF?text=Ad+2",
41+
NavigateUrl = "https://www.example2.com",
42+
AlternateText = "Advertisement 2",
43+
Width = "468",
44+
Height = "60",
45+
Keyword = "example"
46+
},
47+
new Advertisment
48+
{
49+
ImageUrl = "https://via.placeholder.com/468x60/00FF00/FFFFFF?text=Ad+3",
50+
NavigateUrl = "https://www.example3.com",
51+
AlternateText = "Advertisement 3",
52+
Width = "468",
53+
Height = "60",
54+
Keyword = "example"
55+
}
56+
};
57+
58+
private string _lastAdText = "";
59+
60+
private void HandleAdCreated(AdCreatedEventArgs e)
61+
{
62+
_lastAdText = e.AlternateText;
63+
e.AlternateText = "Modified: " + e.AlternateText;
64+
}
65+
}
66+
*@
67+
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
@using System.Collections.Generic
2+
3+
@code {
4+
[Fact]
5+
public void AdRotator_DataSource_RendersFromList()
6+
{
7+
var ads = new List<Advertisment>
8+
{
9+
new Advertisment
10+
{
11+
ImageUrl = "test1.png",
12+
NavigateUrl = "http://www.test1.com",
13+
AlternateText = "Test Ad 1",
14+
Width = "100",
15+
Height = "50"
16+
},
17+
new Advertisment
18+
{
19+
ImageUrl = "test2.png",
20+
NavigateUrl = "http://www.test2.com",
21+
AlternateText = "Test Ad 2",
22+
Width = "100",
23+
Height = "50"
24+
}
25+
};
26+
27+
var cut = Render(@<AdRotator DataSource="@ads" Target="_blank" />);
28+
29+
// Should render one of the ads
30+
var link = cut.Find("a");
31+
link.ShouldNotBeNull();
32+
link.GetAttribute("target").ShouldBe("_blank");
33+
34+
var img = cut.Find("img");
35+
img.ShouldNotBeNull();
36+
img.GetAttribute("width").ShouldBe("100");
37+
img.GetAttribute("height").ShouldBe("50");
38+
39+
// Verify it's one of our ads
40+
var imgSrc = img.GetAttribute("src");
41+
(imgSrc == "test1.png" || imgSrc == "test2.png").ShouldBeTrue();
42+
}
43+
44+
[Fact]
45+
public void AdRotator_DataSource_WithKeywordFilter()
46+
{
47+
var ads = new List<Advertisment>
48+
{
49+
new Advertisment
50+
{
51+
ImageUrl = "tech.png",
52+
NavigateUrl = "http://www.tech.com",
53+
AlternateText = "Tech Ad",
54+
Keyword = "technology",
55+
Width = "100",
56+
Height = "50"
57+
},
58+
new Advertisment
59+
{
60+
ImageUrl = "sport.png",
61+
NavigateUrl = "http://www.sport.com",
62+
AlternateText = "Sport Ad",
63+
Keyword = "sports",
64+
Width = "100",
65+
Height = "50"
66+
}
67+
};
68+
69+
var cut = Render(@<AdRotator DataSource="@ads" KeywordFilter="technology" Target="_blank" />);
70+
71+
var img = cut.Find("img");
72+
img.GetAttribute("src").ShouldBe("tech.png");
73+
img.GetAttribute("alt").ShouldBe("Tech Ad");
74+
}
75+
76+
[Fact]
77+
public void AdRotator_DataSource_EmptyList_RendersNothing()
78+
{
79+
var ads = new List<Advertisment>();
80+
81+
var cut = Render(@<AdRotator DataSource="@ads" Target="_blank" />);
82+
83+
cut.Markup.Trim().ShouldBeEmpty();
84+
}
85+
86+
[Fact]
87+
public void AdRotator_DataSource_OnDataBound_EventIsFired()
88+
{
89+
var ads = new List<Advertisment>
90+
{
91+
new Advertisment
92+
{
93+
ImageUrl = "test.png",
94+
NavigateUrl = "http://www.test.com",
95+
AlternateText = "Test Ad",
96+
Width = "100",
97+
Height = "50"
98+
}
99+
};
100+
101+
var dataBoundFired = false;
102+
103+
var cut = Render(@<AdRotator DataSource="@ads" OnDataBound="() => dataBoundFired = true" />);
104+
105+
dataBoundFired.ShouldBeTrue();
106+
}
107+
108+
[Fact]
109+
public void AdRotator_DataSource_WithCustomFields()
110+
{
111+
var ads = new List<Advertisment>
112+
{
113+
new Advertisment
114+
{
115+
ImageUrl = "custom.png",
116+
NavigateUrl = "http://www.custom.com",
117+
AlternateText = "Custom Ad",
118+
Width = "200",
119+
Height = "100"
120+
}
121+
};
122+
123+
var cut = Render(@<AdRotator DataSource="@ads"
124+
AlternateTextField="AlternateText"
125+
ImageUrlField="ImageUrl"
126+
NavigateUrlField="NavigateUrl"
127+
Target="_self" />);
128+
129+
var img = cut.Find("img");
130+
img.GetAttribute("src").ShouldBe("custom.png");
131+
img.GetAttribute("alt").ShouldBe("Custom Ad");
132+
img.GetAttribute("width").ShouldBe("200");
133+
img.GetAttribute("height").ShouldBe("100");
134+
}
135+
136+
[Fact]
137+
public void AdRotator_DataSource_TakesPriorityOverAdvertisementFile()
138+
{
139+
var ads = new List<Advertisment>
140+
{
141+
new Advertisment
142+
{
143+
ImageUrl = "datasource.png",
144+
NavigateUrl = "http://www.datasource.com",
145+
AlternateText = "DataSource Ad",
146+
Width = "100",
147+
Height = "50"
148+
}
149+
};
150+
151+
// Even with AdvertisementFile set, DataSource should take priority
152+
var cut = Render(@<AdRotator DataSource="@ads" AdvertisementFile="Ads1.xml" Target="_blank" />);
153+
154+
var img = cut.Find("img");
155+
img.GetAttribute("src").ShouldBe("datasource.png");
156+
img.GetAttribute("alt").ShouldBe("DataSource Ad");
157+
}
158+
}

src/BlazorWebFormsComponents/AdRotator.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
@inherits BaseStyledComponent
1+
@inherits DataBoundComponent<Advertisment>
2+
@using BlazorWebFormsComponents.DataBinding
23

34
@{
45
var activeAdvertisment = GetActiveAdvertisment();

0 commit comments

Comments
 (0)