Contents tagged with Silverlight

  • Converting from Silverlight To Universal Apps – MVVM, ListView and Commands

    Converting a Windows Phone Silverlight app to a Universal WinRT app isn’t straight forward, and it’s hard to Google for answers. I converted one of my not too advanced apps to universal Windows/Phone and here are some of the things I had to do. The quick-list for Xaml changes is here.

    I’m using MVVMLight because it makes it so much easier to develop apps. When developing the Silverlight app I used Interation.Triggers and EventTrigger/EventToCommand to fire off commands in my ViewModel from clicked ListBox items. When converting to universal/winrt I ran into problems with referencing the Microsoft.Expression.Interactions assemblies for the Windows and Windows Phone projects so I decided to code up a simple ItemClickCommand instead which uses an attach property on the ListView. Based (more or less a replica) on the code by Marco Minerva, the command-class looks like this:

    public static class ItemClickCommand
    {
    public static readonly DependencyProperty CommandProperty =
    DependencyProperty.RegisterAttached("Command", typeof (ICommand),
    typeof (ItemClickCommand), new PropertyMetadata(null, OnCommandPropertyChanged));

    public static void SetCommand(DependencyObject d, ICommand value)
    {
    d.SetValue(CommandProperty, value);
    }

    public static ICommand GetCommand(DependencyObject d)
    {
    return (ICommand) d.GetValue(CommandProperty);
    }

    private static void OnCommandPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
    var listView = dependencyObject as ListViewBase;
    if (listView != null)
    listView.ItemClick += (sender, itemClickEventArgs) =>
    {
    var viewBase = sender as ListViewBase;
    var command = GetCommand(viewBase);

    if (command != null && command.CanExecute(itemClickEventArgs.ClickedItem))
    command.Execute(itemClickEventArgs.ClickedItem);
    };
    }
    }
     
    The command in the ViewModel is set up like this:

    public class SportsViewModel : ViewModelBase
    {
    public ObservableCollection<Sport> Sports { get; set; }
    public RelayCommand<Sport> SportSelected { get; private set; }

    public SportsViewModel()
    {
    SportSelected = new RelayCommand<Sport>(sport =>
    {
    if (sport == null) return; //should not happen
    _navigationService.NavigateTo(typeof(LeaguesView), sport.Id);
    });

    }

    //and so on...
    }
     
    And this is how I use this command in the XAML view:

    <ListView IsItemClickEnabled="True" SelectionMode="None"
    commands:ItemClickCommand.Command="{Binding SportSelected}"
    ItemsSource="{Binding Sports}" >
    <ListView.ItemTemplate>
    <DataTemplate>
    <TextBlock Text="{Binding Name}" />
    </DataTemplate>
    </ListView.ItemTemplate>
    </ListView>

    Remember to set the IsItemClickEnabled and SelectionMode properties for your ListView or nothing will happen when you click the items in the list Smile

  • Extended Silverlight AutoCompleteBox for Handling Enter and Command Binding

    In our RIA Services application, we’re using the AutoCompleteBox control, but what if you want to select something from the list, press Enter and bind to a Command in a viewmodel? Sort of like the Button Command property.

    I decided to extend the control and add a “Command” DependencyProperty for an ICommand which is executed when Enter is pressed and if an item is selected in the AutoCompleteBox. Works for us:

    public class AutoCompleteBoxEx : AutoCompleteBox

    {

        protected override void OnKeyDown(KeyEventArgs e)

        {

            if (SelectedItem != null && e.Key == Key.Enter)

            {

                if (Command != null)

                    Command.Execute(SelectedItem);

     

            }

            base.OnKeyDown(e);

        }

     

        public static readonly DependencyProperty CommandProperty =

            DependencyProperty.RegisterAttached("Command",

                        typeof(ICommand),

                        typeof(AutoCompleteBoxEx),

                        null);

     

        public ICommand Command

        {

            get { return (ICommand)GetValue(CommandProperty); }

            set { SetValue(CommandProperty, value); }

        }

    }

  • Silverlight AutoCompleteBox DataTemplate

    This is more a note for myself how to do this. The Silverlight 4 SDK contains a pretty good AutoCompleteBox that you can use. Download/install the SDK and and the control will be available in your Silverlight Controls Toolbox.

    If you try to use this with RIA Services, you may notice it is displaying the Id/Key in the dropdown, but you yourself want to show a name or description. In that case you have to add a DataTemplate section to the AutoCompleteBox:

    <sdk:AutoCompleteBox x:Name="FilterTextBox"
                           FilterMode="Contains"
                           MaxDropDownHeight="150"
    ItemsSource="{Binding Data, ElementName=dds}"
                           ValueMemberBinding="{Binding Name}">
        <sdk:AutoCompleteBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" />
            </DataTemplate>
        </sdk:AutoCompleteBox.ItemTemplate>
    </sdk:AutoCompleteBox>

    That should do it.

  • Masked Silverlight TextBox for Swedish National Identification Number (Personnummer)

    In our upcoming Silverlight 4 project there’s a need for a textbox with a mask for entering a Swedish national identification number (personnummer) with either 10 or 12 digits, in the form of yyyymmdd-nnnn. It’s quite simple, I just want to make sure only digits are entered and that the dash (-) is placed automatically and a few other small things that I’d like to tweak.

    As far as I can see (correct me if I’m wrong here) there’s no mask feature in SL4 (yet), so I’m simply going to inherit and extend the built in TextBox. Validation of the number is done in the viewmodel instead of built into the control. See upcoming blogpost for that.

    So, enough talk already, hope it can be of use:

    public class PersonnummerTextBox : TextBox
    {
        public bool TwelveDigits { get; set; }

        private int _leftPart;
        private Key _lastKey;

        public PersonnummerTextBox()
        {
            TextChanged += OnTextChanged;
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            _leftPart = TwelveDigits ? 8 : 6;
        }

        /// <summary>
        /// Only allow digits and tab, no shifts or ctrl
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            _lastKey = e.Key;   //used in OnTextChanged to handle Del key

            base.OnKeyDown(e);

            if (((e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) ||
                (e.Key >= Key.D0 && e.Key <= Key.D9) ||
                e.Key == Key.Tab) &&    //so that we can TAB out from the control ;)
                (Keyboard.Modifiers & ModifierKeys.Shift) == 0 &&
                (Keyboard.Modifiers & ModifierKeys.Control) == 0)
            {
                e.Handled = false;
                return;
            }

            e.Handled = true;
        }

        /// <summary>
        /// Make sure the dash (-) is placed on the correct place automatically
        /// either as yyyymmdd-nnnn or yymmdd-nnnn
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            var leftPartPlusDash = (_leftPart + 1);
            var cursorPosition = SelectionStart;
            var strippedText = Text.Replace("-", "");

            if (strippedText.Length > _leftPart)
            {
                //move the cursor after the dash
                if (strippedText.Length == leftPartPlusDash &&
                    cursorPosition == leftPartPlusDash &&
                    _lastKey != Key.Delete)
                    cursorPosition = (_leftPart + 2);

                //put a dash in the right place
                Text = string.Format("{0}-{1}",
                    strippedText.Substring(0, _leftPart),
                    strippedText.Substring(_leftPart, Math.Min(4, strippedText.Length - _leftPart)));
            }
            else
                Text = strippedText;

            //move cursor to the wanted position
            SelectionStart = cursorPosition;
        }
    }
  • Silverlight Watermark TextBox Behavior

    (using Visual Studio 2010 and .NET/Silverlight 4)

    Maybe there is a working solution for this already out there, but I created my own Silverlight Behavior for a basic TextBox Watermark which might be useful.

    I wanted to use it like this in my XAML (look at the behaviors tag):

    <TextBlock Margin="5">Watermarked textbox:</TextBlock>
    <TextBox Margin="5">
        <Interactivity:Interaction.Behaviors>
            <local:Watermark Text="Watermark" Foreground="LightGray" />
        </Interactivity:Interaction.Behaviors>
    </TextBox>

     

    The result should be something like this:

    image

     

    To create a Behavior for Silverlight, you must get hold of the System.Windows.Interactivity assembly which ships with Expression Blend. In my system it’s located at:

    c:\Program Files (x86)\Microsoft SDKs\Expression\Blend\Silverlight\v4.0\Libraries\System.Windows.Interactivity.dll

     

    And the code for the Behavior:

    public class Watermark : Behavior<TextBox>
    {
        private bool _hasWatermark;
        private Brush _textBoxForeground;

        public String Text { get; set; }
        public Brush Foreground { get; set; }

        protected override void OnAttached()
        {
            _textBoxForeground = AssociatedObject.Foreground;

            base.OnAttached();
            if (Text != null)
                SetWatermarkText();
            AssociatedObject.GotFocus += GotFocus;
            AssociatedObject.LostFocus += LostFocus;
        }

        private void LostFocus(object sender, RoutedEventArgs e)
        {
            if (AssociatedObject.Text.Length == 0)
                if (Text != null)
                    SetWatermarkText();
        }

        private void GotFocus(object sender, RoutedEventArgs e)
        {
            if (_hasWatermark)
                RemoveWatermarkText();
        }

        private void RemoveWatermarkText()
        {
            AssociatedObject.Foreground = _textBoxForeground;
            AssociatedObject.Text = "";
            _hasWatermark = false;
        }

        private void SetWatermarkText()
        {
            AssociatedObject.Foreground = Foreground;
            AssociatedObject.Text = Text;
            _hasWatermark = true;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.GotFocus -= GotFocus;
            AssociatedObject.LostFocus -= LostFocus;
        }
    }

    Like so many Watermark-solutions out there I’m hooking into the GotFocus/LostFocus events and to the work there. Works for me. Love Behaviors.

  • Silverlight DataGrid and DataPager Notes

    I need to write these things down somewhere...

    Brad's excellent tutorials and samples for learning Silverlight + RIA Services: http://blogs.msdn.com/b/brada/archive/2009/10/27/index-for-business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update.aspx

    Scott's super blog post about columns and the DataGrid: http://blogs.msdn.com/b/scmorris/archive/2008/03/27/defining-columns-for-a-silverlight-datagrid.aspx (it's getting "old" but still rocks)

    To use the DataPager properly with a DomainDataSource, you have to set a sort order. The docs for the DataPager says:

    If you use the DataPager in an application with an Entity Framework data store, you must order the data returned by your queries for the DataPager to work correctly. The Entity Framework does not support paging of data without an OrderBy clause.

    This can be done with the SortDescriptor property of the DomainDataSource, for example:

    <riaControls:DomainDataSource  Name="dds" AutoLoad="True"         
                                   QueryName="GetVesselsQuery"                               
                                   LoadSize="20" LoadedData="dds_LoadedData">
        <riaControls:DomainDataSource.DomainContext>
            <Services:SomeDomainContext />
        </riaControls:DomainDataSource.DomainContext>
        <riaControls:DomainDataSource.SortDescriptors>
            <riaControls:SortDescriptor PropertyPath="name" Direction="Ascending"/>
        </riaControls:DomainDataSource.SortDescriptors>
    </riaControls:DomainDataSource>

  • TFS Automatic Build Reporting Access to XAP Denied

    If you’re working with TFS and automatic builds of Silverlight apps, you may happen to stumble upon this error in the end of the build:

    Copying file C:\Builds\2\xxx\yyy\Binaries\zzz.xap failed. Access to the path 'C:\Builds\2\xxx\yyy\Sources\www\ClientBin\zzz.xap' is denied.

    The reason for this error I’m not 100% sure of, but it seems that the target file is read only and the build service do not have the proper access to overwrite it. Others have had the same problem with this and the suggested workaround/solution is to look up the \ClientBin\zzz.xap file in Source Control Explorer, right click to bring up the properties and the Security tab, then mark “Deny” to read for the Build Service. I did it for both the Builders group and the project collection build service accounts:

    image

    I found the tip for the solution here.