lunedì 12 novembre 2012

Diplay CP7–WPF Touch Screen Manager

In queste ultime settimane ho iniziato il porting di un applicativo sviluppato su .NETMF verso .NET Gadgeteer. Oltre al cambi del HW con le inevitabili modifiche, al cambio delle librerie utilizzate, ecc. mi sono imbattuto in alcune problematiche relative all’utilizzo del display CP7. In dettaglio il driver del display, fornito con il relativo modulo Gadgeteer, non si integra minimamente con gli eventi WPF ma fornisce, in modo abbastanza raw, le varie funzionalità di touch. Il codice che sto convertendo ha una parte di interfaccia U,I scritta utilizzando il FW WPF del .NETMF, abbastanza corposa e ovviamente utilizza gli eventi di touch dei singoli controlli. A questo punto le possibilità sono due, riscrivere la parte di UI, magari utilizzando la libreria Glide, o scrivere un manager che catturi gli eventi del touch del display convertendoli in eventi gestibili da WPF e dai relativi controlli. Ovviamente ho scelto la seconda strada. Dopo alcune ore, spese a decompilare con dotpeek sial il driver del display ma soprattutto la parte di tinycore del .NETMF ho trovato la soluzione. Tutto ruota attorno alla classe TouchEventArgs. Di seguito riporto lo spezzone del codice saliente del manager che ho implementato.

private void OnDisplayScreenPressed(Display_CP7 sender, Display_CP7.TouchStatus touchStatus)
{
    try
    {
        if (_isTouchValid || SkipAllEvent)
            return;
 
        if (touchStatus.numTouches > 0)
        {
            _isTouchValid = true;
 
            int len = 0;
            for (int i = 0; i < touchStatus.touchPos.Length; i++)
                len += touchStatus.touchPos[i].bActive ? 1 : 0;
 
            _touchInputs = new TouchInput[len];
 
            for (int i = 0, idx = 0; i < touchStatus.touchPos.Length; i++)
            {
                if (!touchStatus.touchPos[i].bActive)
                    continue;
 
                _touchInputs[idx++] = new TouchInput()
                                            {
                                                X = touchStatus.touchPos[i].xPos,
                                                Y = touchStatus.touchPos[i].yPos
                                            };
 
                if (touchStatus.touchPos[i].bActive)
                {
                    _touchX = touchStatus.touchPos[i].xPos;
                    _touchY = touchStatus.touchPos[i].yPos;
                }
            }
 
            var control = _display.WPFWindow.ChildElementFromPoint((int)_touchX, (int)_touchY);
 
            if (control != null && control.IsVisible && control.IsEnabled)
            {
                var arg = new Microsoft.SPOT.Input.TouchEventArgs(null, DateTime.Now, _touchInputs);
 
                arg.RoutedEvent = TouchEvents.TouchDownEvent;
                control.RaiseEvent(arg);
            }
        }
        else
        {
            _isTouchValid = false;
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
    }
}

Il metodo riporta la gestione dell'evento di pressed del display. Nel metodo sono verificati i punti di touch, viene ricercato il controllo su cui è stato toccato il display e viene così costruito l’oggetto TouchEventArgs. Il tutto poi viene sparato come evento al controllo precedentemente ricercato tramite la RaiseEvent.




private void OnDisplayScreenReleased(Display_CP7 sender)
{
    try
    {
        if (!_isTouchValid || SkipAllEvent)
            return;
 
        _isTouchValid = false;
 
        var control = _display.WPFWindow.ChildElementFromPoint((int)_touchX, (int)_touchY);
 
        if (control != null && control.IsVisible && control.IsEnabled)
        {
            var arg = new Microsoft.SPOT.Input.TouchEventArgs(null, DateTime.Now, _touchInputs);
            arg.RoutedEvent = TouchEvents.TouchUpEvent;
            control.RaiseEvent(arg);
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
    }
}


La stessa cosa viene fatto anche nell’evento di release del display, ovviamente sempre ricercado il controllo su cui si rilascia il tocco dal display.


In questo link potete trovare l’intero esempio basato su una FEZ Hydra e relativo display CP7. Alla fine, con qualche ora di lavoro, sono riuscito a convertire l’intero progetto salvando interamente la parte UI WPF.


Nota: per l’utilizzo del codice si deve aver installato l’ultima versione del SDK di GHI (Release condidate 4.2 Oct. 26, 2012) , questo per problemi di risoluzione a runtime della classe TouchEventArgs !