Development

Silverlight's DatePicker / MessageBox Bug

by JBrooks 26. December 2012 10:49

Silverlight has an annoying bug that has been around since at least 2008. If you have a MessageBox popup in a DatePicker’s SelectedDateChanged event, then the calendar will stay open and the SelectedDateChanged will fire for each time the mouse pointer hovers over a new date.  See HERE for someone asking for help on this and their sample code.

It happens because the DatePicker still flags the left mouse button as being down – the up button event was eaten by the message box.

I have a page where the user works on data for a given day, and if they change the date the page will be refreshed with the new day’s data.

My work flow was that if a user changed data for a day and then changed the date, I would prompt them with “Save Changes?” – Yes, No, Cancel.  If they selected Cancel I would just set the DatePicker’s date back. 

No matter what they selected the DatePicker’s calendar would remain down and the SelectedDataChanged would fire for each date that was hovered over.

I didn’t like any of the answers to this problem that I found.  So I changed the workflow so that the prompt to save changes wasn’t necessary anymore.  So this is the new workflow.

1. New page, you can change the date, refresh the data, or start editing the data.

image

2. The data was changed. Now changing the date is disabled until you make a choice between Save and Cancel. I like how this visibly tells the user that the data is dirty.

image

This is done by capturing the KeyUp event for the parent control that holds all of the editing controls. In my example it is a ContentControl but it could be a grid, etc.  Note that the control’s in the images above are outside of my ContentControl for the data.

private void ccData_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Left:
                case Key.Right:
                case Key.Down:
                case Key.Up:
                case Key.Tab:
                    break;

                default:
                    if (Status != PageStatus.Changed)
                    {
                        Status = PageStatus.Changed;
                    }
                    break;
            }
        }

When the Status property gets set the showing, hiding and disabling of controls takes place depending on the new value of PageStatus.

If the user clicks “Save” then the data is saved, the page is refreshed and they are back at step 1 with the date selection enabled. If they click “Cancel”, then I go to step 3.

3. They clicked “Cancel”.  Now they are free to change the date which pulls up another day’s data and throws away their changes (step 1).   Or they can continue editing, which puts them back to step 2.  Or they can click Refresh which will pull the old data from the database and the page will be back to step 1.

image

I like doing it this way so I don’t have to mess with the SelectedDateChanged event and I like the visual cues.

Tags:

Development | Silverlight

Generic Boolean to Text Converter

by JBrooks 20. July 2012 11:52
I’ve seen this done a few different ways, but I think this is the best way because you can reuse it. Below is the converter, notice the 2 public properties.
public class BoolToTextConverter : IValueConverter
{
    public string TrueText { get; set; }
    public string FalseText { get; set; }

    public object Convert(object value, Type targetType, object parameter, 
                                                   System.Globalization.CultureInfo culture)
    {
        return ((bool)value) ? TrueText : FalseText;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
                                                  System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

}
Then in your XAML resource section you would set the properties:
<localHelpers:BoolToTextConverter x:Key="boolToTextConverter">
    <localHelpers:BoolToTextConverter.TrueText>
        Sent
    </localHelpers:BoolToTextConverter.TrueText>
    <localHelpers:BoolToTextConverter.FalseText>
        Not Sent
    </localHelpers:BoolToTextConverter.FalseText>
</localHelpers:BoolToTextConverter>
So this is setting the 2 public properties. The final part is to bind this to the TextBox, in this example I’m binding to a boolean property named “sent”. The result is that the text will be “Sent” if it is true and “Not Sent” if it is false.
<TextBlock Text="{Binding Path=sent, Converter={StaticResource boolToTextConverter}}"
            VerticalAlignment="Center" Margin="4"  
            TextAlignment="Center" />

Tags:

Silverlight | Development | XAML

Smart Phone Development for .NET Developers

by JBrooks 18. February 2012 21:26

As a .NET developer I was feeling left out of the IPhone, Android and BlackBerry world, until now! 

Let’s start by looking at the sample application that I created in Visual Studio as learning exercise.  The application allows you to create a note at a GPS location.  I called it NoteToPlace and you can open it in your phone by going to www.NoteToPlace.com   You can register and start using it now.

Here is what the opening screen looks like on an IPhone:

Home

This is the basic look and feel out of the box given the tools I used (talked about below.)  You can install this as an app on the IPhone so the browser related stuff at the top goes away.  The user will also have an icon on their home screen just like every other app - so it isn’t obvious to them that this is really a browser based app.  And no you don’t have to go through the App store.  See this link for more details: HTML5 IPhone App.

Once the user clicks the “Create A Note” option from the main menu they get prompted to allow the application to know their GPS location, and then they get this page to add a note:

CreateNote

A few things to note.  First, notice the upper left hand corner shows me as logged in (as user “j”).  All of the ASP.Net membership stuff came free out of the box (or template.) That includes the pages for a user to register and to log in.

Also note that I used a different emulator for this second image – this is how it looks in a Motorola Droid X. I tried out the following emulators:

  1. http://iphonetester.com/
  2. http://dev.opera.com/articles/view/opera-mobile-emulator/
  3. http://ripple.tinyhippos.com/
  4. https://www.google.com/chrome/

(The images are in this order.) I mostly used the desktop version of Chrome for development and I use fiddler2 for debugging.  I just change the size to be close to a phone’s. All of these emulators were free.

In my sample app, if I go back and select the second main menu option “View My Notes”, I will get a list of my notes. Show below in the Ripple emulator:

Ripple

It is easy to create lists like these and you see them a lot. If I then click the “Map” link for an item I will see the Google map of the location where I left the note:

Map

 

Note that I didn’t spend any time optimize this application for performance (it is just a test of the technologies.)  It can take some time on the first load. HTML5 has an advanced application caching feature that I didn’t use at all, but looks promising for a real effort.  Also note that the Google map page has problems displaying in some situations (sometimes you just need to hit refresh.)

Developer’s Section

Now for the technologies used.  First, I downloaded and installed MVC4 beta from ASP.Net.  This installs Visual Studio templates that allow you to create a MVC 4 project and then select “Mobile Application” as an option.

Doing this will install JQuery Mobile into your project which is the part that does a lot of the user interface.  From there you are doing HTML 5 in the markup, EF4 for the data access layer (YEAH!) and MVC for the application. There was also more JavaScript / JQuery then I would have liked.

I didn’t know MVC before this application and learned it from these free videos: MVC from Pluralsight.  (I took heavy notes while I watched them.) You can build a mobile application using ASP.Net WebForms, but in my research it became clear that MVC with JQuery Mobile is the future here. You can add MVC to your currently existing WebForms application, see HERE.

Also, I found this book to be very helpful when I started:

 

It is a good overview of mobile development and is heavy on JQuery Mobile. They even have a chapter talking about PhoneGap where you can turn your application into a native IPhone or Android application.  PhoneGap is open source and more information can be found HERE.

The Google maps page was done with a JavaScript call to the Google maps API. More information can be found HERE.

I wouldn’t build a game using this technology, but it looks very promising for building web related apps. I think it will be valuable in extending some of the web applications that I currently work on. Now I have the option to include a little mobile where it makes sense and adds value.

Tags:

Development | HTML5 | MVC | Phone

One Click CheckBox in a Silverlight DataGrid

by JBrooks 16. December 2011 09:54

I found this code on the internet to create a CheckBox in a DataGrid that wouldn’t require more than one click to change.  But it didn’t work for me.

<sdk:DataGridTemplateColumn>
    <sdk:DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <CheckBox IsThreeState="False" IsChecked="{Binding Path=IsActive, Mode=TwoWay}" 
                      HorizontalAlignment="Center" VerticalAlignment="Center" />
        </DataTemplate>
    </sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
The reason it didn’t work is because I also had this as part of my DataGrid
<sdk:DataGrid CurrentCellChanged="dgTotals_CurrentCellChanged" …

 

That method had a dgTotals.BeginEdit(); in it to allow the user to begin editing the DataGrid cells without having to first click on them.  The simple solution was to just skip that for my CheckBox column.

if(dgTotals.CurrentColumn != null && dgTotals.CurrentColumn.DisplayIndex != 1)
    dgTotals.BeginEdit();

Tags:

Development | Silverlight

Troubleshooting the Silverlight to RIA Services Connection

by JBrooks 6. December 2011 11:41

The first thing I do when troubleshooting a Silverlight startup error (like where it just has the startup progress bar sitting at 100% forever) is to make sure I can reach the RIA Service and it doesn’t return an error.

This example shows how to decode the URL back to the service:

http://localhost:1893/MWEntry5-Web-Services-ScheduleDomainService.svc

This is just the path delimited with dashes to the service as seen in your project and also with all of the periods changed to dashes and then just add “.svc” at the end.

clip_image001

You should see a page for the RIA Service if you put this URL in a browser.

When calling a method it is ClientBin first, followed by the name space, followed by “binary” and then your method name.  Here are 2 examples:

http://localhost:1893/ClientBin/MWEntry5-Web-Services-ScheduleDomainService.svc/binary/GetSchedulableEntities

http://MyDomain/MyWebApp/ClientBin/MyWebApp-Web-AuthenticationService.svc/binary/GetUser

I just needed to put this somewhere so I can reference it in the future.

Tags:

Silverlight | Development

.NET and Silverlight Rounding

by jbrooks 10. November 2011 09:09
In Excel 34.5 will round to 35 and in .NET it will round to 34. This is because .NET defaults to using the MidpointRounding.ToEven mode – also called banker’s rounding.  In .NET you have the option of overriding this by passing in the other MidpointRounding like:
 
    Math.Round(34.5, MidpointRounding.AwayFromZero);   // Now rounds to 35

A little annoying since in all of the applications that I’ve ever written the users expect rounding to match Excel’s and I’ve never needed the default. Worse, in Silverlight you don’t even have the option to change the default when using Math.Round().

So I had to write my own extension that can be used like:

 
    double myValue = 34.5;
    
    int myRoundedValue = (int) myValue.RoundCorrect(0);  // rounds to 35
 

Here is the extension code:

public static class DoubleExtensions
{
    public static double RoundCorrect(this double d, int decimals)
    {
        double multiplier = Math.Pow(10, decimals);
 
        if (d < 0)
            multiplier *= -1;
                                   
        return Math.Floor((d * multiplier) + 0.5) / multiplier;
    }
}

or if you want the code for a normal method it would be

public double RoundCorrect(double d, int decimals)
{
    double multiplier = Math.Pow(10, decimals);
 
    if (d < 0)
        multiplier *= -1;
 
    return Math.Floor((d * multiplier) + 0.5) / multiplier;
 
}

Tags:

Development | Silverlight

MSBuild with Silverlight / RIA Services

by jbrooks 2. November 2011 09:36

I introduced a Silverlight project to my ASP.Net application and every thing worked fine in development but when I went to build it with out automatic build process MSBuild would kick out the following error:

[msbuild]   App.xaml.cs(2,17): error CS0234: The type or namespace name 'Services' does not exist in the namespace 'MyProject.Web' (are you missing an assembly reference?)

Had to change a registration key, the DefaultToolsVersion was set to 2.0, changed  it to 4.0

image

 

Ran the install selecting repair for the Silverlight 5.0 sdk (silverlight5_sdk.exe) – it was originally installed as part of the tool kit, but found others saying the real install still needed to be done to get it to work with MSBuild.

Next go the error:  error MSB4044: The "CreateRiaClientFilesTask" task was not given a value for the required parameter "ClientFrameworkPath"

Had to install EntityFramWork4.1 on my build server and then reboot.

Now on to the next problem.

Tags:

Silverlight | Development

Binding a Silverlight DataGrid’s ComboBox

by JBrooks 6. May 2011 07:13

Some things in Silverlight seem harder than they should be.  Binding a list of strings to a combobox is one such thing.  This involves the following steps to do a simple bind.

1. Have a private list of the strings in your page code.

 
private List<string> FuelList = new List<string>()
         {
             "OIL",
             "GAS",
             "COAL"
         };
 

2. In your initialization method add the list as a resource so your XAML can use it.  This must be done before the InitializeComponent(); call.

 
        public ucMyUserControl()
        {
            this.Resources.Add("FuelList", FuelList);
 
            InitializeComponent();
        }

 

3. Then you can reference the list as the source to you combo box.

<sdk:DataGrid AutoGenerateColumns="False"  MinHeight="130" Name="dgMyDataGrid" 
    ItemsSource="{Binding Path=ScheduleRows, Mode=TwoWay}"  
        CurrentCellChanged="dgMyDataGrid_CurrentCellChanged">
 
 
  <sdk:DataGrid.Columns>
     <sdk:DataGridTemplateColumn  Header="Fuel Type">
         <sdk:DataGridTemplateColumn.CellTemplate>
             <DataTemplate>
                  <TextBlock Margin="2" VerticalAlignment="Center"  
            HorizontalAlignment="Left" Text="{Binding FUEL_TYPE}" Width="120" />
             </DataTemplate>
             </sdk:DataGridTemplateColumn.CellTemplate>
                  <sdk:DataGridTemplateColumn.CellEditingTemplate>
                     <DataTemplate>
                         <ComboBox Height="23" Name="cbxFuelType" 
                                 ItemsSource="{StaticResource FuelList}"
                                 SelectedValue="{Binding Path=FUEL_TYPE, Mode=TwoWay}" Width="120">
 
                         </ComboBox>
                      </DataTemplate>
             </sdk:DataGridTemplateColumn.CellEditingTemplate>
        </sdk:DataGridTemplateColumn>
...

4. One last item, notice the datagrid’s current cell changed event is wired up to start editing without requiring the user to click first.  That method is simple enough:

private void dgMyDataGrid_CurrentCellChanged(object sender, EventArgs e)
  {
      dgMyDataGrid.BeginEdit();
  }

 

 

That gives me my datagrid with my combo box:

image

 

 

Here is a sample solution that has the full code: 

 ComboBoxTest.zip (1,016.17 kb)

Tags:

Development | Silverlight

Having the Result Set of a Stored Proc Sent to You by RSS Feed.

by JBrooks 14. December 2010 12:44

I wanted to monitor one of my system from my desk top and from my phone.  I found a simple solution whereby I can subscribe to the result set of a stored proc by using RSS. So I can have this feed MS Outlook, my phone or my web browser.

First, Visual Studio 2010 makes creating an RSS feed a simple matter that is about 1 page of code.

I simply add an ASPX page to my project and remove most of the markup so it only has 2 lines:

 
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="rss.aspx.cs"  Inherits="RSS.rss"  %>
 
<%@ OutputCache Duration="60" VaryByParam="none" %>

Next the code behind simply calls the stored proc placing the results into a table and then loading up some of the RSS related collections VS2010 gives you. 

 
using System;
using System.Data;
using System.ServiceModel.Syndication;
using System.Web;
using System.Collections.Generic;
using System.Xml;
 
namespace RSS
{
public partial class rss : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
        string id = Request.QueryString["id"];
 
        // I don't want just anyone to subscribe, so you have to know the GUID.
        if (id== null || id != "23F14EA1-1B20-443B-9B94-92C4EA4A8099")
            throw new Exception("Guid not reconized");
 
 
        Response.ContentType = "application/atom+xml";
 
        // this gets the data from the database and populates a table.
        DataTable dt = cDB.getFeed();
        SyndicationFeed myFeed = new SyndicationFeed();
 
        myFeed.Title = TextSyndicationContent.CreatePlaintextContent("SampleApp Activity");
        myFeed.Description = TextSyndicationContent
            .CreatePlaintextContent(@"A syndication of the most recently 
                    SampleApp activity including exceptions.");
        myFeed.Links.Add(SyndicationLink.CreateAlternateLink(
            new Uri(GetFullyQualifiedUrl("/rss.aspx"))));
        myFeed.Links.Add(SyndicationLink.CreateSelfLink(
            new Uri(GetFullyQualifiedUrl(Request.RawUrl))));
        myFeed.Copyright = TextSyndicationContent
                        .CreatePlaintextContent("Copyright SampleApp");
        myFeed.Language = "en-us";
 
 
        List<SyndicationItem> feedItems = new List<SyndicationItem>();
        foreach (DataRow dr in dt.Rows)
        {
 
            SyndicationItem item = new SyndicationItem();
            item.Title = TextSyndicationContent.CreatePlaintextContent(dr["title"].ToString());
            SyndicationPerson authInfo = new SyndicationPerson();
            authInfo.Email = "SampleApp@YourDomain.com";
 
            item.Authors.Add(authInfo);
            // RSS feeds can only have one author.
 
            // The stored proc returns different categories of data that I am interested in.
            switch (dr["category"].ToString())
            {
                case "WindFarms":
                case "WindFarms ":
                    item.Links.Add(SyndicationLink.CreateAlternateLink(
                        new Uri(GetFullyQualifiedUrl("/WindFarms.aspx"))));
                    authInfo.Name = "SampleApp WindFarm";
                    break;
 
                case "Exceptions":
                    item.Links.Add(SyndicationLink.CreateAlternateLink(
                        new Uri(GetFullyQualifiedUrl("/ErrorLog.aspx"))));
                    authInfo.Name = "SampleApp Exception";
                    break;
 
                default:
                    authInfo.Name = "SampleApp";
                    break;
 
            }
            item.Summary = TextSyndicationContent.CreatePlaintextContent(
                dr["summary"].ToString());
 
            item.Categories.Add(new SyndicationCategory(dr["category"].ToString()));
            item.PublishDate = DateTime.Parse(dr["pubdate"].ToString());
            item.LastUpdatedTime = item.PublishDate;
            item.Id = item.PublishDate.ToString();
 
            // Add the item to the feed
            feedItems.Add(item);
        }
 
 
        myFeed.Items = feedItems;
 
 
        XmlWriter feedWriter = XmlWriter.Create(Response.OutputStream);
 
        // Use Atom 1.0 
        Atom10FeedFormatter atomFormatter = new Atom10FeedFormatter(myFeed);
        atomFormatter.WriteTo(feedWriter);
 
        feedWriter.Close();
 
    }
 
    private string GetFullyQualifiedUrl(string s)
    {
        Uri u = new Uri(HttpContext.Current.Request.Url, s);
        return u.ToString();
 
    }
 
}
}

 

To have this feed my Outlook RSS folder I just need to right click “RSS Feeds” and select “Add a New RSS Feed…”. 

AddFeedMenu

Then enter the URL of my RSS feed.   Don’t forget to add the GUID at the end with     ?id=23F14EA1-1B20-443B-9B94-92C4EA4A8099

AddFeed

If you site uses authentication in your site you will have to turn it off for the rss.aspx page.  To do this you would add an entry in your web config file:

 
<location path="rss.aspx">
    <system.web>
        <authorization>
            <allow users="*"/>
        </authorization>
    </system.web>
</location>

You should now have a folder in Outlook that will get populated by the feed.

Tags:

ASP.Net | Development

Logging Exceptions and the Commands that Caused Them.

by jbrooks 14. December 2010 12:12

For a long time I’ve been logging all of my exceptions to a table that looks like:

ExceptionTable

Now for exceptions that were generated by the database I want to also log the exact database call that caused the exception.  It will make debugging go a lot faster.  For this I have a new table:

ExceptionCmdTable

This will get the exceptionId from the first table and the a string that caused the exception.  Something like:

saveSomething @id=100, @UserName='Jakey',  @IPAddress='100.100.100.100',  @editBy='JBrooks'

So the first part of logging the Exception is two methods on my base class.

 
public void LogException(Exception ex)
{
    LogException(ex, null);
}
 
 
public void LogExceptionx(Exception ex, SqlCommand objBadCmd)
{
    int id = 0;
 
    if (this._WritingErrorLog == true)
    {
        // don't want to get in an infinite loop.
        return;
    }
    else
    {
        this._WritingErrorLog = true;
    }
 
 
    try
    {
 
        SqlCommand objCmd = this.GetNewCmd("dbo.insertException");
 
        string message = "";
 
        // Get the userId of the user that got the error.
        if (System.Web.HttpContext.Current.Request.Cookies["UID"] != null)
            message = System.Web.HttpContext.Current.Request.Cookies["UID"].Value + "|";
 
        // Add the command that gave the error.
        if (objBadCmd != null && !string.IsNullOrEmpty(objBadCmd.CommandText))
            message += objBadCmd.CommandText + "|";
 
 
        message += ex.ToString();
 
        if (message.Length > 8000)
            message = message.Substring(0, 7999);
 
 
        objCmd.Parameters.Add("@Message", SqlDbType.VarChar, 8000).Value = message;
 
        objCmd.Parameters.Add("@StackTrace", SqlDbType.VarChar, 8000).Value =
            ((ex.StackTrace == null) ? "no stack trace." : ex.StackTrace);
 
        //insert the exception and get the new ID.
        id = this.ExecuteScalarInt(objCmd);
 
        if (id > 0 && objBadCmd != null)
            insertExceptionCmd(id, objBadCmd);
 
    }
    catch (Exception ExLogException)
    {
        this.LogExceptionToFile(ex);
        this.LogExceptionToFile(ExLogException);
    }
 
    this._WritingErrorLog = false;
 
}

Now the part that generates the command string is another method that gets called be the method above:

 
private string getCmdString(SqlCommand objBadCmd)
{
 
    if (objBadCmd == null)
        return string.Empty;
 
    if (objBadCmd.Parameters == null || objBadCmd.Parameters.Count == 0)
        return objBadCmd.CommandText;
 
    StringBuilder sb = new StringBuilder();
 
    try
    {
        sb.Append(objBadCmd.CommandText+" ");
 
        for (int i = 0; i < objBadCmd.Parameters.Count; i++)
        {
            SqlParameter p = objBadCmd.Parameters[i];
 
            if (i > 0)
                sb.Append(", ");
 
            sb.Append(p.ParameterName + "=");
 
            if (p.Value == null)
                sb.Append("null");
            else
            {
                switch (objBadCmd.Parameters[i].SqlDbType)
                {
                    case SqlDbType.BigInt:
                    case SqlDbType.Int:
                    case SqlDbType.Float:
                    case SqlDbType.Decimal:
                    case SqlDbType.SmallInt:
                    case SqlDbType.Money:
                    case SqlDbType.Image:
                    case SqlDbType.Real:
                    case SqlDbType.SmallMoney:
                    case SqlDbType.TinyInt:
                    case SqlDbType.DateTimeOffset:
                        sb.Append(p.Value.ToString());
                        break;
 
                    default:
                        sb.Append("'" + p.Value.ToString() + "'");
                        break;
 
                }
            }
        }
    }
    catch (Exception ex)
    {
        return "getCmdString created its own error processing:  " + 
            sb.ToString() + "      +++++" + ex.ToString().Substring(0, 8000);
    }
 
    return sb.ToString();
}

So now I have a complete logging of exception in my applications and it is a simple matter to make a page where I can view them, or make an RSS feed where they are sent to me.

Tags:

Development | SQL | ASP.Net