#Xamarin Grid Row and Column Definitions – Simplified!

Xamarin Grids let you layout your controls in, well, a Grid. Rows and Columns. Before Xamarin Forms 4.7 here’s how your layout a 3 by 3 grid:

<Grid>
 <Grid.RowDefinitions>
  <RowDefinition Height="Auto"></RowDefinition>
  <RowDefinition Height="*"></RowDefinition>
  <RowDefinition Height="*"></RowDefinition>
 </Grid.RowDefinitions>

 <Grid.ColumnDefinitions>
  <ColumnDefinition Width="*"></ColumnDefinition>
  <ColumnDefinition Width="*"></ColumnDefinition>
  <ColumnDefinition Width="*"></ColumnDefinition>
 </Grid.ColumnDefinitions>

That’s a lot of typing.

Once you upgrade to Xamarin Forms 4.7 you can then use a short form:

<Grid ColumnDefinitions="*,*,*" RowDefinitions="Auto,*,*">

Thanks Morton for your suggestion to the Xamarin team!

#VSCode: Quickly clone a git repository from #GitHub

download.pngI really enjoy Pluralsight.com as a place to refresh and learn new tech. In their courses they usually have example code on public GitHub repositories. Here’s the fast way to get them downloaded and up in VSCode (Visual Studio Code)

Here’s the fastest way to download a repo and open it in Visual Studio Code in the folder you’d like.

Step 1: Get the repo link.

For this example I’m using https://github.com/DanWahlin/Angular-JumpStart

Open the that link and press the Clone or Download button, then the Copy Link button seen here:

2020-03-11 09_43_03-DanWahlin_Angular-JumpStart_ Angular and TypeScript JumpStart example applicatio.png

Step 2: Open Windows Explorer to the folder you’d like it, then right click the folder and choose Open with Code 

2020-03-11 09_45_57-.png

Step 3: With VS Code open, press CTRL+<back tick> to open the Terminal

Type in git clone, then paste the URL:

2020-03-11 09_48_40-Welcome - Angular Architecture and Best Practices - Visual Studio Code.png

Done!

2020-03-11 09_50_11-Welcome - Angular Architecture and Best Practices - Visual Studio Code.png

 

#VSCode: Adding Windows Explorer Context Menus

1200px-Visual_Studio_Code_1.35_icon.svg.pngIn order to open VSCode in a specific folder, I used to Right Click the folder with SHIFT help down then choose Open PowerShell Window Here, then type code .

2020-03-11 09_05_44-Select Windows PowerShell.png

But, it turns out you can add Open in Visual Studio Code much easier if you choose the option in the Visual Studio Code installer.

If you have VSCode installed or not, just run the installer again. You can get it here.

Select these options:

2020-03-11 09_08_06-Setup - Microsoft Visual Studio Code (User).png

Now, when you right click a folder in Windows Explorer (SHIFT is not needed) you can choose the option:

2020-03-11 09_09_17-.png

Pro tip:

When you hold the SHIFT key down, you’ll get more options:

2020-03-11 09_11_22-.png

 

 

#VSCode: Handling Debugging Error “Breakpoint set but not yet bound”

download.pngYour Angular VS Code project can contain either a simple application, or multiple. When you have multiple, it can cause issues with connecting a debugger. Here’s the trick on how to handle that situation:

After you create a new Angular app then try to debug it in VSCode, you can often get the error “Breakpoint set but not yet bound” when you try to set a break point.

The fix is simple, just indicate the actual path to the webRoot, not just the workspaceFolder in the launch.json. This depends on if you opened the workspace in the root of the app, or deeper. It must take that into consideration. workspaceFolder or workspaceFolder/your-app/

{
 
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome against localhost",
      "url": "http://localhost:4200",
      "webRoot": "${workspaceFolder}",
      "sourceMaps": true,
      
      --SOMETIMES THIS IS NEEDED--
      "sourceMapPathOverrides": {
        "webpack:///./*": "${workspaceRoot}/*"
      }
       
    }
  ]
}

When you just have a single application (you don’t have a Project/ folder) you might have to set the webRoot to

"webRoot": "${workspaceFolder}"

Tip: When you open the folder in VS Code, do so at the root where the .vscode folder is. The e2e, src etc folders should all be at the top level.

2020-03-10 12_36_19-launch.json - application - Visual Studio Code.png

Some Gotchas:

  • It’s all about in what folder you opened your workspace in. This affects “webRoot”: “${workspaceFolder}/MAYBE-THIS-my-app/”
  • I’ve noticed that sometimes you still get the error “Breakpoint set but not yet bound” after the first try. After you build then try to debug, close VSCode and Chrome then open up again. That sometimes helps.
  • Make sure that tsconfig.json has sourceMaps set to true
  • When you run the app in Chrome, go to the dev tools and see if the ts files are displayed like this:
2020-03-11 11_23_43-Cloning.png

#VSCode: One #Angular Workspace with multiple projects

In VS Code (Visual Studio Code) you can create a single Workspace with 1 git repo but contain multiple projects. This lets you create library and app and contain them all under the same angular.json as well.

Here’s how.

Create a folder where you’d like to create the Workspace. In my example e:\ngLearn\CLIPlayGround

2020-03-09 10_42_08-Select Windows PowerShell.png

VS Code will appear. Open the Terminal Ctrl+Shift+`

Now, ask the CLI to create an Workspace, but with no default application:

ng new –createApplication=false

In the prompt give your workspace a name, like my-workspace

2020-03-09 10_46_33-CLIPlayGround - Visual Studio Code.png

It’s take a few minutes to set up. If you open Windows Explorer you’ll see the new workspace made, but without any application inside:

2020-03-09 10_47_29-my-workspace.png

Let’s now add a few applications by using the commands in the new folder

cd .\my-workspace\

ng g application

Run this twice and you’ll then notice there are 2 applications in your workspace: under a new Projects folder

2020-03-09 10_50_11-CLIPlayGround - Visual Studio Code.png

 

 

#NDepend – Rate your .Net code

download.pngRecently I tried out the NDepend tool to see how it can help with code reviews and code quality assessment of .Net code in Visual Studio.

I found this tool to be helpful and it quickly found areas of code in our projects that need some extra help.

Once you run the tool, you get a report which lays out the good, the bad and the ugly in your code. This is helpful as it lets us focus on what is important, and perhaps how much work needed to fix an issue.

Here’s a summary example:

2020-03-09 09_01_20-NDepend Report.png

 

You can then drill down and see the detail:

2020-03-09 09_02_25-NDepend Report.png

NDepend comes with a Visual Studio plug as well as a stand alone version.

Xamarin iOS: Getting a list of Contacts

Getting Contacts from iOS and Android is a little harder than it really should be. I hope one day it will be added to Xamarin.Essentials, but until then, here’s how to get them in a simple way.

I won’t cover all the DTOs needed as those are simple and not really part of the point, but here’s the overview of the PhoneContact class I’ll use:

public class PhoneContact
{
    public string Identifier { getset; }
    public string FirstName { getset; }
    public string LastName { getset; }
    public List<PhoneNumberContact> PhoneNumbers { getset; } = new List<PhoneNumberContact>();
    public List<EmailContact> Emails { getset; } = new List<EmailContact>();
    public List<AddressContact> PostalAddresses { getset; } = new List<AddressContact>();
    public List<DateLabelContact> Dates { getset; } = new List<DateLabelContact>();
    public string DepartmentName { getset; }
    public List<InstantMessageContact> InstantMessageAddresses { getset; } = new List<InstantMessageContact>();
    public string JobTitle { getset; }
    public string OrganizationName { getset; }
    public List<SocialContact> SocialProfiles { getset; }
    public string Notes { getset; }
}

To get the contacts out of iOS, you can use this service. The namespace is marked as a Dependency so you could inject it into a Xamarin.Forms project.

using Contacts;
using Foundation;
using MyApp.Extensions;
using MyApp.Interfaces;
using MyApp.iOS.Dependencies;
using MyApp.Services.Contacts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Telerik.XamarinForms.Common.iOS;
using Xamarin.Forms;
 
[assemblyDependency(typeof(ContactService))]
namespace MyApp.iOS.Dependencies
{
 
    /// <summary>
    /// SEE for more help on Android: https://github.com/jamesmontemagno/Xamarin.Mobile/blob/master/MonoDroid/Xamarin.Mobile/Contacts/ContactHelper.cs
    /// </summary>
    public class ContactService : IContactService
    {
 
        public Task<IList<PhoneContact>> GetContactListAsync()
        {
            return Task.Run(GetContactList);
        }
 
        public IList<PhoneContactGetContactList()
        {
            List<PhoneContactresult = new List<PhoneContact>();
            try
            {
                var keysToFetch = new[]
                {
                    CNContactKey.GivenName,
                    CNContactKey.FamilyName,
                    CNContactKey.EmailAddresses  ,
                    CNContactKey.PhoneNumbers  ,
                    CNContactKey.OrganizationName,
                    CNContactKey.Dates,
                    CNContactKey.DepartmentName,
                    CNContactKey.Identifier,
                    CNContactKey.InstantMessageAddresses,
                    CNContactKey.JobTitle,
                    CNContactKey.Birthday,
                    CNContactKey.PostalAddresses,
                    CNContactKey.SocialProfiles,
                };
                var contactList = ReadRawContactList(keysToFetch);
 
                if (contactList != null)
                    result = GetContacts(contactList).ToList();
            }
            catch (Exception ex)
            {
                App.Logger.LogError(ex);
                result = new List<PhoneContact>();
            }
 
            return result;
        }
 
        private static IEnumerable<CNContactReadRawContactList(IEnumerable<NSStringkeysToFetch)
        {
            using (var store = new CNContactStore())
            {
                var allContainers = store.GetContainers(nullout _);
                return ContactListFromAllContainers(keysToFetchallContainersstore);
            }
 
        }
 
        private static IEnumerable<CNContactContactListFromAllContainers(
            IEnumerable<NSStringkeysToFetch,
            IEnumerable<CNContainerallContainers,
            CNContactStore store)
        {
            var nsStrings = keysToFetch.ToList();
            var contactList = new List<CNContact>();
            foreach (var container in allContainers)
            {
                var contacts = ReadFromContainer(nsStringscontainerstore);
                if (contacts != null)
                {
                    contactList.AddRange(contacts);
                }
                else
                {
                    Console.WriteLine("ERROR unable to get contacts");
                }
            }
 
            return contactList;
        }
 
        private static IEnumerable<CNContactReadFromContainer(
            IEnumerable<NSStringkeysToFetch,
            CNContainer container,
            CNContactStore store)
        {
            try
            {
                using (var predicate = CNContact.GetPredicateForContactsInContainer(container.Identifier))
                {
                    return store.GetUnifiedContacts(predicatekeysToFetch.ToArray(), out _);
                }
            }
            catch (Exception ex)
            {
                App.Logger.LogError(ex);
            }
 
            return null;
        }
 
        private static IEnumerable<PhoneContactGetContacts(IEnumerable<CNContactcontactList)
        {
            return contactList.Select(GetContact);
        }
 
        private static PhoneContact GetContact(CNContact item)
        {
            if (item == nullreturn new PhoneContact();
 
            try
            {
                var contact = new PhoneContact
                {
                    FirstName = item.GivenName,
                    LastName = item.FamilyName,
                    PhoneNumbers = item?.PhoneNumbers?
                        .Where(x => x.Value != null)
                        .Select(x => new PhoneNumberContact() { Name = x.Label, Phone = x.Value?.StringValue })
                        .ToList(),
                    Emails = item.EmailAddresses?
                        .Where(x => x.Value != null)
                        .Select(x => new EmailContact() { Name = x.Label, Email = x.Value?.PathComponents?.FirstOrDefault() })
                        .ToList(),
                    OrganizationName = item?.OrganizationName,
                    Dates = item?.Dates?
                        .Where(q => q.Value != null)
                        .Select(x => new DateLabelContact { Name = x.Label, Date = x.Value?.Date?.ToDateTime() }).ToList(),
                    DepartmentName = item.DepartmentName,
                    Identifier = item.Identifier,
                    InstantMessageAddresses = item?.InstantMessageAddresses?
                        .Where(x => x.Value != null)
                        .Select(x => new InstantMessageContact { Service = x.Value?.Service, Username = x.Value?.Username })
                        .ToList(),
                    JobTitle = item.JobTitle,
 
                    PostalAddresses = item?.PostalAddresses?
                        .Where(x => x.Value != null)
                        .Select(x => new AddressContact()
                        {
                            City = x.Value.City,
                            Country = x.Value.Country,
                            PostalCode = x.Value.PostalCode,
                            State = x.Value.State,
                            Street = x.Value.Street
                        }).ToList(),
                    SocialProfiles = item?.SocialProfiles?
                        .Where(x => x.Value != null)
                        .Select(x => new SocialContact() { Username = x.Value.Username, Service = x.Value.Service, }).ToList()
                };
 
                var birthDate = item?.Birthday?.Date?.ToDateTime().Date;
                if (birthDate != null)
                {
                    contact?.Dates?.Add(new DateLabelContact()
                    {
                        Name = "BIRTHDATE",
                        Date = birthDate
                    });
                }
 
                //  Console.WriteLine(contact.ToJson());
                return contact;
            }
            catch (Exception ex)
            {
                App.Logger.LogError(ex);
            }
 
            return null;
        }
 
    }
}

 

In my next post I’ll show you how to get it from Android, much harder sadly 😦

 

#Redis Pub/Sub using .Net and #Docker – The easy way

PNG-image-00D34A55B376-1.pngSo, you’ve heard about Redis and want to go kick the tires. How does one do Pub/Sub between two .Net clients with Redis in the middle?

The Redis/Docker Part:

  • Docker will host our Redis. Go to https://hub.docker.com , sign up and download the Docker software. Install it and login with your new Docker Hub account. It’ll take a few moments to start.
  • Open a Powershell and enter this Docker command to download and run Redis in docker on the standard port:

docker run -it -d -p 6379:6379    redis 

(Pro tip: Here’s how to enable local file usage for Redis to record data to: docker run -it -d -p 6379:6379 -v e:/docker/redis:/data redis  where e: is a drive you enabled access to via the Docker Dashboard )

  • Close Powershell, it’s not needed any more.

Redis is now running, to check it, open Docker Dashboard by right clicking the little whale icon in your system tray. Docker will give the Redis a funny random name:

2020-02-20 08_23_02-.png        2020-02-20 08_23_48-Container list.png

The Coding Part:

Open Visual Studio Twice and create 2 Console apps. One will publish and one will subscribe.

In each project, add the Nuget StackOverflow.Redis

In the publisher app, add this class and call it from Main():

using StackExchange.Redis;
using System;
using System.Threading.Tasks;
 
 
namespace RedisTest
{
    public class Publisher
    {
        private ConnectionMultiplexer redis;
        public Publisher()
        {
            redis = ConnectionMultiplexer.Connect("localhost");
        }
 
        public async Task PublisherMessages()
        {
            ISubscriber sub = redis.GetSubscriber();
            for (int i = 0; i < 1300; i++)
            {
                sub.Publish("message"Guid.NewGuid().ToString());
            }
        }
    }
}

In the subscriber app, add this class and call it from Main():

using StackExchange.Redis;
using System;
using System.Threading.Tasks;
 
 
namespace RedisTest
{
    public class Subscriber
    {
        private ConnectionMultiplexer redis;
        public Subscriber()
        {
            redis = ConnectionMultiplexer.Connect("localhost");
        }
 
        public async Task SubscriberMessages()
        {
            ISubscriber sub = redis.GetSubscriber();
            await sub.SubscribeAsync("message"async (channelvalue) =>
            {
                Console.WriteLine(value);
            });
        }
    }
}

Let’s run them!

Start up the Subscriber, then the Publisher. The publisher will pump the messages to Redis, Redis will them pump them to the subscriber. Even on a low powered system, you should be able to do 1500 messages a second. There are basically an unlimited number of subscribers and publishers.

Afterward:

Get a tool like Redily to allow you to view the data inside Redis.