Silverlight to Sharepoint – Upload Image

by Bobbi Perreault 2. April 2010 11:27
Share on Facebook

 

Upload images to Sharepoint using Silverlight and SPAPI (a link to the code in a txt file for easier copying-silverlightImageUpload.txt (5.53 kb) )

 

This little piece of code is a Silverlight control that will place an image into a Sharepoint folder. After the upload, a thumbnail of that image is placed into the UI for feedback to the user. It uses the opensource Javascript to Sharepoint library, SPAPI, http://darrenjohnstone.net/2008/07/22/ a-cros....

In the Html that holds your Silverlight object definition, include the two SPAPI files Core and Imaging. The function sendImageToSharepoint will be used by your Silverlight object after it has been told what image to send to Sharepoint. I keep mine in another javascript file and it's also part of a larger object, I've placed just the piece used to upload here because the rest of that object doesn't apply here.

html------------------------------------------------

  <script src="scripts/SPAPI_Core.js" type="text/javascript"></script>
    <script src="scripts/SPAPI_Imaging.js" type="text/javascript"></script>
<script type="text/javascript">
    sendImageToSharepoint: function(strListName, strFolder, bytes, fileName) {
        var lists = new SPAPI_Imaging(labstools.sharepointUrl);
        var returned = lists.upload(strListName, strFolder, bytes, fileName, true);
        if (returned.status != 200) {

            alert("There was an error: " + returned.statusText);

        };

    }
</script>

In the Silverlight UserControl XAML file, place this StackPanel, it will be your user's point of interaction with the control. The second StackPanel, imagestorage, is used to place the thumbnail image.

Xaml--------------------------------------------------

<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
    <TextBlock Text="Attach Images(s):" TextWrapping="Wrap" Width="100"/>
    <TextBox x:Name="ImageFile" TextWrapping="Wrap" Width="211"/>
    <Button x:Name="btnUploadImage" Width="75" OnClick="bOpenFileDialog" Content="Browse" Style="{StaticResource styleA}" Height="25" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel x:Name=" imagestorage" />

In the Silverlight UserControl XAML code behind file, place this code, it is the Click event handler for btnUploadimage.. The Javascript function that is used to communicate with SPAPI is in a Namespace called labsSurveyQues, so you can see that being initialized in this code as well. One piece that isn't shown here, is where the EventHandler was added for CompositionTarget_Rendering, this piece of code syncronizes with the UI to place the thumbnail. I got this from Jeff Prosise's Blog, http://www.wintellect.com/CS/blogs/jprosise/archive/2008/10/24/cool-silverlight-trick-5.aspx

behind Xaml:------------------------------------------------

 Private Sub bOpenFileDialog(sender as Object, evt as EventArgs)
        ' Create an instance of the open file dialog box.
        Dim openFileDialog1 As OpenFileDialog = New OpenFileDialog
        Dim so As ScriptObject = TryCast(HtmlPage.Window.Eval(&quot;labsSurveyQues&quot;), ScriptObject)

        ' Set filter options and filter index.
        openFileDialog1.Filter = &quot;Image Files (*.png, *.jpg, *.jpeg)|*.png;*.jpg;*.jpeg&quot;
        openFileDialog1.FilterIndex = 1

        openFileDialog1.Multiselect = True

        ' Call the ShowDialog method to show the dialogbox.
        Dim UserClickedOK As Boolean = openFileDialog1.ShowDialog

            ' Process input if the user clicked OK.
        If (UserClickedOK = True) Then
           
            _files.Clear()
            For Each file1 As FileInfo In openFileDialog1.Files

                    Dim tb As New TextBlock
                    tb.Text = openFileDialog1.File.Name
                    tb.Name = &quot;image_&quot; + imagestorage.Children.Count.ToString()

                    Dim testextension As String = tb.Text.ToLower()
                    If testextension.Contains(&quot;jpg&quot;) Or testextension.Contains(&quot;jpeg&quot;) Or testextension.Contains(&quot;png&quot;) Then
                        imagestorage.Children.Add(tb)

                    '' ''Open the selected file to read.
                    Dim fileStream As System.IO.Stream = openFileDialog1.File.OpenRead
                    Dim binary As BinaryReader = New BinaryReader(fileStream)
                    Dim imgB() As Byte = binary.ReadBytes(fileStream.Length)
                    fileStream.Close()

                    'send the binary stream to the script that will put it out to the server for me
                    so.Invoke(&quot;sendImageToSharepoint&quot;, &quot;PictureLibrary&quot;, &quot;&quot;, Convert.ToBase64String(imgB), file1.Name)

                    _files.Enqueue(file1) 'displays to the UI

                End If
            Next
        End If
    End Sub

    Private _files As New Queue(Of FileInfo)()
       Private Sub CompositionTarget_Rendering(ByVal sender As [Object], ByVal e As EventArgs)
        Dim imagestorage As StackPanel = DirectCast(FindControls.RecursiveFindControl(controlspanel.Children, &quot;imagestorage&quot;), StackPanel)
        If _files.Count &lt;&gt; 0 And imagestorage IsNot Nothing Then
            Dim fi As FileInfo = _files.Dequeue()
            Using stream As Stream = fi.OpenRead()
                Dim bi As New BitmapImage()
                bi.SetSource(stream)
                Dim Img As New Image
                Img.Source = bi
                imagestorage.Children.Add(Img)
            End Using
        End If
    End Sub

 

Silverlight Elastic List Control

by Bobbi Perreault 30. May 2009 11:59
Share on Facebook

For this contest entry, How People Use Global’s Millennium, I chose to present the data using the Elastic List concept described here.  This is a post on how I built a piece of the Elastic List control, that is the controlling ListBox.

I was asked to share the source, Sure, no problem.  If you'd like the whole package, it's right here.

Elastic List ListBox

The ListBox that controls this chart needs to do three things:  1. Display the items in descending order of occurrance.  2. Color the items in respect to their occurrance and 3. Size the items in respect to their occurrance.

The Phizzpop contest entry provided a DataVisualization project which was to serve as the starting point.  In this project was the datasource, Customersurveys.xml.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CustomerSurveys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <Survey>
  <CustomerId>1</CustomerId>
  <Device>DX100 MP3 Player</Device>
  <Month>9</Month>
  <Year>2008</Year>
  <TimeUsed>20</TimeUsed>
  <Activity>Driving</Activity>
 </Survey>

The Xaml for the ListBox:

 

<PP4:ListControl x:Name="ActivityList" Title="Activities" Width="200" Height="343" VerticalAlignment="Top" AllClicked="ActivityList_AllClicked" FilterClicked="ActivityList_FilterClicked" SelectionChanged="ActivityList_SelectionChanged" LockClicked="ActivityList_LockClicked" Margin="0,0,5,0"/>

 

 

Loading a list from the xml file:

XmlReader reader = XmlReader.Create(s); XElement elementRoot = XElement.Load(reader); IEnumerable<DataItemXml> theBarItems = from element in elementRoot.Descendants("Survey") select new DataItemXml {  CustomerId = (int)element.Element("CustomerId"),  Device = (string)element.Element("Device"),  Month = (short)element.Element("Month"),  Year = (short)element.Element("Year"),  TimeUsed = (short)element.Element("TimeUsed"),  Activity = (string)element.Element("Activity") }; _barItems = theBarItems.ToList<DataItemXml>();

 

Use the list _barItems, to load our declared ListBox, ActivityList using this structure:

public class DataItem
 {
  public int Index { get; set; }
  public string Description { get; set; }
  public double Value { get; set; }
  public double Percentage { get; set; }
  public double ItemHeight { get; set; }
  public SolidColorBrush ItemColor { get; set; }
 }

.

 private List<DataItem> getWeightedActivityList()
{
 List<DataItemXml> activities = _barItems.GroupBy(b => b.Activity).Select(g => g.First()).ToList();
 // compute the percentage size of each activity
 List<DataItem> dataItems = new List<DataItem>();
 foreach (DataItemXml activity in activities)
 {
  DataItem dataItem = new DataItem();
  dataItem.Index = activities.IndexOf(activity);
  dataItem.Description = activity.Activity;
  double total = _barItems.Sum(b => b.TimeUsed);
  double thisTotal = _barItems.Where(b => b.Activity == activity.Activity).Sum(b => b.TimeUsed);
  dataItem.Percentage = thisTotal / total;
  dataItem.Value = thisTotal;
  
  double itemheight = 15 + (dataItem.Percentage * 100);
  dataItem.ItemHeight = itemheight;
  dataItem.ItemColor = getColorFromPercentage(dataItem.Percentage);
  dataItems.Add(dataItem);
 }
 dataItems.Sort(delegate(DataItem d1, DataItem d2) { return d2.Percentage.CompareTo(d1.Percentage); });
 return dataItems;
}

In the ListBox control, here is how we bind our ListItems so that they will use the items we initialized for Sizing and Coloring:

 <ListBox x:Name="ItemsList"  Margin="0,5,0,0" 
  Style="{StaticResource BlueListBoxStyle}" 
  SelectionChanged="ItemsList_SelectionChanged" Width="190" Height="270" >
<ListBox.ItemTemplate>
 <DataTemplate>
  <StackPanel Margin="0,0,0,0" Background="{Binding ItemColor}" Orientation="Horizontal" Height="{Binding ItemHeight}">
<TextBlock Text="{Binding Description}" x:Name="Body" Style="{StaticResource listcolumn}" Width="100"  />
<TextBlock Text="{Binding Value}" x:Name="Weight" Style="{StaticResource listcolumn}" Width="50"  />
  </StackPanel>
 </DataTemplate>
</ListBox.ItemTemplate>
 </ListBox>

Use Paint.NET to Create a Lake

by Bobbi Perreault 16. January 2009 20:05
Share on Facebook

Paint.NET is free software for digital photo editing.  I use it for 85% of my bitmap image processing needs - about the only time I switch to Photoshop is when I need to remove backgrounds because the Polygonal lasso tool makes it so easy. 

I had some fun this morning by putting a lake where none existed.  I started with a picture of FlatIrons that was taken by me when I worked in Longmont. 

I first increased the size of my canvas by adding 120 or so pixels onto the bottom. (Ctrl-R, or Image-Resize)

I used the Rectangle Select tool to select the mountain piece, then Ctrl-C to copy it.  Ctrl-Shift-V pastes my selection into a new layer.

From the Layers menu, select "Flip Vertical" and this will turn it upside down.  Then I type 'M' to get the 'Move Selected Pixels' tool, and move the flipped layer down to the bottom of my canvas.  This lines up the old bottom of the picture with a flipped copy of it's self.

 

 

Keep this layer selected. 

Use the Effects-Noise-Add Noise menu item to litter up the selection with a bunch of white specs.

Use the Effects-Blur-Motion Blur menu item to make the water 'wavy' - Do that by setting the angle to 180 and extending the blur to lengthen the lines.  Keep this selection active.

Once again, copy then paste into a new layer. 

Set the layer properties to Transparency of 70.  (F4 to access, or Layers-Properties)

Keeping this area selected, bump up the contrast to lighten this layer.

 

Move your layer selection back down to the first layer, the original picture - but keep your 'water' selection active.  Copy once again.

Move your layer selection up to the layer that is lightened and made transparent.  Then once again, paste your clipboard into a new layer.

For this third layer - once again set the transparency to 90.  This time use the Contrast tool (Ctrl-Shift-C, or Adjustments-Brightness/Contrast) to Darken the layer.

Now here's the fun piece.  Grab the Lasso select tool and with your mouse down for the whole time - drag your cursor back and forth and back and forth in a wave pattern across the darkened layer you just added.  Keep the lines you're drawing very skinny at the top of your "water" and far apart.  By the time you get to the bottom of your "water" your polygons should be wider - but closer to each other - think wave.

When you've filled the water with your lines, let up the mouse this will select portions of the transparent darker layer.  DELETE your selection.  And magic waves appear.

Here's a link to the final product - I like it for the background of my Twitter page.

NEXT - I'm going to take my waves into Expression Design.  These guys are going to ripple across the screen in the background of my new toy web site, Dont Call Me Late For Supper.

 

Silverlight-Capture and Define Rectangles/Regions and Wire Events on them

by Bobbi Perreault 29. October 2008 12:46
Share on Facebook

This is a piece of an ecommerce tool I built, I call it the Room Builder. It was what I learned Silverlight on - so this project has been brought up from Silverlight 1.1 Alpha version. In other words, you may see some bad practices in there. But it's still useful. Or could be to the right situation.

Specifically today I want to talk about one of the features of the tool, that is the part that allows me to designate a region of a photograph, name it, and wire events to it for use in the application.

There is a link to the finished tool, so you can see what I mean on the left side of this blog, under the section titled "links to some of my silverlight controls".   If you hold your mouse over that link there should be a window pop up with instructions on it's use.

Regions of the photograph are targeted by clicking at key points along the perimeter of a path.  In the case of our Room Builder, it would be the outline of a couch.  After the path is defined, it is finalized with the click of a button.  At that time, the defined region is turned into an object and added to the children of the canvas along with a click event so that object may be selected again.

You can download the source code for this Room Builder application here.  It won't run for you, though, because there is no server side application with the correct web services.  SORRY.  If anyone really needs that piece, I'll be happy to send it.  It's just time I didn't have tonight to spend.  Sorry.

Anyway,

Here's the key points of this region building operation:

Click the Get Started Button, Cursor changes to a pencil.

public void StartMappingClick(object sender, RoutedEventArgs e)
{
//if it's not visible (Opacity), don't execute it.  Stray Clicks.
if ( StartButton.Opacity < 1)
return;
if (_room == null || string.IsNullOrEmpty(_room.ProductID))
{
message.Text = "Please setup your room first.";
return;
}
_gettingRoom = false;
SBHideStartButton.Begin();
SBShowCancelButton.Begin();
SBShowEndButton.Begin();
//for our cursor
ImageMapperCanvas.Cursor = Cursors.None;
LayoutRoot.Cursor = Cursors.None;
///////////////////////
_points = new List();
// Capture mouse and update stat
CaptureMouse();
if (pencil != null)
{
pencil.Visibility = Visibility.Visible;
if (_positionLast != null)
movePencilToCurrentPosition((Point)_positionLast);
}
lastRectangle = "";
_bIn = true;
this.message.Text = "";
}

Click at each turning point of your region

private void HandleMouseLeftButtonDown(object sender, MouseEventArgs e)
{
// Capture mouse and update stat
Point pos = e.GetPosition(this);
if (_bIn)
{
//highlight the point and add it to the list 
registerPoint( pos );
}
_positionLast = pos; //this is so we can track for edits.
}
private void registerPoint(Point pos)
{
//only register the point if it's inside the image.
if (!CheckCollision(pencil, ImageMapperCanvas))
return;
_points.Add(pos);
if (_points.Count == 1)
{
//mark the point
Ellipse el = new Ellipse();
el.SetValue(Canvas.LeftProperty, pos.X+20);
el.SetValue(Canvas.TopProperty, pos.Y+20 );
el.Width=2;
el.Height=2;
el.Stroke = new SolidColorBrush(Color);
el.StrokeThickness = 1;
el.SetValue(Line.NameProperty, lineName(_points.Count));
LayoutRoot.Children.Add(el);
}
else
{
//draw the polyline and add the line to the list.
//string of points
//
// Create new line
var line1 = new Line
{
X1 = _positionLast.Value.X,
Y1 = _positionLast.Value.Y,
X2 = pos.X,
Y2 = pos.Y,
Stroke = new SolidColorBrush(Color),
StrokeThickness = 1
};
line1.SetValue(Line.NameProperty, lineName(_points.Count));
LayoutRoot.Children.Add(line1);
}
}

Click the End Mapping Button

public void EndMappingClick(object sender, RoutedEventArgs e)
{
//if it's not visible (Opacity), don't execute it.  Stray Clicks.
if (this.EndButton.Opacity < 1)
return;
SBHideStartButton.Begin();
SBHideEndButton.Begin();            
SBShowcontrolgrid.Begin();
// Release mouse and update stat
ReleaseMouseCapture();
ImageMapperCanvas.Cursor = Cursors.Stylus;
LayoutRoot.Cursor = Cursors.Arrow;
if (pencil != null)
{
pencil.Visibility = Visibility.Collapsed;
}
if (_bIn)
{
_ictr++;
//we could switch this out based on a selected tool.
// MapItemBO newMapItem = drawRectangle();
MapItemBO newMapItem = drawPolygon();
if (newMapItem != null)
persistMapItem( newMapItem );
_bIn = false;
_points.Clear();
}
this.message.Text = "Search for the outlined item";
}

During this end mapping process, a path object is created, a click event attached, and that object is added to the children of the canvas.

code to get this path added to the canvas, by the way, if you download the sample code, this is line 509 in page.xaml file.

private void createPolygonPath(string dataline, string shapeName, Color border)
{
//to get this I used code from CreateWPFShape.  I also brought up Blend to
//help me with the Xaml for the path, expecially fill and opacity.
//also, in my diagram maker is code which created path geometries programatically
//this came from scribbler.
//
//extract the point array from this dataline. remove markup from the dataline.
dataline = dataline.ToLower();
int ilen = dataline.IndexOf("coords=");
if (ilen < 7)
return; 
dataline = dataline.Substring(ilen + 7);
ilen = dataline.IndexOf("href");
if (ilen < 1)
return;
dataline = dataline.Substring(0,ilen);
dataline = dataline.Replace("\"", "");
//message.Text = dataline;  //for debugging.
string[] pointlist = dataline.Split(','); //split the dataline on commas into an array.
if (pointlist.Count() < 2)
return;
// Create a new geometry.
PathGeometry geometry = new PathGeometry();
// Create a new path figure.
PathFigure figure = new PathFigure();
figure.Segments = new PathSegmentCollection();
// Add figures to the geometry.
for (int i = 0; i < pointlist.Count(); i++)
{
// Determine the starting index and the end index
// into the points array that defines the figure.
int ptx = -1;
int.TryParse( pointlist[i], out ptx);
int pty = -1;
if ( pointlist.Count() > 1 && i != (pointlist.Count() - 1) )
int.TryParse( pointlist[i + 1], out pty);
else
int.TryParse( pointlist[ pointlist.Count()-1 ], out pty);
System.Windows.Point pt = new Point( ptx, pty);
if (i == 0)
figure.StartPoint = pt;
else
{
LineSegment linesegment1 = new LineSegment();
linesegment1.Point = pt;
figure.Segments.Add(linesegment1);
}
i++; //move to the next x
}
if (geometry.Figures == null)
{
PathFigureCollection pathFigures = new PathFigureCollection();
pathFigures.Add(figure);
geometry.Figures = pathFigures;
}
else
{
// Add the new figure to the geometry.
geometry.Figures.Add(figure);
}
// Add the geometry to a new Path and set path properties.
System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
path.Data = geometry;
path.SetValue(Canvas.NameProperty, shapeName);
SolidColorBrush brw = new SolidColorBrush(Colors.White);           
path.Fill = brw;
path.Opacity = .40;
SolidColorBrush brb = new SolidColorBrush( border ); 
path.Stroke = brb;
path.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(editItemClick);
LayoutRoot.Children.Add(path);
}

Happy SILVERLIGHT! 

menu in html and silverlight

by Bobbi Perreault 12. September 2008 22:27
Share on FacebookDemo source links (big download warning)

Links to this demo project, which is created from MVC Storefront -  (Updated:  link to the demonstration page)

MVC Storefront. is an ecommerce demo and is a perfect starting place for Silverlight enhancement - you can get help for MVC Storefront here:  http://www.codeplex.com/mvcsamples

This html/silverlight demo is built as it is to enable the site to conform with a Web standards-based approach. On the UI layer, conforming to Web standards means 100% separation of presentation from content and structure, as well as the scripting behavior of UI elements. On the back end, this means limiting the mixing of UI code in the Web applications and CMS code that may need periodic updates, it means applying the same strict separation as to any other static screen.

There’s a fine line here, at the same time we need to avoid the appearance of "cloaking".  "Cloaking" is the tactic of detecting search engine spiders when they visit and varying the HTML code specifically for the spiders in order to improve rankings. This is only acceptable in a very limited use: namely, as a way of simplifying search engine unfriendly links. If you are in any way selectively modifying the page content, this is nothing less than a bait-and-switch. Search engines have undercover spiders that masquerade as regular visitors to detect such unscrupulous behavior.

In this demo, The Silverlight control will be responsible for both retrieving it ‘s content from the server as well as hiding the indexable content using CSS. By having the Silverlight menu hide the indexable menu we have just solved the problem of how to know if the user has Silverlight Installed and how to know if the user has Javascript enabled.

Let’s look at the Silverlight project: I’m going to start off by opening an mvc project that I downloaded from Codeplex, the MVC Storefront. This is an ecommerce demo and is a perfect starting place for Silverlight enhancement.

The requirements for the menu are this:   If the client doesn't support Silverlight ( bots, clients who  haven't installed it yet, older browsers, etc.)  - keep the page in pure html. If the client does support Silverlight, load this menu from the exact same source that the MVC view menu is using.

As stated previously, the Silverlight control is constructed in such a way that it will – if created – retrieve it’s contents from the server and after formatting it’s self – hide the html version. Once we have our Silverlight, we don’t really give control back to the html and that avoids any further delays to the user.

Let’s look at how this is setup. Here is the MVC Storefront application. Let’s have a really quick overview of how an MVC app is constructed. First, Global.asax file has code in application startup to assign controller classes to specific urls. All the controller files are located in folder: Controllers. When a request for one of the assigned urls is received at the server, that controller is called along with the default action. In our case, the MVC storefront default page goes into the CatalogController,and uses the function of Index. Index method will gather the menu details into a list of type CatalogData and pass that list over to the View also called Index - which will use that data to present the user page. All the view files are located in the folder called View. So, we can find our Index view in folder View/Catalog/Index.aspx. Index view renders a control called CategoryList.ascx which presents the CatalogData list in the form of an html menu. Here’s what this page looks like.

<div id="sl-categoryNavigation" ></div>
<div id="categoryNavigation" class="categoryNavigation">
    <%foreach (Category parent in ViewData) { %>
    <h3><%=parent.Name %></h3>
    <ul>
        <%foreach (Category child in parent.SubCategories) { %>
        <li>
        <%=Html.ActionLink<CatalogController>(x=>x.Index(parent.Name,child.Name),child.Name) %>
        </li>
        <%} %>
    </ul>
    <%} %>
</div>

View the source of this MVC page, and you’ll see clear, clean html. No mangled ID’s. No ViewState.  When this page loads, this Index.aspx view, there is some javascript contained in an included file that comes into play. The javascript is contained in an included file to preserve the separation of presentation from content and structure. This script is is executed when the page is ready. The file is called menu.js, and it uses JQuery to simplify scripting.

We’ll see that in great detail in our next demo. For this demo, we’re going to skip that part and get right to the Silverlight.

What we’re interested in here is in how this Silverlight menu can be created from the same source as the html menu. That saves me as the programmer from a lot of maintenance during the life of this application.

Let’s look at the Silverlight control first. In our Silverlight project there’s a control called menu.xaml. In menu.xaml we have a Grid which contains a StackPanel which contains a ListBox. There is a template applied to the ListBox which enables another ListBox to be embedded within. All this xaml is written in such a way so as to make it skinnable.

We’re going to skip over the part where this Silverlight control contacts the server for it’s menu details and save that for later. Right now, we’ll look at what happens when fully formatted web.sitemap xml is ready to be turned into a menu. Here’s what the web.sitemap this control works with looks like.

<?xml version="1.0" encoding="utf-8" ?>
<siteMap >
<siteMapNode>
    <siteMapNode url="" title="Apparel" description="Apparel" menugroup="Apparel" itemType="menuTitle"  >
          <siteMapNode url="/store/Apparel/Boots" title="Boots" description="Boots" menugroup="Apparel" itemType="menuItem"  />
           <siteMapNode url="/store/Apparel/Hats" title="Hats" description="Hats" menugroup="Apparel" itemType="menuItem"  />
           <siteMapNode url="/store/Apparel/Sunglasses" title="Sunglasses" description="Sunglasses" menugroup="Apparel" itemType="menuItem"  />
         </siteMapNode>
...
    <siteMapNode url="" title="Featured Items" description="Featured Items" menugroup="Featured Items" itemType="menuTitle"  >
          <siteMapNode url="/store/Featured Items/All Items" title="All Items" description="All Items" menugroup="Featured Items" itemType="menuItem"  />
         </siteMapNode>
</siteMapNode>
</siteMap>

This data is passed into the code behind of menu.xaml first as a string. (open the menu.xaml.cs file and move to method setMenu())

In the setMenu method,

I’m going to build some business objects to represent the Silverlight menu and I’m going to use  LINQ over Xml ( Xlinq  )

One thing I ran into while working with LINQ over Xml that I’ll just mention real quick and that is: namespaces can complicate the code. And in my case, since it was my site map, talking to my server, I just removed the namespace which was in the first node.   This saved me from writing some code to match namespaces.

To get the Xdocument to load up in my Silverlight control from my Sitemap XML,  I created two new Business Object classes in my project.  The first class represents a SiteMapNode.  The second class represents a MenuGroup.

Here’s what the LINQ looks like which is going to parse the XML into an IEnumerable list of my sitemap objects, which are the menu items. Use the XDocument.Parse to get my xml string into an XDocument. And then I’m going to select from xdoc.Decendentants(“siteMapNode”) new SitemapBO objects – and load em up right there. The url property is assigned from the sitemap attribute “url”. The title is assigned from attribute title, and so on.

The sitemapbo class, which we have just made a list of is just a property bag which stores and organizes my XML for me.

SiteMapBO.cs:

public class SiteMapBO
    {
        public string url { get; set; }
        public string title { get; set; }
        public string description { get; set; }
        public string menugroup { get; set; }
        public string itemType { get; set; }
    }

XLINQ:

xdoc = XDocument.Parse(sitemapXML);

IEnumerable<SiteMapBO> menuitems = from menuitem in xdoc.Descendants("siteMapNode")
               select new SiteMapBO
               {
                   url = (string)menuitem.Attribute("url"),
                   title = (string)menuitem.Attribute("title"),
                   description = (string)menuitem.Attribute("description"),
                   menugroup = (string)menuitem.Attribute("menugroup"),
                   itemType = (string)menuitem.Attribute("itemType")
               };

Once I have this list of sitemapbo objects, I still need to organize it into a list of MenuGroup objects.

How I format the menu using "menuItem" and "menuTitle" of the sitemap.

What I want is to have my menu titles to be independently skinnable from my menu items.  It gives me freedom, I can make my menu look special because I can touch each type of object within it with a skin. I have the ability to make this Silverlight something special and That's what the MenuGroups class is for.  After getting a list of SiteMapBO's from the Xdocument, in the olden days, I would then re-traverse the list to load a new IEnumerable list of MenuGroups - and this is the list that gets bound to my ListBox.

Today, I’m able to do this using LINQ to Objects.

MenuGroup class consists of a Title property , and a list of SiteMapBO’s.

MenuGroups.cs:

public class MenuGroups
   {
       public string title { get; set; }
       public List<SiteMapBO> itemsList { get; set; }

       public MenuGroups()
       {
           itemsList = new List<SiteMapBO>();
       }
   }

 

Here’s the code that will load the list which will become my ItemsSource of the menu ListBox  using LINQ to Objects, Framework V3.5:

List<MenuGroups> menus = new List<MenuGroups>();
if (menuitems.Count() > 0)
{
    foreach (var menugroup in menuitems.Where(grp => grp.itemType == "menuTitle"))
    {
        MenuGroups mnu = new MenuGroups();
        mnu.title = menugroup.title;
        mnu.itemsList = menuitems.Where(p => p.itemType != "menuTitle" && p.menugroup == menugroup.menugroup).ToList();
        menus.Add(mnu);
    }
    this.MenuList.ItemsSource = menus;

    //the object is ready to show itself - this is javascript call.
    HtmlPage.Window.Invoke("showSlMenu");
    //here, we'll show ourself with an animation
    this.showMyself.Begin();
}

Ok, at the end, then, when I’ve finished building the list of MenuGroup objects, I just set the ItemsSource of the MenuGrid Listbox to this list, menus. And the Styling takes care of the rest of it – that’s it, we’re done.

This is how I build this Silverlight menu control so that it is skinnable and that way I can re-use it in different projects.

In the XAML, notice that this solution allows for individual formatting of the header and the items.

This is the menu: (a stackpanel which contains an image and a listbox.)

<StackPanel x:Name="MenuGrid" >
<Image Source="images/bdlogo.png" Stretch="Fill"/>
<ListBox x:Name="MenuList" Style="{StaticResource menuTitleStyle}" >
</ListBox>
</StackPanel>

 

The listbox name is Menulist, and it’s styled with a StaticResource, menuTitleStyle. The menuTitleStyle is contained in the top of the XAML file, inside the UserControl.Resources section.

Here is the menuTitleStyle:

   It has an Item Template which contains a TextBlock and a ListBox. The TextBlock is using databinding to set it to the MenuGroup property, Title. And the ListBox is databound to the itemsList property of that menugroup.

<Style TargetType="ListBox" x:Key="menuTitleStyle" >
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding title}"
Style="{StaticResource menuTitle}" />
<ListBox ItemsSource="{Binding itemsList}" SelectionChanged="MenuList_SelectionChanged"
Style="{StaticResource menuItemsStyle}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

So, to finish this piece I need to discuss how the Silverlight menu replaces the html menu. While the Silverlight menu is getting it’s xml, formatting it’s xml, putting it’s face on – it’s not visible on the web page. Once it’s ready there is a line of code here that runs a javascript which is linked to the web page. Here’s the line of code that executes the javascript, which will take care of sizing the div which contains our Silverlight control to place it over the top of the Html control (this is a modified version of CSS Image replacement technique)

//the object is ready to show itself - this is javascript call.
HtmlPage.Window.Invoke("showSlMenu");

Following this line is the animation (Silverlight) which fades in the new menu.

//here, we'll show ourself with an animation
this.showMyself.Begin();

Let’s look at showSLMenu in the file menu.js file

First, in this SLMenu function we get the width and height of the html menu.

Then, The div which holds the Silverlight control is then resized to match the original menu. In our css, we’ve set the z-index property of this div very high which will position it over any html content with a lower z-index.

And lastly, the Silverlight contol’s opacity is brought from 0 to 100 which fades it into place.

Silverlight Islands Usergroup Presentation

by Bobbi Perreault 15. July 2008 08:07
Share on Facebook

Today was the Minneapolis Silverlight User Group. It was my first user group presentation, so kind of glitchy! All the GUYS (ok there were Four women this time) were very kind and attentive.  No one got up and walked out - so I'm a happy camper.

We talked about Search Engine Optimization and how to enable applications that use Silverlight to keep Search Engine Rankings as high as possible.  Those topics are discussed freely on the web and I pulled a lot of the content from Nathan Buggia's Mix08 talk and Nikhil Kothari's blog.  So thanks you guys. 

But, the sample code is mine, at least.  It's an Ecommerce site demo (minimal!!!) which is based off of an actual ecommerce site that is in development.  So I hope it's helpful.  I may pull some of the relevant pieces out of the app and write up some details - why it's there, it's operation, it's purpose.

Some of the features in the sample application are:  web.Sitemap to Silverlight menu, WrapPanel for category listing, a sample of how to use the same .xap file for multiple page areas (those are the islands, look in app.xaml), registering your url (for deep linking)  and turning off the html when javascript is enabled. 

To see the page as a search engine would just turn off your javascript and the html formatting will be there.

Those links are at the top. Have a great day!

 

RSS Feed FriendFeed