Wpf and emoticons

How to display emoticons/emoji in a Wpf TextBox/TextBlock?

I made a test project to show how to do this (you can download it at the end of this post).
It is all about playing with Inlines in the TextBox’s FlowDocument and in the TextBlock.
The main code is this:

private static int FindFirstEmoticon(string text, int startIndex, out string emoticonFound)
{
  InitEmoticons();
  emoticonFound = string.Empty;
  int minIndex = -1;
  foreach (string e in m_Emoticons.Keys) {
    int index = text.IndexOf(e, startIndex);
    if (index >= 0) {
      if (minIndex < 0 || index < minIndex) {
        minIndex = index;
        emoticonFound = e;
      }
    }
  }
  return minIndex;
} // FindFirstEmoticon

public static void ParseText(FrameworkElement element)
{
  InitEmoticons();
  TextBlock textBlock = null;
  RichTextBox textBox = element as RichTextBox;
  if (textBox == null)
    textBlock = element as TextBlock;

  if (textBox == null && textBlock == null)
    return;

  if (textBox != null){
    FlowDocument doc = textBox.Document;
    for (int blockIndex=0; blockIndex < doc.Blocks.Count; blockIndex++){
      Block b = doc.Blocks.ElementAt(blockIndex);
      Paragraph p = b as Paragraph;
      if (p != null) {          
        ProcessInlines(textBox, p.Inlines);
      }
    }
  }else{
    ProcessInlines(null, textBlock.Inlines);
  }
} // ParseText

private static void ProcessInlines(RichTextBox textBox, InlineCollection inlines)
{
  for (int inlineIndex=0; inlineIndex < inlines.Count; inlineIndex++){
    Inline i = inlines.ElementAt(inlineIndex);
    if (i is Run) {
      Run r = i as Run;
      string text = r.Text;
      string emoticonFound = string.Empty;
      int index = FindFirstEmoticon(text, 0, out emoticonFound);
      if (index >= 0) {
        TextPointer tp = i.ContentStart;
        bool reposition = false;                
        while (!tp.GetTextInRun(LogicalDirection.Forward).StartsWith(emoticonFound)) 
          tp = tp.GetNextInsertionPosition(LogicalDirection.Forward);
        TextPointer end = tp;
        for (int j=0; j<emoticonFound.Length; j++)
          end = end.GetNextInsertionPosition(LogicalDirection.Forward);
        TextRange tr = new TextRange(tp, end);
        if (textBox != null)
          reposition = textBox.CaretPosition.CompareTo(tr.End) == 0;
        tr.Text = string.Empty;

        string imageFile = m_Emoticons[emoticonFound];
        Image image = new Image();
        BitmapImage bimg = new BitmapImage();
        bimg.BeginInit();
        bimg.UriSource = new Uri(imageFile, UriKind.Relative);
        bimg.EndInit();
        image.Source = bimg;
        image.Width = 24;

        InlineUIContainer iui = new InlineUIContainer(image, tp);
        iui.BaselineAlignment = BaselineAlignment.TextBottom;

        if (textBox != null && reposition)
          textBox.CaretPosition = tp.GetNextInsertionPosition(LogicalDirection.Forward);
      }
    }
  }
} // ProcessInlines

The code parses the current content of the TextBox/TextBlock and replaces the emoticons text (initialized int the InitEmoticons method) with the correct image.
If the element passed is a TextBox it also repositions the cursor in the correct position.

You can download the test project here.

WhatsApp su Windows?

E’ possibile utilizzare WhatsApp su pc Windows?
Per ora sembra di no a meno di non emulare un intero sistema Android.
Esiste però una libreria ben fatta e stabile scritta in Python che permette di interfacciarsi col servizio di messaggistica di WhatsApp.
La libreria si chiama Yowsup ed è utilizzata da Wazapp (un client WhatsApp per Nokia N9).

Ho provato quindi ad utilizzare la libreria scrivendo una interfaccia grafica in WPF.
E’ venuta bene?

Forse lo rilascerò (cambiandogli nome ed icona, ovviamente) una volta testato per bene. 😉

Bing Maps e WPF

Mi è capitato di dover sviluppare una applicazione desktop usando il WPF che visualizzasse le mappe di Bing.
Pensavo sarebbe stato facile (“E’ tutto di Microsoft, sarà sicuramente facile”, pensavo) ed invece con qualche rapida ricerca su internet mi sono reso conto che non esiste un metodo “standard”, semplice e supportato direttamente da Microsoft di fare ciò.

Ho cercato un po’ di documentazione, e vagliato almeno due possibilità

  • Scrivere una semplice applicazione Silverlight, renderla disponibile su qualche server ed usarla all’interno di una window WPF (ma anche l’embedding di Silverlight in WPF non è a noi “concesso” da Microsoft in modo nativo)
  • Scrivere una pagina HTML ed interagire con essa tramite Javascript.

La soluzione che ho scelto di utilizzare è la seconda: più semplice e senza necessità di hostare niente su un server.

Partiamo con un po’ di documentazione:
Qui potete trovare un esempio di utilizzo dei servizi SOAP di Bing.
Qui trovate un esempio di interazione tra WPF e WebBrowser tramite javascript.

Il progetto di esempio, che potete scaricare in fondo a questa mini-guida, è composto principalmente da una pagina HTML che si occupa di visualizzare le mappe di Bing e da una finestra WPF che interagisce con la pagina HTML fornendo coordinate e ricevendo informazioni da essa.
La classe che permette la conunicazione tra WPF e Javascript è la seguente:

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[ComVisible(true)]
public class ObjectForScriptingHelper
{
    Window1 mExternalWPF;
    public ObjectForScriptingHelper(Window1 w)
    {
        this.mExternalWPF = w;
    }

    public void ActionChanged(string action)
    {
      this.mExternalWPF.ActionChanged(action);
    }

    public void PinAdded(string latitude, string longitude)
    {
      this.mExternalWPF.PinAdded(latitude, longitude);
    }
}

Come vedete è molto semplice. Da notare soltanto i metodi ActionChanged e PinAdded, che sono quelli che verrano chiamati da Javascript per comunicare con il programma principale.

La pagina HTML usa i normali metodi AJAX utilizzati sul web.
Qui potete trovare molti esempi di utilizzo.

La parte WPF non fa altro che visualizzare il contenuto HTML in un controllo WebBrowser, comunicando con essa attraverso Javascript.

Visto che un esempio vale più di mille parole, ecco il progetto di test.
BingMapsTest

Per poterlo utilizzare dovete registrare un account al Bing Maps Account Center ed inserire la chiave nella costante:

private const string m_BingKey = "Your Bing Maps Key";