Bing Maps v6.3 – Context Menu with jQuery Plugin

September 29, 2010 Leave a comment

The tradition way to add a context menu for the Bing Map v6.3 is to attach event handler for “onclick” event and check if the right mouse button is clicked in the event handler. Please see Abhishek Sur‘s post for details.

There is another way to add a context menu – attach event handler for “oncontextmenu” event, but this is for Bing Map v4 (see this). The behavior is very similar to the “onclick” event in v6.3.

These two methods add some overhead in the event handling, and I found there are some glitches when handling “onclick” event – it’s better to check the right mouse button clicking in “onmouseup” event. Anyway, thanks to the jQuery Context Menu Plugin – it handles mouse and keyboard events very well and it makes adding context menus in Bing Map way easier.

Let’s check the source code of the demo page:

1 <%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”ContextMenu.aspx.cs” Inherits=”BingMap.ContextMenu” %>

2

3 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;

4

5 <html xmlns=”http://www.w3.org/1999/xhtml&#8221; >

6 <head runat=”server”>

7 <title>Bing Map – Context Menu</title>

8 <script type=”text/javascript” src=”http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3″></script>

9 <script src=”JavaScript/jquery-1.4.2.min.js” type=”text/javascript”></script>

10 <script src=”JavaScript/jquery.contextMenu.js” type=”text/javascript”></script>

11 <link href=”CSS/jquery.contextMenu.css” rel=”stylesheet” type=”text/css” />

12

13 <script type=”text/javascript”>

14 var map = null;

15

16 jQuery(document).ready(function() {

17 InitMap();

18 });

19

20 function InitMap() {

21 // Set and load map

22 map = new VEMap(“myMap”);

23 map.LoadMap(new VELatLong(49.260407, -123.114034), 15, VEMapStyle.Road);

24

25 // Add context menu to the map

26 jQuery(“#myMap”).contextMenu({

27 menu: ‘myMapContextMenu’

28 },

29 function(action, src, pos) {

30 MapContextMenuHandler(action, src, pos);

31 });

32

33 }

34

35 function MapContextMenuHandler(action, src, pos) {

36 if (action == “add_shape”) {

37 // Add a new shape

38 var pixel = new VEPixel(pos.x + 5, pos.y + 5);

39 var shape = new VEShape(VEShapeType.Pushpin, map.PixelToLatLong(pixel));

40 shape.SetCustomIcon(“<img src=’/images/POI_dd-via.png’ />”);

41 map.AddShape(shape);

42

43 // Add context menu to the map

44 var primitiveID = shape.Primitives[0].iid;

45 jQuery(“#” + primitiveID).contextMenu({

46 menu: ‘myShapeContextMenu’

47 },

48 function(action1, src1, pos1) {

49 ShapeContextMenuHandler(action1, src1, pos1);

50 });

51 }

52 }

53

54 function ShapeContextMenuHandler(action, src, pos) {

55 if (action == “remove_shape”) {

56 // Remove the shape

57 var primitiveID = jQuery(src).attr(“id”);

58 var shape = map.GetShapeByID(primitiveID);

59 if (shape != null) {

60 map.DeleteShape(shape);

61 }

62 }

63 }

64

65 </script>

66 </head>

67 <body>

68 <form id=”form1″ runat=”server”>

69 <div>

70 <div id=”myMap” style=”position: relative; width: 900px; height: 700px;“></div>

71

72 <ul id=”myMapContextMenu” class=”contextMenu”>

73 <li class=”edit”><a href=”#add_shape”>Add Shape</a></li>

74 <li class=”quit separator”><a href=”#quit”>Close</a></li>

75 </ul>

76

77 <ul id=”myShapeContextMenu” class=”contextMenu”>

78 <li class=”delete”><a href=”#remove_shape”>Remove Shape</a></li>

79 <li class=”quit separator”><a href=”#quit”>Close</a></li>

80 </ul>

81 </div>

82 </form>

83 </body>

84 </html>

The code is pretty self-explanatory. Here are some notes:

  1. Include jQuery and jQuery Context Menu Plugin JavaScripts and jQuery Context Menu CSS file.
  2. Two types of context menus:
    • myMapContextMenu – this context menu shows up when right mouse button is clicked anywhere inside the map – except the shapes (green dots). Selecting “Add Shape” will add a shape at the point right button was clicked.
    • myShapeContextMenu – this context menu shoes up when right mouse button is clicked on the shapes (green dots). Selecting “Remove shape” will remove the shape from the map.
  3. Use jQuery ready() to initialize map.
  4. Bind the myMapContextMenu to the map control and provide a callback named “MapContextMenuHandler”.
  5. In the callback “MapContextMenuHandler”, create a new shape with custom icon (green dot), then bind the myShapeContextMenu to the newly created shape and provide the callback named “ShapeContextMenuHanlder”. The import thing is I use “Primitive” of the shape as the hook to the shape itself – shape.Primitives[0].iid.
  6. In the callback “ShapeContextMenuHandler”, get back the primitive ID and find the shape with the primitive ID, then remove the shape from the map.

Cool, eh?

Here are screenshots:

  • Right-click shows up a context menu and select “Add Shape”.

  • A shape is added to the map.

  • Right-click the newly created shape to show the context menu. Click “Remove Shape” to remove this shape from the map.

Change Background Color of Invalid Controls with ASP.NET Validators

July 16, 2010 Leave a comment

Chris posted an excellent article explaining how to change background color of invalid controls (with ASP.NET Validators). In his article, the code loops through each validator in Page_Validators (it’s a global array in Javascript), finds the control to validate (ctrl.controltovalidate) of the validator, changes the background color of the control if the valiator is in invalid mode (ctrl.isvalid == false).

As the updates of validators only happens when the form is submitted, but I need a bit more – I want the updates trigger whenever the user makes change to the controls (type text in the text box, change option in the select box, etc).  Using bind() function in jQuery will do the trick.

Here is the code:

<script type=”text/javascript”>

var g_BackgroundColor = “#FFAAAA”;

jQuery(document).ready(function() {

for (var i = 0; i < Page_Validators.length; i++) {

var validator = Page_Validators[i];

if (validator.controltovalidate) {

BindValidatorsForChecking(validator.controltovalidate);

}

}

});

function OnUpdateValidators() {

for (var i = 0; i < Page_Validators.length; i++) {

var validator = Page_Validators[i];

// *** For each validator, find its associated control, then check its valid status

if (validator.controltovalidate) {

var control = document.getElementById(validator.controltovalidate);

if (control != null) {

control.style.backgroundColor = CheckValidatorsForControl(control) ? : g_BackgroundColor;

// *** Bind to check valid status

BindValidatorsForChecking(validator.controltovalidate);

}

}

}

}

// *** Check all validators for a control

function CheckValidatorsForControl(control) {

for (var i = 0; i < control.Validators.length; i++) {

if (!control.Validators[i].isvalid) {

return false;

}

}

return true;

}

// *** Bind to check valid status

function BindValidatorsForChecking(controlID) {

jQuery(“#” + controlID).bind(“blur focus mouseout mouseleave”, function(e) {

this.style.backgroundColor = CheckValidatorsForControl(this) ? : g_BackgroundColor;

});

}

</script>

In the code behind, you will need to hook the updates when the form is submitted as well:

Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), “OnUpdateValidators_Key”, “OnUpdateValidators();”);

Categories: AJAX, ASP.NET, C#, jQuery Tags: , ,

Using LINQ to parse XML

April 14, 2010 Leave a comment

LINQ is cool. Using LINQ to parse XML is cool. I will show you how to use LINQ to parse XML and build a list of objects (I know I can use XML serialization/deserialization to do the same thing, but this is just a demonstration).

Here is my two XML files:

(1) member01.xml

1 <?xml version=1.0 encoding=utf-8 ?>

2 <Members>

3 <Member>

4 <MemberId>1</MemberId>

5 <Name>Marlon Brando</Name>

6 <Character>Don Vito Corleone</Character>

7 <IMDBLink>http://www.imdb.com/name/nm0000008/</IMDBLink>

8 </Member>

9 <Member>

10 <MemberId>2</MemberId>

11 <Name>Al Pacino</Name>

12 <Character>Michael Corleone</Character>

13 <IMDBLink>http://www.imdb.com/name/nm0000199/</IMDBLink>

14 </Member>

15 <Member>

16 <MemberId>3</MemberId>

17 <Name>James Caan</Name>

18 <Character>Santino ‘Sonny’ Corleone</Character>

19 <IMDBLink>http://www.imdb.com/name/nm0001001/</IMDBLink>

20 </Member>

21 </Members>

(2) member02.xml

1 <?xml version=1.0 encoding=utf-8 ?>

2 <Members>

3 <Member>

4 <MemberId>1</MemberId>

5 <Name>Marlon Brando</Name>

6 <Character>Don Vito Corleone</Character>

7 <IMDBLink>http://www.imdb.com/name/nm0000008/</IMDBLink>

8 </Member>

9 <Member>

10 <MemberId>2</MemberId>

11 <Name>Al Pacino</Name>

12 <Character>Michael Corleone</Character>

13 <IMDBLink>http://www.imdb.com/name/nm0000199/</IMDBLink>

14 </Member>

15 <Member>

16 <MemberId>3</MemberId>

17 <Name>James Caan</Name>

18 <Character>Santino ‘Sonny’ Corleone</Character>

19 <IMDBLink>http://www.imdb.com/name/nm0001001/</IMDBLink>

20 </Member>

21 <Member>

22 <MemberId>4</MemberId>

23 <Name>Robert Duvall</Name>

24 <Character>Tom Hagen</Character>

25 </Member>

26 </Members>

As you can see in the member02.xml, the last member does not have <IMDBLink> element.

I build an entity class to represent the member object:

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.Web;

5 using System.Text;

6

7 namespace FrameworkTestBed

8 {

9 [Serializable]

10 public class Member

11 {

12 public int MemberId { get; set; }

13 public string Name { get; set; }

14 public string Character { get; set; }

15 public string IMDBLink { get; set; }

16

17 public override string ToString()

18 {

19 StringBuilder sb = new StringBuilder();

20 sb.Append(string.Format(“MemberId: {0}, “, MemberId));

21 sb.Append(string.Format(“Name: {0}, “, Name));

22 sb.Append(string.Format(“Character: {0}, “, Character));

23 sb.Append(string.Format(“IMDBLink: {0}”, IMDBLink));

24 return sb.ToString();

25 }

26 }

27 }

Here is the ASPX page which allows me to upload the XML file:

1 <%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”LinqParseXml.aspx.cs” Inherits=”FrameworkTestBed.LinqParseXml” %>

2

3 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;

4

5 <html xmlns=”http://www.w3.org/1999/xhtml&#8221; >

6 <head runat=”server”>

7 <title>Linq Parse Xml</title>

8 </head>

9 <body>

10 <form id=”form1″ runat=”server”>

11 <div>

12 <p>

13 <b>XML file:</b>

14 <asp:FileUpload ID=”fileXml” runat=”server” />

15 <asp:Button ID=”btnUploadXmlFile” runat=”server” Text=”Upload Xml File” OnClick=”btnUploadXmlFile_Click” />

16 </p>

17 <p>

18 <asp:Label ID=”lblContent” runat=”server” />

19 </p>

20 </div>

21 </form>

22 </body>

23 </html>

Here is the code-behind using LINQ to parse the uploaded XML file:

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.IO;

5 using System.Web;

6 using System.Web.UI;

7 using System.Web.UI.WebControls;

8 using System.Text;

9 using System.Xml.Linq;

10

11 namespace FrameworkTestBed

12 {

13 public partial class LinqParseXml : System.Web.UI.Page

14 {

15 protected void Page_Load(object sender, EventArgs e)

16 {

17 }

18

19 protected void btnUploadXmlFile_Click(object sender, EventArgs e)

20 {

21 if (fileXml.HasFile)

22 {

23 if (fileXml.PostedFile.ContentType == “text/xml”)

24 {

25 using (StreamReader reader = new StreamReader(fileXml.PostedFile.InputStream))

26 {

27 XDocument xdoc = XDocument.Parse(reader.ReadToEnd());

28

29 List<Member> members =

30 (

31 from item in xdoc.Descendants(“Member”)

32 select new Member

33 {

34 MemberId = Convert.ToInt32(item.Element(“MemberId”).Value),

35 Name = item.Element(“Name”).Value,

36 Character = item.Element(“Character”).Value,

37 IMDBLink = item.Element(“IMDBLink”).Value,

38 }

39 ).ToList<Member>();

40

41

42 StringBuilder sb = new StringBuilder();

43 foreach (var member in members)

44 {

45 sb.Append(string.Format(“{0}<br/>”, member));

46 }

47 lblContent.Text = sb.ToString();

48 }

49 }

50 }

51 }

52 }

53 }

The MemberId property is an integer type, we convert the element from string to integer with Convert.ToInt32().

Let’s upload member01.xml first:

Let’s upload member02.xml:

Crap, I got an exception – it’s because the missing XML element <IMDBLink>!

So I have to do something to prevent this from happening – check if the element exists before using its value. Let’s use the new features from C# 3.0 – Extension Methods:

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.Web;

5 using System.Xml.Linq;

6

7 namespace FrameworkTestBed

8 {

9 public static class XElementExtensions

10 {

11 public static T GetValue<T>(this XElement element, string name)

12 {

13 if (element.Element(name) == null)

14 {

15 return default(T);

16 }

17

18 try

19 {

20 return (T)Convert.ChangeType(element.Element(name).Value, typeof(T));

21 }

22 catch

23 {

24 return default(T);

25 }

26 }

27 }

28 }

I added one extension method on top of System.Xml.Linq.XElement: returns default generic type if the conversion fails.

Now let’s change the code-behind to use the new extension method:

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.IO;

5 using System.Web;

6 using System.Web.UI;

7 using System.Web.UI.WebControls;

8 using System.Text;

9 using System.Xml.Linq;

10

11 namespace FrameworkTestBed

12 {

13 public partial class LinqParseXml : System.Web.UI.Page

14 {

15 protected void Page_Load(object sender, EventArgs e)

16 {

17 }

18

19 protected void btnUploadXmlFile_Click(object sender, EventArgs e)

20 {

21 if (fileXml.HasFile)

22 {

23 if (fileXml.PostedFile.ContentType == “text/xml”)

24 {

25 using (StreamReader reader = new StreamReader(fileXml.PostedFile.InputStream))

26 {

27 XDocument xdoc = XDocument.Parse(reader.ReadToEnd());

28

29 List<Member> members =

30 (

31 from item in xdoc.Descendants(“Member”)

32 select new Member

33 {

34 MemberId = item.GetValue<int>(“MemberId”),

35 Name = item.GetValue<string>(“Name”),

36 Character = item.GetValue<string>(“Character”),

37 IMDBLink = item.GetValue<string>(“IMDBLink”),

38 }

39 ).ToList<Member>();

40

41

42 StringBuilder sb = new StringBuilder();

43 foreach (var member in members)

44 {

45 sb.Append(string.Format(“{0}<br/>”, member));

46 }

47 lblContent.Text = sb.ToString();

48 }

49 }

50 }

51 }

52 }

53 }

Here is the result of parsing the member02.xml:

Now it works well with the extension method.

As I said, this is just a demonstration. We can use XML serialization/deserialization to achieve the same thing. Using LINQ to parse XML with the help of extension methods is just cool :)

Categories: ASP.NET, C#, LINQ, XML Tags: , ,

Wishlist for Velocity (CPT4?)

June 4, 2009 Leave a comment

I went to Rob‘s presentation on Velocity tonight, it’s a great presentation and a lot of information shared with audience.

I prepare a wishlist for Velocity (CPT4 or next beta release) and hope the Velocity team can take my wishlist into consideration.

  1. A new method: bool DataCache.ContainsRegion(string region).
    • This method checks if the region exists in the named cache or not.
    • See my previous post for details.
  2. Update the method DataCache.GetObjectsByAnyTag(List<DataCacheTag> tags, string region) so the keys of returned cache items are unique.
    • Make sure the cache key is not contained in the keys, then add the item into the collection.
    • See my previous post for example.
  3. Make sure the default region name is not arbitratry.
    • DataCache.GetCacheItem Method – Gets a DataCacheItem object to retrieve all information associated with your cached object in the cluster.
    • DataCache.GetCacheItem (String) -> Gets a DataCacheItem object to retrieve all information associated with your cached object in the cluster.
    • DataCache.GetCacheItem (String, String) -> Gets a DataCacheItem object to retrieve all information associated with your cached object in the cluster. For objects stored in regions.
    • When using the DataCache.GetCacheItem (String) method (without region specified), the return object’s property -RegionName – is kind of arbitrary.
  4. A new commad for administrators to create region in PowerShell.
    • New-CacheRegion [-CacheName] <CacheName> [-RegionName] <RegionName>
    • Example 1:
      • New-CacheRegion -CacheName Products -RegionName MyRegion -> this creates a region “MyRegion” in the named cache “Products”.
    • Example 2:
      • New-CacheRegion -RegionName MyRegion -> this creates a region “MyRegion” in the default cache “default”

Visual Studio 2008 (SP1) Team System Profiler causes blue screen on Intel Core i7 processor

June 2, 2009 Leave a comment

Yesterday I used the VS2008 (SP1) Team System Profiler to test the performance of the web application at work, it caused blue screen three times. I didn’t know what happen but I suspect it’s a hardware conflict, so I did a search and found the Knowledge Base from Microsoft:

http://code.msdn.microsoft.com/KB958842

Symptoms
You may experience a stop error or a crash on the operating system when you profile an application by using Microsoft Visual Studio 2008 Service Pack 1 (SP1). The problem occurs when the following conditions are true:
• You use the sampling profiling method.
• The computer is running on the Intel Nehalem processor.

Cause
This problem occurs because the sampling profiler uses drivers in operating system. However, the Intel Nehalem processor was created after the 2008 version of the profiler.

Additional Information about the issue resolved by this Hotfix can be found in its Knowledge Base article at http://support.microsoft.com/kb/958842

Additional Hotfixes
Search the list of all other publicly available Visual Studio and .NET Framework Hotfixes at http://code.msdn.microsoft.com/Project/ProjectDirectory.aspx?TagName=Hotfix&ProjectSearchText=Hotfix*

Velocity (CTP3) – DataCache.GetObjectsByAnyTag Method

June 1, 2009 1 comment

I was writing some code to experiment the Tag search in Velocity CTP3 and I found there is some small bug in one of the get objects by tag methods – DataCache.GetObjectsByAnyTag.

The definition of the method of DataCache.GetObjectsByAnyTag is:

Gets an enumerable list of all cached objects in the specified region that have any of the same tags in common.

For example I have 3 cache items:

Item 1: key = “111”, content = “aaa”, tag = “a”

Item 2: key = “222”, content = “bbb”, tag = “b”

Item 3: key = “333”, content = “ccc”, tag = “a,b,c”

Here are some scenarios:
(1) If I search with Tag “a” via DataCache.GetObjectsByAnyTag(<a>, “my_region”), I get:
Item 1: key = “111”, content = “aaa”, tag = “a”
Item 3: key = “333”, content = “ccc”, tag = “a,b,c”
(2) If I search with Tag “b” via DataCache.GetObjectsByAnyTag(<b>, “my_region”), I get:
Item 2: key = “222”, content = “bbb”, tag = “b”
(3) But if I search with Tags “a,b” via DataCache.GetObjectsByAnyTag(<a, b>, “my_region”), I get:
Item 1: key = “111”, content = “aaa”, tag = “a”
Item 3: key = “333”, content = “ccc”, tag = “a,b,c”
Item 3: key = “333”, content = “ccc”, tag = “a,b,c”
Item 2: key = “222”, content = “bbb”, tag = “b”

Here are some scenarios:

(1) If I search with Tag “a” via DataCache.GetObjectsByAnyTag(<a>, “my_region”), I get:

Item 1: key = “111”, content = “aaa”, tag = “a”

Item 3: key = “333”, content = “ccc”, tag = “a,b,c”

(2) If I search with Tag “b” via DataCache.GetObjectsByAnyTag(<b>, “my_region”), I get:

Item 2: key = “222”, content = “bbb”, tag = “b”

Item 3: key = “333”, content = “ccc”, tag = “a,b,c”

(3) But if I search with Tags “a,b” via DataCache.GetObjectsByAnyTag(<a, b>, “my_region”), I get:

Item 1: key = “111”, content = “aaa”, tag = “a”

Item 2: key = “222”, content = “bbb”, tag = “b”

Item 3: key = “333”, content = “ccc”, tag = “a,b,c”

Item 3: key = “333”, content = “ccc”, tag = “a,b,c”

In scenario (3), the item 3 has been added twice because its tags are “a,b,c” and I search by <a,b> (Any Tag).

I believe this method – DataCache.GetObjectsByAnyTag – should check if the cache key is contained in the collection, if not, then add it to the collection – unless the velocity team want the developers do the checking of duplicated key in the caller program.

I will list my code and some screenshots.

The first file is CacheWorker.cs:

using System;

using Microsoft.Data.Caching;

namespace VelocityTest

{

public class CacheWorker

{

public const string DEFAULT_CACHE_REGION = “Default_Cache_Region”;

private static object _synclock = new object();

private static CacheWorker _cacheWorker = null;

private static DataCacheFactory _cacheFactory = null;

private static DataCache _defaultCache = null;

private static DataCache _eventCache = null;

private CacheWorker()

{

}

public static CacheWorker Instance

{

get

{

lock (_synclock)

{

try

{

if (_cacheWorker == null)

{

_cacheWorker = new CacheWorker();

// we use configuration file

_cacheFactory = new DataCacheFactory();

// default cache

_defaultCache = _cacheFactory.GetDefaultCache();

_defaultCache.RemoveRegion(DEFAULT_CACHE_REGION);

_defaultCache.CreateRegion(DEFAULT_CACHE_REGION, true);

// names cache

_eventCache = _cacheFactory.GetCache(“Event2″);

}

return _cacheWorker;

}

catch (DataCacheException ex)

{

return null;

}

}

}

}

public DataCacheFactory CacheFactory

{

get { return _cacheFactory; }

}

public DataCache DefaultCache

{

get { return _defaultCache; }

}

public DataCache EventCache

{

get { return _eventCache; }

}

public DataCacheItemVersion AddCache(string key, object value, TimeSpan? timeout)

{

DataCacheItemVersion version = null;

if (timeout.HasValue)

{

version = _defaultCache.Put(key, value, timeout.Value);

}

else

{

version = _defaultCache.Put(key, value);

}

return version;

}

}

}

The second file is Default3.aspx page:

<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”Default3.aspx.cs” Inherits=”VelocityTest.Default3″ %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;

<html xmlns=”http://www.w3.org/1999/xhtml&#8221; >

<head runat=”server”>

<title>Velocity – Tag Example</title>

</head>

<body>

<form id=”form1″ runat=”server”>

<p>

<h3>Velocity – Tag Example</h3>

<table border=”0″ cellpadding=”2″>

<tr>

<td><b>Cache Key</b></td>

<td>

<asp:TextBox ID=”txtCacheKey” runat=”server” />

<asp:RequiredFieldValidator ID=”vadCacheKey” runat=”server” ControlToValidate=”txtCacheKey” ErrorMessage=”* required” Display=”Dynamic” />

</td>

</tr>

<tr>

<td><b>Cache Content</b></td>

<td>

<asp:TextBox ID=”txtCacheContent” runat=”server” />

<asp:RequiredFieldValidator ID=”vadCacheContent” runat=”server” ControlToValidate=”txtCacheContent” ErrorMessage=”* required” Display=”Dynamic” />

</td>

</tr>

<tr>

<td><b>Cache Tags</b></td>

<td><asp:TextBox ID=”txtCacheTag” runat=”server” />(Tags are seperated by comma)</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>

<asp:Button ID=”btnCancel” runat=”server” Text=”Cancel” CausesValidation=”false” />

<asp:Button ID=”btnSave” runat=”server” Text=”Save” />

</td>

</tr>

</table>

</p>

<hr />

<p>

<table border=”0″ cellpadding=”2″>

<tr>

<td><b>Search Method:</b></td>

<td>

<asp:RadioButtonList ID=”radSearchMethodList” runat=”server” RepeatDirection=”Horizontal”>

<asp:ListItem Text=”No Tag” Value=”NoTag” />

<asp:ListItem Text=”All Tags” Value=”AllTags” />

<asp:ListItem Text=”Any Tag” Value=”AnyTag” />

<asp:ListItem Text=”One Tag” Value=”OneTag” />

</asp:RadioButtonList>

</td>

</tr>

<tr>

<td><b>Search Tags:</b></td>

<td><asp:TextBox ID=”txtSearchTag” runat=”server” />(Tags are seperated by comma)</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>

<asp:Button ID=”btnClearSearch” runat=”server” Text=”Clear” CausesValidation=”false” />

<asp:Button ID=”btnSearch” runat=”server” Text=”Search” CausesValidation=”false” />

</td>

</tr>

</table>

</p>

<hr />

<p>

<asp:GridView ID=”gvCache” runat=”server”

AutoGenerateColumns=”false”

EmptyDataText=”No cache data”>

<Columns>

<asp:TemplateField HeaderText=”Key” ShowHeader=”true”>

<ItemTemplate>

<asp:Label ID=”lblCacheKey” runat=”server” />

</ItemTemplate>

</asp:TemplateField>

<asp:TemplateField HeaderText=”Content” ShowHeader=”true”>

<ItemTemplate>

<asp:Label ID=”lblCacheContent” runat=”server” />

</ItemTemplate>

</asp:TemplateField>

<asp:TemplateField HeaderText=”Tags” ShowHeader=”true”>

<ItemTemplate>

<asp:Label ID=”lblCacheTag” runat=”server” />

</ItemTemplate>

</asp:TemplateField>

</Columns>

</asp:GridView>

</p>

</form>

</body>

</html>

The third file is Default3.aspx.cs code-behind:


using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Text;

using Microsoft.Data.Caching;

namespace VelocityTest

{

[Serializable]

public class MyCacheItem

{

public string Key { get; set; }

public string Content { get; set; }

public List<DataCacheTag> Tags { get; set; }

public MyCacheItem(string key, string conent)

{

Key = key;

Content = conent;

Tags = new List<DataCacheTag>();

}

public MyCacheItem(string key, string conent, string[] tags)

{

Key = key;

Content = conent;

Tags = new List<DataCacheTag>();

foreach (string tag in tags)

{

Tags.Add(new DataCacheTag(tag));

}

}

public string[] GetTags()

{

string[] tags = null;

if (Tags != null && Tags.Count > 0)

{

tags = new string[Tags.Count];

int i = 0;

foreach (DataCacheTag tag in Tags)

{

tags[i++] = tag.ToString();

}

}

return tags;

}

}

public enum TagSearchMethod

{

NoTag,

AllTags,

AnyTag,

OneTag

}

public partial class Default3 : System.Web.UI.Page

{

protected const string DEFAULT_DELIMITER = “,”;

protected DataCacheFactory _factory = CacheWorker.Instance.CacheFactory;

protected DataCache _cache = CacheWorker.Instance.DefaultCache;

protected void Page_Load(object sender, EventArgs e)

{

btnSave.Click += new EventHandler(btnSave_Click);

btnCancel.Click += new EventHandler(btnCancel_Click);

btnClearSearch.Click += new EventHandler(btnClearSearch_Click);

btnSearch.Click += new EventHandler(btnSearch_Click);

gvCache.RowDataBound += new GridViewRowEventHandler(gvCache_RowDataBound);

gvCache.DataSource = GetCacheDataSource(TagSearchMethod.NoTag, null);

gvCache.DataBind();

}

protected List<MyCacheItem> GetCacheDataSource(TagSearchMethod method, List<DataCacheTag> tags)

{

List<MyCacheItem> items = new List<MyCacheItem>();

IEnumerable<KeyValuePair<string, Object>> cachePairs = null;

if (method == TagSearchMethod.NoTag)

{

cachePairs = _cache.GetObjectsInRegion(CacheWorker.DEFAULT_CACHE_REGION);

}

else if (method == TagSearchMethod.AllTags)

{

cachePairs = _cache.GetObjectsByAllTags(tags, CacheWorker.DEFAULT_CACHE_REGION);

}

else if (method == TagSearchMethod.AnyTag)

{

cachePairs = _cache.GetObjectsByAnyTag(tags, CacheWorker.DEFAULT_CACHE_REGION);

}

else if (method == TagSearchMethod.OneTag)

{

cachePairs = _cache.GetObjectsByTag(tags[0], CacheWorker.DEFAULT_CACHE_REGION);

}

if (cachePairs != null)

{

foreach (KeyValuePair<string, object> item in cachePairs)

{

items.Add((MyCacheItem)item.Value);

}

}

return items;

}

protected void gvCache_RowDataBound(object sender, GridViewRowEventArgs e)

{

if (e.Row.RowType == DataControlRowType.DataRow)

{

MyCacheItem data = e.Row.DataItem as MyCacheItem;

if (data != null)

{

Label lblCacheKey = e.Row.FindControl(“lblCacheKey”) as Label;

if (lblCacheKey != null)

{

lblCacheKey.Text = data.Key;

}

Label lblCacheContent = e.Row.FindControl(“lblCacheContent”) as Label;

if (lblCacheContent != null)

{

lblCacheContent.Text = data.Content;

}

Label lblCacheTag = e.Row.FindControl(“lblCacheTag”) as Label;

if (lblCacheTag != null)

{

if (data.Tags != null)

{

lblCacheTag.Text = String.Join(“,”, data.GetTags());

}

else

{

lblCacheTag.Text = “&nbsp;”;

}

}

}

}

}

protected void btnSearch_Click(object sender, EventArgs e)

{

if (radSearchMethodList.SelectedItem != null)

{

TagSearchMethod method = (TagSearchMethod)Enum.Parse(typeof(TagSearchMethod), radSearchMethodList.SelectedValue);

List<DataCacheTag> tags = GetTags(txtSearchTag.Text.Trim());

gvCache.DataSource = GetCacheDataSource(method, tags);

}

else

{

gvCache.DataSource = GetCacheDataSource(TagSearchMethod.NoTag, null);

}

gvCache.DataBind();

}

protected void btnClearSearch_Click(object sender, EventArgs e)

{

Response.Redirect(Request.RawUrl);

}

protected void btnCancel_Click(object sender, EventArgs e)

{

Response.Redirect(Request.RawUrl);

}

protected void btnSave_Click(object sender, EventArgs e)

{

if (!String.IsNullOrEmpty(txtCacheKey.Text.Trim()) &&

!String.IsNullOrEmpty(txtCacheContent.Text.Trim()))

{

MyCacheItem item = null;

string[] tags = null;

if (!String.IsNullOrEmpty(txtCacheTag.Text.Trim()))

{

tags = (string[])txtCacheTag.Text.Trim().Split(DEFAULT_DELIMITER.ToCharArray());

}

if (tags != null && tags.Length > 0)

{

item = new MyCacheItem(txtCacheKey.Text.Trim(), txtCacheContent.Text.Trim(), tags);

}

else

{

item = new MyCacheItem(txtCacheKey.Text.Trim(), txtCacheContent.Text.Trim());

}

_cache.Put(item.Key, item, item.Tags, CacheWorker.DEFAULT_CACHE_REGION);

}

gvCache.DataSource = GetCacheDataSource(TagSearchMethod.NoTag, null);

gvCache.DataBind();

}

protected List<DataCacheTag> GetTags(string tagString)

{

List<DataCacheTag> tags = new List<DataCacheTag>();

string[] items = tagString.Split(DEFAULT_DELIMITER.ToCharArray());

foreach (string item in items)

{

tags.Add(new DataCacheTag(item));

}

return tags;

}

}

}

The following are screenshots.

This screenshot has nothing is cache.

ScreenShot001

This screenshot has 3 cache items.

ScreenShot002

This screenshot is search by AllTags <a>.

ScreenShot003

This screenshot is search by AllTags <a, b>.

ScreenShot004

This screenshot is search by AnyTag <a>.

ScreenShot005

This screenshot is search by AnyTag <b>.

ScreenShot006

This screenshot is search by Tag <a, b> – there are 4 cache items!

ScreenShot007

Velocity (CTP3) – How do I know if a cache region is created?

June 1, 2009 1 comment

In Velocity (CTP3) we can create, remove, and clean a cache region programmatically by calling the following methods:

  • DataCache.CreateRegion
  • DataCache.RemoveRegion
  • DataCache.ClearRegion

But there is no method to indicate if a cache region is created or not; so if I create a region which has been created already, an exception is thrown with the error code DataCacheErrorCode.RegionAlreadyExsits.

So far the walkaround is remove the cache region before creating it:

_defaultCache.RemoveRegion(DEFAULT_CACHE_REGION);

_defaultCache.CreateRegion(DEFAULT_CACHE_REGION, true);

But this is not a good approach; so I hope the Velocity team will add the feature in the next release to check if a cache region is created.

Follow

Get every new post delivered to your Inbox.