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

Monday, 18 March 2013

Some of the things I love and hate about Silverlight

After a previous job I had decided Silverlight wasn't the greatest tool in the world and vowed to push HTML5 as a better solution for MS CRM in the future. But, in my current position we use Silverlight quite a lot so I ended up back developing some pages in it again. I constantly ask the question why people continue to develop on a framework that will be eventually unsupported? But I guess the answer is people learnt to love the beast and found it hard to move on! And I have to admit, one of the beautiful things about Silverlight is a lot of those niceties you get from WPF also exist in Silverlight, such as slick interfaces and speedy UI development.

Unfortunately that probably just about completes the list of things I love about silverlight. Because with its slightly different and vastly reduced framework it also brings a lot of nuisances. These are noticeable in the controls, libraries (or lack thereof) and general functionality. Here are a few of the most recent/common issues I have come across:

Text boxes

There are a few problems with text boxes, but only one I shall mention here. Ever try setting up an autoselect text in Silverlight? Yes, you'll notice you don't have an option or property to do this. This to me is a massive gaping hole, because it's one of the very first things I write or implement every time I write a new Silverlight page with text fields. A really neat way around this is to create new behaviour for yourself and enable it via a property on the Silverlight page. Effectively all this behaviour needs to do is capture the focus event in the background and select all text upon entry

Combo boxes.

There are several annoying issues with Silverlight combo boxes. Firstly, have you ever tried to use a combo box in a really height restricted Silverlight page? It's just not at all viable and is something I have hit a few times in MS Dynamics CRM. If you need to develop a small web resource for a form that will contain a combo box you'll find it won't work, because your "frame" or area of use is restricted to the size of the Silverlight control, a bit like flash I guess. So even if your page is huge this is the sort of behaviour you'll notice happening on a combo box similar to the "Cars" combo box on my form:


The next problem with combo boxes that is quite annoying is if you add a blank row to a combo box it doesn't display it with the correct height:


See how we just have a little slither of a line above "Ford"? Well that's the blank row. This is incredibly annoying, but thankfully there's a quick but slightly dirty way to get around this, instead of using an empty string add a space to the string and suddenly it display it correctly.

Another nuisance of combo boxes is the direction the item list renders. Because of the first problem with Silverlight not being able to use the space outside of the control's frame it needs to be clever in which direction, up or down, the item list is rendered. This behaviour works fine first time around, as in the item list will render upwards if there is more room in that direction. But after that first time you've used the combo box it all goes a bit pear shaped:


(Yes, I notice "Aston" has a fantastic spelling fail... but I really can't be bothered to update the screen shot!)

There are a few ways to fix this, but the 2 best solutions, in my opinion, are

  1. Instead of binding to custom objects create ComboBoxItems and bind your combo box to a collection of those instead. (The Silverlight bug only rears its head when bound to custom objects)
  2. Create your own combo box template with the correct behaviour (can't believe I just wrote that!)
There are more combo box nuisances but these for me are the worst. Apart from the first one, which is more of a limitation, those second 2 are bugs that should never have made it to the public community.

Silverlight, JSON and dynamic types

The next thing I hate about Silverlight is the choice of deprecated libraries. I understand that they had to reduce the framework to make it somewhat viable for browser use, but why deprecate a library that's so closely linked to browsers and javascript? In short, if you want to parse JSON into a dynamic type in Silverlight you have to write your own parser. For more information see my blog post on the matter: silverlight-json-and-dynamic-types.

Assembly Library Caching

I don't have an issue with this actual concept, more of how it was implemented. It's quite common that you'll share XAPs between Silverlight pages and controls, such as images for example. So to reduce XAP sizes there is no real point including these in every single XAP file. If we split them up we can make use of caching and speed up our overall download times. The issue here is how this works in conjunction with MS Dynamics CRM (I haven't chosen which framework I will blame yet...). When you enable this in the project properties it splits up the relevant files into ZIP files (not XAP files). This is a royal pain in the buttocks, because you cannot upload ZIP files to MS Dynamics CRM. I have never bothered getting around this, but I'm guessing you could unzip the main XAP file, open up the AppManifest file, change the references to XAP instead of ZIP, and repack the XAP file again. Also you'd need to ensure you rename your other resources to XAP instead of ZIP. Unfortunately you would have to do this after every build, so you might have to write a post build script rather than performing it as a manual task. I haven't tested this yet either, but I'm fairly sure it would work fine once put in place.

Exposing browser error messages

Ever get that infamous error "The remote server returned an error: NotFound"? This is basically Silverlight telling you "something screwed up, but I have no idea what it was". To get your hands on the error messages you need to perform some registering on application start up:

WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp)

This will get you better error messages, but has a massive drawback. If you're making a lot of simultaneous asynchronous calls your browser will now frequently crash. I'm not sure what it's doing behind the scenes here, but neither Silverlight or your browser seem to like it. 99% of the time I find myself removing that line and accepting rubbish error messages.

I could go on...

... but I won't. I'm sure not the first person who's come up with a list like this and I'm guessing I won't be the last.

Conclusion

So, my conclusion out of all of this is let's all admit that Silverlight was a nicety at the time, and we all enjoyed it to begin with. But in reality, HTML wins yet again, so can we please just let it die with dignity. Yes, it's easier to throw together a UI in Silverlight, but once you start binding that baby together, setting up all of those models and viewmodels and have gone through about 4 iterations of async callback hell and rewriting missing feature/property helpers, you'd have written 80% / 90% of the exact same thing in HTML5 and JQuery. Also, what with Silverlight not seeing any more upgrades after 5 I envisage a framework that will retain these bugs for some time yet. So for me it's time to drop the Silverlight baton (yet again!) and push HTML5 where ever I go.


Tuesday, 12 March 2013

Silverlight - Passing javascript objects into a web page dialog arguments

I asked this question on Stack Overflow a while back and was a little surprised that it earned me the tumbleweed award. Has nobody seriously ever done this? Here's the original question: silverlight-passing-an-array-to-a-web-pages-dialog-arguments. It's a scenario quite a few Microsoft Dynamics CRM developers might come across if trying to invoke MSCRM web dialogs from silverlight. But for the purpose of this post I'll keep it somewhat generic.

Take this scenario, you have a web page dialog that requires some dialog arguments to work correctly. And it performs the following when loaded up:

var args = getDialogArguments();
if (args == null) return;
if (args.items == null) return;
var items = args.items;

var len = items.length;
for (var i = 0; i < len; i++)
{
  var item = items[i];
  cur.id = item.getAttribute("oid");
  cur.type = item.getAttribute("otype");
  cur.values = item.values;
  ... etc
}

We want to invoke this page via Silverlight, but the question is how do we pass in the arguments correctly? If we take a closer look at the dialog arguments (args) we can see that it has a member called "items" which is an array. Each of these items have attributes called "oid" and "otype". So let me explain what you need to do to set this up.

Before I continue, I want to add a "rule" before I explain how to achieve this. We need to do this without using "dynamic" because this causes you to have to reference the Microsoft.CSharp library which in turn causes your XAP file to bloat.

To start let's ask a slightly different question, what do these objects materialise themselves as within Silverlight? This I already knew the answer to, they are of type ScriptObject which is located in System.Windows.Browser. So why can't we just go and create one of these? Here is where I hit my first roadblock, it has an internal constructor. But, a quick search across the internet reveals that we can set this up using the following:

var dialogArgs = HtmlPage.Window.CreateInstance("Object");

And how about a property on this field?

dialogArgs.SetProperty("items", items);

Excellent, so now we're getting somewhere. Next up, how do you set up this array called "items"? Same way, but we can add indexers to it. Some code for setting up an array and an item will look something like this (I have just created a new GUID for the purpose of this example):

var item = HtmlPage.Window.CreateInstance("Object");
item.SetProperty("oid", Guid.NewGuid());
item.SetProperty("otype", "account");
var items = HtmlPage.Window.CreateInstance("Object");
items.SetProperty(0, item);

And finally, just pass that object straight into your dialog window like this:

var so = (ScriptObject)HtmlPage.Window.Invoke("showModalDialog", lookUpWindow, dialogArgs, "dialogWidth:600px;dialogHeight:600px;");

Job done.

Tuesday, 19 February 2013

CRM 2011 - How do you close an opportunity in Silverlight?

This is an interesting one I came across today. Due to using a different set of objects within the Silverlight framework you'll notice you're missing that valuable "WinOpportunityRequest" object! So, it begs the question, how do you close an opportunity in Silverlight?

Thankfully, we can go back to basics and raise an ordinary OrganizationRequest to achieve this. But before we do this let's take a look at what the code might look like in a plugin or standard CRM service request:


var opportunityClose = new Entity("opportunityclose");
opportunityClose.Attributes.Add("opportunityid",
    new EntityReference("opportunity", opportunityId));
opportunityClose.Attributes.Add("subject", "Opportunity expired");

var winOpportunity = new WinOpportunityRequest
    {
        OpportunityClose = opportunityClose,
        Status = new OptionSetValue(20001) // or whatever is valid for you
    };
service.Execute(winOpportunity);

You might be tempted to try achieve this using a SetState request, but you'll quickly find CRM complaining and telling you you're not allowed to do it. Instead, an easier way to do this is just replicate the above call as if you were using late binding. Like this:


var organizationRequest = new OrganizationRequest
   { RequestName = (won ? "WinOpportunity" : "LoseOpportunity") };
var opportunityClose = new Entity {LogicalName = "opportunityclose"};
SetAttribute(opportunityClose, "opportunityid", opportunityEntity.Id);
SetAttribute(opportunityClose, "subject", "Opportunity Expired");
organizationRequest["OpportunityClose"] = opportunityClose;
organizationRequest["Status"] = new OptionSetValue {Value = 20001}; // or whatever is valid for you



For reference, my SetAttribute function just looks like this:

private void SetAttribute(Entity entity, string attribute, object value)
{
  if (entity != null)
  {
    if (entity.Attributes == null) entity.Attributes = new AttributeCollection();
    if (entity.Attributes.ContainsKey(attribute))
    {
       entity.Attributes.SetItem(attribute, value);
    }
    else
    {
      entity.Attributes.Add(
          new Common.XrmSoapService.KeyValuePair<string, object> 
             { Key = attribute, Value = value });
    }
  }
}

Thursday, 1 March 2012

Silverlight MSCRM Dashboards - SoapHelper throwing "Object reference not set..." errors

So, you've created that shiny dashboard using Silverlight. You're using the SoapHelper provided by the SilverCrmSoap library. But any time you try to hit Dynamics you get a big dirty "Object reference not set to an instance of an object" error. What could be going on?

In the associated HTML page that loads the XAP file make sure you've referenced the correct javascript library:

<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>

9 times out of 10 you'll have referenced the Silverlight.js by mistake. I keep falling into this trap. I must have a memory like a sieve!

Tuesday, 25 October 2011

Silverlight, JSON and dynamic types...

Todays Dynamics 2011 task was to investigate using JSON within a Silverlight control. I started this task with high levels of excitement being new to the whole restful/JSON scene. Yes, I know, I am a bit of a late-comer here! The purpose of this task was to create a generic control I could put on a Dynamics CRM Form. This control was going to be driven by data from an external restful service. I don't want to have to reconfigure this control every time my data changes, so the purpose is to drive the label and value from this external source. So, this isn't about communicating with a Dynamics database using OData/JSON.

A quick google on how I do this results in me writing the following snippet:


var proxy = new WebClient();
proxy.OpenReadCompleted += proxy_OpenReadCompleted;
proxy.OpenReadAsync(new Uri("restfulurl"));

// Calling the following method...
private void proxy_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
var strm = new StreamReader(e.Result);
string jsonText = strm.ReadToEnd();
// ... etc
}


Next, how do we parse this into a dynamic type? In C# 4.0 we can do some really nice stuff like this:


using System.Web.Script.Serialization;

...

JavaScriptSerializer jss = new JavaScriptSerializer();
var d = jss.Deserialize(jsonText);


Here we hit a roadblock... JavaScriptSerializer was deprecated after Silverlight 1.1... HRMPH!

Silverlight, sometimes I hate you...

After countless searches and getting very close to embarking on a journey to build a JSON parser... I'm going to give a call out to a great blog post by jprichardson here. Saved me a few days effort, cheers mate! I quickly grabbed this source from GIT, built it, and stuck the silverlight libraries into my project. Now I do this:


var jsp = new JsonParser();
dynamic json = jsp.Parse(jsonText);



Whazzam, I have dynamic object from my JSON!

I have to say though, the deeper I dig into Silverlight the more shocked I am at the little things that take so much effort...

Monday, 19 September 2011

MS Dynamics 2011 and MVVM

So, I've been out of the Microsoft Dynamics world for about a year now (and semi AWOL in the process!), but after changing jobs I find myself back in this world using 2011. First up, Silverlight! Now, I have started to notice quite a bit that a lot of samples or guides on CRM 2011 and Silverlight don't seem to use MVVM. Why on earth is that? First things first, this in my opinion sheds a very bad light on Dynamics development. The very first project I get to work on had already been started by a previous developer. I open up the project and to my dismay I see absolutely zero view models. Task 1 - Push TDD.

So, why would we not use MVVM in Dynamics 2011? Let's walk through a sample I found and my phase 1 of refactoring. I open up the code behind and find this:


    private DataItemCollection _collection;
    public System.String _serverUrl;
    private string _userId;
    public MainPage()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        GetPageInformation();
         LoadCompanies();
    }

    private void LoadCompanies()
    {
        _collection = new DataItemCollection();
        QueryExpression query = QueriesHelper.GetAllAccounts();
        SoapHelper.BeginExecuteRetrieveMultiple(query, new AsyncCallback(GetAccountsCompleted), null);
    }

    private void GetAccountsCompleted(IAsyncResult result)
    {
        OrganizationResponse response = ((IOrganizationService)result.AsyncState).EndExecute(result);
        if ((response != null) && (response.Results.Count > 0))
        {

...I SHALL PROTECT YOUR EYES FROM A MONSTROSITY OF A FUNCTION HERE...

        }

        if (!Deployment.Current.Dispatcher.CheckAccess())
        {
            Deployment.Current.Dispatcher.BeginInvoke(delegate()
            {
                this.TreeViewClients.DataContext = _collection;
            });
        }
        else
        {
            this.TreeViewClients.DataContext = _collection;
        } 
    }

    private void GetPageInformation()
    {
        ScriptObject xrm = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
        ScriptObject page = (ScriptObject)xrm.GetProperty("Page");
        ScriptObject pageContext = (ScriptObject)page.GetProperty("context");
        _userId = (string)pageContext.Invoke("getUserId");
        _serverUrl = (string)pageContext.Invoke("getServerUrl");
    }


So what do we have here? What hints do I get as to why we wouldn't implement MVVM? Ok, so it looks like either the developer hadn't done much Silverlight/WPF before... or didn't quite know how to MVVM this. Looking at the code I can see that there's 2 things that we need to be considerate of when considering MVVM:

1. Queries need to run Asynchronous.

2. Access to our dispatcher for when we need to run "stuff" on the UI thread.

So how do we move this across to a MVVM structure then? Step 1, let's create a ViewModel:


    public class MyDamnViewModel
    {
        private readonly Dispatcher _dispatcher;

        public MyDamnViewModel()
        {
            _dispatcher = Deployment.Current.Dispatcher;
        }
    }


One plan here will be to allow for testing, but for now just copy the dispatcher to a local variable.

So what else can we add to this? How about everything! Considering the old form set everything up in the "onload" let's go with this for now. Let's just copy ALL methods across to my viewmodel and add the 2 method calls to our ViewModel constructor (GetPageInformation and LoadCompanies)... This will cause 1 major problem...


    this.TreeViewClients.DataContext = _collection;


What do we do? I notice my DataItemCollection already inherits from ObservableCollection... let's work with that for now and mark for review... Refactor the variable _collection so that we expose as a public ObservableCollection property:


    private DataItemCollection _collection; 
    public ObservableCollection<DataItem> NowWeHaveACollection 
    { 
        get { return _collection; } 
        set 
        { 
            _collection= (DataItemCollection)value; 
            NotifyPropertyChanged("NowWeHaveACollection"); 
        } 
    } 


I won't go into NotifyPropertyChanged stuff into too much depth here... but to get us by I just inherited from INotifyPropertyChanged and added this code to the view model:


    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }


Let's worry about refactoring and tidying all that up in a phase 2. For now I just want to get a basic MVVM structure in place.

So what did this new property give us? We can just set the property instead of directly accessing the control:


    NowWeHaveACollection = _collection;


Nearly there yet boss? Not quite. Code should be there, but we don't have anything hooked up. Let's set up our datacontext in the XAML:


    <usercontrol.datacontext> 
        <MyDamnViewModel /> 
    </usercontrol.datacontext>


And set our bindings up in the XAML instead of in the code. In my case it was a telerik tree view so we have converters and all sorts of rubbish in the code... I'll save that for another post. Let's pretend for now it's a bog standard list or something. Set this on your object in the XAML:


    ItemsSource="{Binding NowWeHaveACollection}"


And now our main form looks like this:


    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }


Ignoring the fact that it's called "MainPage" and some "phase 2" refactoring we're pretty much done.

So, to answer the question - No reason not to use MVVM in Dynamics 2011. I'd like to see less samples using direct code behind please!!