Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1906, comments - 2047, trackbacks - 352

My Links

News

Your host

This is me! Questo spazio è dedicato a Microsoft® .NET®, di cui sono molto appassionato :-)

Cookie e Privacy

Disabilita cookie ShinyStat

Microsoft MVP

My MVP Profile

Microsoft Certified Professional

Microsoft Specialist

Xamarin Certified Mobile Developer

Il mio libro su VB 2015!

Pre-ordina il mio libro su VB 2015 Pre-ordina il mio libro "Visual Basic 2015 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su WPF 4.5.1!

Clicca sulla copertina per informazioni! E' uscito il mio libro "Programmare con WPF 4.5.1". Clicca sulla copertina per informazioni!

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.
If you're not an Italian user, please visit my English blog

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!

Altri spazi

GitHub
I miei progetti open-source su GitHub

Article Categories

Archives

Post Categories

Image Galleries

Privacy Policy

Windows Phone: salvare immagini in un database SQL CE

Poter contare su SQL Server Compact Edition come ambiente di memorizzazione dei dati nelle proprie app per Windows Phone è un'ottima opportunità, perchè consente di archiviare dati in modo efficiente e con strumenti efficienti, sia a livello di data store che a livello di data model con LINQ to SQL.

Vi rimando ad un precedente articolo su VB T&T per iniziare a capire come creare ed integrare database di SQL CE nelle vostre app. Potrebbe capitarvi l'esigenza di memorizzare delle fotografie, sia scattate che prelevate da un album, all'interno del db. Non è la soluzione di archiviazione più efficiente: questo tipo di approccio è più "time consuming" e richiede un maggiore sforzo in termini di risorse, quindi solitamente si preferisce la memorizzazione delle foto nell'isolated storage e nel db si preferisce salvare il solo percorso. Tuttavia, potreste avere necessità di mantenere dati e foto in un solo file, per svariate ragioni (es. backup di dati su un servizio sulla Cloud).

A livello di database, SQL CE 3.5 mette a disposizione diversi tipi adatti allo scopo, ma il migliore è image, che non pone limitazioni troppo restrittive (ad esempio varbinary non permette dati più grandi di 8 Kb). Nel nostro data model, quindi, la proprietà che servirà a memorizzare l'immagine sarà così predisposta:

    Dim _image As Byte()       <Column(DbType:=("image"), UpdateCheck:=UpdateCheck.Never)> Public Property ItemImage As Byte()

Quindi,la proprietà è di tipo Byte() ma nel db sarà di tipo image come stabilito attraverso l'attributo Column di LINQ to SQL. E' anche importante determinare l'impostazione UpdateCheck come Never, altrimenti nasceranno dei conflitti ogni qual volta otterrete l'istanza di un record esistente in fase di modifica.

Prima di vedere come salvare una foto nel db, è bene fare una considerazione: i telefoni più recenti, come ad esempio il mio Lumia 920, catturano fotografie ad altissima qualità, il che si traduce in diversi megabyte di spazio. Questo non è pensabile in un db presente nello storage di un'app. Quindi dovremo ridimensionare l'immagine e codificarla in modo da ridurre, seppur di poco, la sua qualità finale.

Nel seguente codice, il metodo GetImageBytes traduce uno stream in un oggetto di tipo WriteableBitmap, che è il tipo da usare nella manipolazione delle foto in Windows Phone. Per fare ciò si avvale del metodo statico DecodeJpeg della classe PictureDecoder. Notate come le dimensioni della foto vengano stabilite in 500 x 500 e la qualità dell'immagine sia stabilita nel 75% di quella originale:

    Private Function GetImageBytes(str As StreamAs Byte()         Try             Using ms As New MemoryStream                 Dim wb As WriteableBitmap = PictureDecoder.DecodeJpeg(str, 500, 500)                 wb.SaveJpeg(ms, 500, 500, 0, 75)                 Return ms.ToArray()             End Using           Catch ex As Exception             Return Nothing         End Try     End Function       Private Sub AddImageToDatabaseItem(image As Stream, dataItem As Item)         Try             'Get Byte array of Image             Dim byteArray = GetImageBytes(image)             If byteArray Is Nothing Then Throw New InvalidOperationException             '  Add Image to Database             dataItem.ItemImage = Nothing             dataItem.ItemImage = byteArray           Catch ex As Exception             Throw         End Try     End Sub

Il metodo AddImageToDatabaseItem invoca il metodo precedente passando uno stream, che in realtà è lo stream catturato dalla fotocamera, e un oggetto rappresentante un record nel nostro database.

Se l'array di byte ottenuto è nullo, al 99% l'utente ha selezionato un formato immagine non supportato quindi viene sollevata eccezione. Diversamente, assegnamo alla proprietà apposita il risultato ottenuto. Ovviamente, non dobbiamo dimenticare di invocare il SubmitChanges dell'istanza del DataContext per persistere i dati sul db.

L'acquisizione da fotocamera funzionerà in modo simile al seguente, dove, tra l'altro, impostiamo a priori le dimensioni accettate (sempre 500 x 500 come nell'esempio di cui sopra):

    Private Sub ShowCamera()              Dim p As New Microsoft.Phone.Tasks.PhotoChooserTask             AddHandler p.Completed, AddressOf p_completed             p.PixelWidth = 500             p.PixelHeight = 500             p.ShowCamera = True             p.Show()      End Sub       Private Sub p_completed(sender As Object, e As PhotoResult)         Try             If e.TaskResult = TaskResult.OK Then                 AddImageToDatabaseItem(e.ChosenPhoto, Me.newItem)             End If         Catch ex As Exception             MessageBox.Show(ex.Message)         End Try     End Sub

Ora è chiaro il perchè dell'argomento di tipo stream per i metodi visti in precedenza: il risultato restituito dal PhotoChooserTask, e.ChosenPhoto, è proprio uno stream.

Il passaggio successivo è quello di recuperare la foto dal db e visualizzarla nella user interface, magari attraverso un controllo Image. Per poter sfruttare il data-binding, possiamo scrivere un converter di questo tipo:

Imports System.Windows.Data Imports System.IO Imports System.Windows.Media.Imaging Imports Microsoft.Phone   Public Class ImageConverter     Implements IValueConverter           Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfoAs Object Implements IValueConverter.Convert         If TypeOf (value) Is Byte() Then             Dim ms As New MemoryStream(CType(value, Byte()))             Dim writeBitmap As WriteableBitmap = PictureDecoder.DecodeJpeg(ms, 500, 500)             Return writeBitmap         Else             Return Nothing         End If       End Function       Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfoAs Object Implements IValueConverter.ConvertBack         Throw New NotImplementedException     End Function End Class

L'oggetto proveniente dal db è di tipo Byte(); con lo stesso procedimento visto in precedenza, viene ottenuta un'istanza corrispondente dell'oggetto WriteableBitmap, che è un tipo collegabile in binding al controllo Image. A questo punto, in XAML:

xmlns:local="clr-namespace:DoveLHaiMesso" 
    <phone:PhoneApplicationPage.Resources>         <local:ImageConverter x:Key="ImageConverter"/>     </phone:PhoneApplicationPage.Resources>
<Image Source="{Binding ItemImage, Converter={StaticResource ImageConverter}}"/>                     

Dichiaro l'istanza del converter nelle risorse e poi la uso nel binding del controllo.

Et voilà!

Alessandro

 

Print | posted on martedì 17 settembre 2013 14:46 | Filed Under [ Silverlight e Windows Phone ]

Powered by:
Powered By Subtext Powered By ASP.NET