Showing posts with label Silverlight. Show all posts
Showing posts with label Silverlight. Show all posts

Thursday, November 13, 2008

Capturing Control Key Sequences in Silverlight 2

It took me a while to locate this, but I found in this MSDN article how to capture control key sequences in Silverlight 2. I was expecting to be able to 'and' the control key with the pressed alpha key, but that's not the way it works. I attached the following event handler to my layout root grid's KeyUp event. This performs a 'save' when pressing Ctrl+S, and 'save and close' when pressing Ctrl+Shift+S:

        /// <summary>
        /// Handles keyboard shortcuts.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void LayoutRoot_KeyUp(object sender, KeyEventArgs e)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                switch (e.Key)
                {
                    case Key.S:
                        // Ctrl+S: save; Ctrl+Shift+S: save and close
                        e.Handled = true;
                        SaveMyItem((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift);
                        break;
                }
            }
        }

Thursday, September 25, 2008

Enable Silverlight in MOSS - web.config changes

I know there are numerous articles on this, but here are the web.config changes I made to enable Silverlight in Microsoft Office SharePoint Server 2007 (modified parent node names in bold):

configuration/configSections

<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere" />
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
</sectionGroup>
</sectionGroup>
</sectionGroup>

configuration/system.web/httpHandlers

<remove verb="*" path="*.asmx" />
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false" />

configuration/system.web/httpModules

<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />

configuration/system.web/compilation/assemblies

<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Silverlight, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />

configuration/system.web/pages

<controls>
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</controls>

configuration

<system.web.extensions>
<scripting>
<webServices />
</scripting>
</system.web.extensions>

Monday, September 08, 2008

Silverlight web part - Code Access Security and Startup Permissions

I built a web part based upon the Silverlight 2 beta 2 guidance and encountered an interesting situation. If a user that did not have administrative privileges on the web server was the first to browse the portal hosting the web part, the user would simply receive a 403 (Forbidden) error page.

I had been working with a web part installer based upon the SharePoint Solution Installer (an excellent project to simplify installation of web part packages), and my WSP specified a custom code access security policy. Additionally, my web part referenced Enterprise Library 4.0 assemblies that I had built and signed. So my troubleshooting initially focused around the following:
  • Changing the custom code access security policy to grant unrestricted access to the web part (no effect);
  • Changing the trust level for the entire WSS site to Full (no effect);
  • Registering the Ent Lib assemblies via InstallUtil (no effect);
  • Adding the Ent Lib assemblies to the GAC (they were running in bin before--no effect);
  • Removing all reference to Ent Lib from my web part assembly (no effect).
Finally, I added System.Web.Silverlight.dll to the GAC, and voila--the site started working. I backed out all other changes and it continued working.

In case it helps, I believe this is the minimal CAS policy for a web part that hosts an application via Silverlight:

<CodeAccessSecurity>
<PolicyItem>
<PermissionSet Name="Web Part Permission Set" class="NamedPermissionSet" version="1" Description="Permission set for Silverlight-hosting web part">
<IPermission class="AspNetHostingPermission" version="1" Level="Medium" />
<IPermission class="SecurityPermission" version="1" Flags="Execution" />
<IPermission class="Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
version="1" ObjectModel="True" />
</PermissionSet>
<Assemblies>
<Assembly Name="My.WebPart" Version="1.0.0.0" PublicKeyBlob="---insert long encoded public key blob extracted with sn -Tp here ---" />
</Assemblies>
</PolicyItem>
</CodeAccessSecurity>

Friday, June 27, 2008

Silverlight and REST for corporate (intranet) applications

Danger Will Robinson, Silverlight 2 beta 2's WebClient implementation does not support passing integrated security credentials. I was hoping to implement a RESTful services tier for my intranet Silverlight application using Windows Integrated Security. While this does work brilliantly when using IE as a client, the Silverlight client cannot call the services. There seems to be no good workaround: I could use ASP.NET authentication services using Forms authentication against AD, but I would have to present the client with a login prompt.

FYI, the service configuration for WCF REST and integrated security is as follows:

<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp/>
<!--<enableWebScript/>-->
</behavior>
</endpointBehaviors>
</behaviors>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

<bindings>
<webHttpBinding>
<binding name="integratedWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</webHttpBinding>
</bindings>

<services>
<service name="AppNamespace.Service">
<endpoint address=""
behaviorConfiguration="webBehavior"
binding="webHttpBinding"
bindingConfiguration="integratedWebHttpBinding"
contract="AppNamespace.Service" />
</service>
</services>
</system.serviceModel>

Friday, June 13, 2008

Moving Silverlight 2 beta 1 applications to beta 2

With new times come a new Silverlight beta, released last week. I set out to update my beta 1 applications to beta 2. First, I let it run the project through the upgrade wizard. After that, I found there were several more steps, starting with fixing your installation...

Remove and Re-Add System.Windows References

This includes:
  • System.Windows
  • System.Windows.Browser
  • System.Windows.Controls.Data
  • System.Windows.Controls.Extended (this one will likely not be needed now as many controls have moved into the base assemblies)
Update Namespaces of the Silverlight User Control and Application Objects

Make this change in App.xaml and any other user control (*.xaml) objects.

From:
xmlns="http://schemas.microsoft.com/client/2007"
To:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
This avoids mystery "invalid XAML" errors from the Silverlight control on web pages:
Sys.InvalidOperationException: Invalid XAML for control 'Xaml1'. [] (line 1, col 229): The element is not valid in the given namespace.
Update the Deployment Node Namespace in AppManifest.xaml

From:
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="HCHB.ServiceRequests" EntryPointType="HCHB.ServiceRequests.App" RuntimeVersion="2.0.30226.2">
To:
<Deployment xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="HCHB.ServiceRequests" EntryPointType="HCHB.ServiceRequests.App" RuntimeVersion="2.0.30523.6">
Update the Silverlight Control Declaration

Find the Silverlight control on the web page(s) in your site and update the node accordingly, from:
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/Silverlight1.xap" Version="2.0" Width="100%" Height="100%" />
To:
<asp:Silverlight ID="Silverlight1" runat="server" Source="~/ClientBin/Silverlight1.xap" MinimumVersion="2.0.30523" Width="100%" Height="100%" />
Change the Cross-Domain Access Policy

This prevents 404 errors when calling my web services.

From:
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
...
To:
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
...


Wednesday, April 30, 2008

Silverlight 2.0 integration with Windows Forms

Here's how you can host Silverlight 2.0 beta 1 applications in existing Windows Forms applications and have two-way communication between them. There may be an ActiveX approach that will accomplish this, but the approach I took was with a WebBrowser control.

First, set up the Windows Forms client. I'll use Form1, the hardest working form of them all. I simply drag the WebBrowser control onto the form. In its constructor, I set a few properties needed for having the embedded WebBrowser:


public partial class Form1 : Form {
public Form1() {
InitializeComponent();

webBrowser1.AllowWebBrowserDrop =
webBrowser1.IsWebBrowserContextMenuEnabled =
webBrowser1.WebBrowserShortcutsEnabled = false;
webBrowser1.ObjectForScripting = this;

webBrowser1.Navigate("http://<url of Silverlight application>");
}


I also added a property and a method in the Windows Form for the hosted Silverlight application to call:


public void ButtonClicked(string message) {
MessageBox.Show("ButtonClicked: " + message);
}

public string Test {
set {
MessageBox.Show("Test: " + value);
}
}


In the Silverlight application, use managed code like the following (e.g. in a click event handler, load event, etc.) to call the custom methods on the hosting form (note that you need a reference to System.Windows.Browser to get to the browser):



using System.Windows.Browser;
/* ... */

string msg = "Clicked at " + DateTime.Now.ToString();
HtmlWindow w = HtmlPage.Window;
ScriptObject o = (ScriptObject)w.GetProperty("external");
if (null != o)
{
try
{
// Set a property value on the host form
o.SetProperty("Test", msg);

// Call a method on the host form
o.Invoke("ButtonClicked", new object[] { msg });
}
catch { }
}


Note the catch block: I haven't yet discovered how to detect if the Silverlight application is being hosted in a WebBrowser from inside (if it is being run directly in a browser, the 'external' object exists but invoking custom properties and methods will fail).

Calling methods in the Silverlight application from the WebBrowser control is a bit more complex and involves a few steps:

1) Stage the Silverlight application and methods for scripting

In App.xaml, you must register each Silverlight page for scripting support. For example, if I have a page called Page, I need code like the following:



Page p = new Page();
this.RootVisual = p;
HtmlPage.RegisterScriptableObject("Page", p); // The identifier ("Page") here is arbitrary and refers to your key for identifying the object from script



Now that I've done this, I have to mark each Silverlight page class with the ScriptableType attribute:


[ScriptableType()]
public partial class Page : UserControl
{
/* ... */



Also, each public method I intend to call in the marked class from external script must be marked with the attribute:


[ScriptableMember()]
public void SetSearchText(string msg)
{
/* ... */



2) Set up scripting support in the web page hosting the Silverlight application

The WebBrowser control can call script methods within the page, but doesn't seem to be able to call the Silverlight control object directly (I made several attempts to do this but was unsuccessful). What I ended up doing that worked was to set up a script method corresponding to each method I want to call within the Silverlight application.

First, alter the tag on the Silverlight control so that it calls a script method of yours when it loads:



<div style="height:100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/MySilverlightApp.xap" Version="2.0" Width="100%" Height="100%" OnPluginLoaded="pluginLoaded" />
</div>



Now, add a script block in the HEAD tag with a method corresponding to the above to store a reference to the Silverlight control when it loads:



<head runat="server">
<title>Test Page For DiggSample</title>
<script type="text/javascript">
var scControl;

function pluginLoaded(sender) {
scControl = sender.get_element();
}
</script>
...



Next, add one script method for each method you marked exposed within the Silverlight application. Note the formatting (you must call it in the form: objectName.Content.<name you chose as the Silverlight script object identifier>.method(arguments)):



<script type="text/javascript">
function SetSearchText(msg) {
scControl.Content.Page.SetSearchText(msg);
}
</script>



Finally, from within the Windows Forms application, you can call the method you exposed via the WebBrowser control as follows:



private void btnSetSearchText_Click(object sender, EventArgs e)
{
webBrowser1.Document.InvokeScript("SetSearchText", new string[] { "foo" });
}



That's it--you should be able to have two-way communication between the Windows Forms application and the Silverlight application hosted via its WebBrowser control.