En este tutorial, nos centraremos en cómo crear una aplicación multimedia para Windows Phone aprovechando la cámara del dispositivo, interactuando con la biblioteca de medios y explorando las posibilidades de Photos Hub.
La cámara es una de las características más importantes de los dispositivos Windows Phone, especialmente gracias a Nokia, que ha creado algunos de los mejores teléfonos con cámara disponibles en el mercado..
Como desarrolladores, podemos integrar la experiencia de la cámara en nuestra aplicación para que los usuarios puedan tomar fotos y editarlas directamente dentro de la aplicación. Además, con la función de la aplicación Lens que analizaremos más adelante, es aún más fácil crear aplicaciones que puedan reemplazar la experiencia nativa de la cámara..
Nota: para interactuar con la cámara, debe habilitar la ID_CAP_IS_CAMERA
capacidad en el archivo manifiesto.
El primer paso es crear un área en la página donde podamos mostrar la imagen grabada por la cámara. Vamos a utilizar VideoBrush
, que es uno de los pinceles XAML nativos que pueden incrustar un video. Lo usaremos como fondo de una Lona
control, como se muestra en la siguiente muestra:
Observe la CompositeTransform
que se ha aplicado; Su propósito es mantener la orientación correcta del video, según la orientación de la cámara..
Ahora que tenemos un lugar para mostrar la imagen de la cámara en vivo, podemos usar las API que se incluyen en el Windows.Phone.Media.Capture
espacio de nombres. Específicamente, la clase disponible para tomar fotos se llama PhotoCaptureDevice
(luego veremos otra clase para grabar videos).
El siguiente código es un ejemplo de inicialización:
anulación protegida vacío asíncrono OnNavigatedTo (NavigationEventArgs e) Resolución de tamaño = PhotoCaptureDevice.GetAvailableCaptureResolutions (CameraSensorLocation.Back) .First (); PhotoCaptureDevice camera = aguarda PhotoCaptureDevice.OpenAsync (CameraSensorLocation.Back, resolución); video.SetSource (cámara); previewTransform.Rotation = camera.SensorRotationInDegrees;
Antes de inicializar la transmisión en vivo, debemos hacer dos elecciones: qué cámara usar y cuál de las resoluciones disponibles queremos usar.
Esto lo logramos llamando al GetAvailableCaptureResolutions ()
método en el PhotoCaptureDevice
clase, pasando como parámetro a CameraSensorLocation
Objeto que representa la cámara que vamos a utilizar. El método devolverá una colección de las resoluciones compatibles, que están identificadas por el tamaño
clase.
Sugerencia: es seguro usar el código anterior porque cada dispositivo con Windows Phone tiene una cámara trasera. Si queremos interactuar con la cámara frontal, es mejor verificar si una está disponible primero, ya que no todos los dispositivos con Windows Phone tienen una. Para ello, puede utilizar el DisponibleSensorLocalización
propiedad de la PhotoCaptureDevice
clase, que es una colección de todas las cámaras compatibles.
Una vez que hayamos decidido qué resolución usar, podemos pasarla como un parámetro (junto con la cámara seleccionada) a la OpenAsync ()
método de la PhotoCaptureDevice
clase. Se devolverá un PhotoCaptureDevice
objeto que contiene el feed en vivo; simplemente tenemos que pasarlo a la SetSource ()
método de la VideoBrush
.
Como ya se mencionó, manejamos la orientación de la cámara usando la transformación que hemos aplicado a la VideoBrush
: configuramos el Rotación
utilizando la SensorRotaciónIndegrees
Propiedad que contiene la rotación del ángulo actual..
Nota: Puede obtener un error cuando intenta pasar un PhotoCaptureDevice
objeto como un parámetro de la SetSource ()
método de la VideoBrush
. Si es así, tendrás que añadir el Microsoft.Devices
espacio de nombres a su clase, ya que contiene un método de extensión para el SetSource ()
método que soporta el PhotoCaptureDevice
clase.
Ahora la aplicación simplemente mostrará la transmisión en vivo de la cámara en la pantalla. El siguiente paso es tomar la foto..
La técnica utilizada por la API es crear una secuencia de marcos y guardarlos como una secuencia. Desafortunadamente, hay una limitación en el SDK actual: solo podrás tomar una foto a la vez, por lo que solo podrás usar secuencias creadas por un cuadro.
privado async void OnTakePhotoClicked (transmisor de objeto, RoutedEventArgs e) CameraCaptureSequence cameraCaptureSequence = camera.CreateCaptureSequence (1); MemoryStream stream = new MemoryStream (); cameraCaptureSequence.Frames [0] .CaptureStream = stream.AsOutputStream (); aguarda camera.PrepareCaptureSequenceAsync (cameraCaptureSequence); aguarda cameraCaptureSequence.StartCaptureAsync (); stream.Seek (0, SeekOrigin.Begin); Biblioteca MediaLibrary = new MediaLibrary (); library.SavePictureToCameraRoll ("picture1.jpg", secuencia);
El proceso comienza con una CameraCaptureSequence
Objeto, que representa el flujo de captura. Debido a la limitación de un solo picutre mencionada anteriormente, podrá llamar al CreateCaptureSequence ()
método de la PhotoCaptureDevice
clase solo por pasar 1
como su parámetro.
Por el mismo motivo, solo vamos a trabajar con el primer fotograma de la secuencia que se almacena dentro del Marcos
colección. los CaptureStream
La propiedad del marco debe establecerse con el flujo que vamos a utilizar para almacenar la imagen capturada. En la muestra anterior, usamos un MemoryStream
para almacenar la foto en la memoria. De esta manera, podemos guardarlo más adelante en el Hub de fotos del usuario (específicamente, en el álbum Camera Roll).
Nota: Para interactuar con el Mediateca
clase que necesita para habilitar el ID_CAP_MEDIALIB_PHOTO
capacidad en el archivo manifiesto.
También puede personalizar muchos ajustes de la cámara llamando al SetProperty ()
método en el PhotoCaptureDevice
Objeto que requiere dos parámetros: la propiedad para establecer y el valor para asignar. Las propiedades disponibles están definidas por dos enumeradores: ConocidasCameraGeneralPropiedades
, que contiene las propiedades generales de la cámara, y ConocidasCameraFotoPropiedades
, que contiene las propiedades específicas de la foto.
Algunas propiedades son de solo lectura, por lo que la única operación que puede realizar es obtener sus valores mediante el uso de GetProperty ()
método.
En las siguientes muestras, utilizamos el SetProperty ()
método para establecer el modo de flash y GetProperty ()
para obtener la información si la región actual obliga a los teléfonos a reproducir un sonido cuando toman una foto.
Private Void OnSetPropertiesClicked (objeto remitente, RoutedEventArgs e) camera.SetProperty (KnownCameraPhotoProperties.FlashMode, FlashMode.RedEyeReduction); bool isShutterSoundRequired = (bool) camera.GetProperty (KnownCameraGeneralProperties.IsShutterSoundRequiredForRegion);
Tenga en cuenta que el GetProperty ()
El método siempre devuelve un objeto genérico, por lo que tendrá que convertirlo manualmente de acuerdo con las propiedades que está consultando..
Puede ver una lista de todas las propiedades disponibles en la documentación de MSDN.
Por lo general, los dispositivos de Windows Phone tienen un botón dedicado para la cámara, que se puede usar tanto para establecer el enfoque con solo presionarlo a la mitad, y para tomar la foto al presionarlo por completo. También puede usar este botón en sus aplicaciones suscribiéndose a tres eventos que están expuestos por el Botones de cámara
clase estática:
ShutterKeyPressed
se dispara cuando se presiona el botón.ShutterKeyReleased
se activa cuando se suelta el botón.ShutterKeyHalfPressed
se dispara cuando el botón se presiona a medias.En la siguiente muestra, nos suscribimos a la ShutterKeyReleased
evento para tomar una foto y la ShutterKeyHalfPressed
evento para utilizar la función de enfoque automático.
cámara pública () InitializeComponent (); CameraButtons.ShutterKeyReleased + = CameraButtons_ShutterKeyReleased; CameraButtons.ShutterKeyHalfPressed + = CameraButtons_ShutterKeyHalfPressed; async void CameraButtons_ShutterKeyHalfPressed (objeto remitente, EventArgs e) await camera.FocusAsync (); void CameraButtons_ShutterKeyReleased (objeto remitente, EventArgs e) // Tome la fotografía.
El proceso para grabar un video es similar al que usamos para tomar una foto. En este caso, vamos a utilizar el AudioVideoCaptureDevice
clase en lugar de la PhotoCaptureDevice
clase. Como puede ver en la siguiente muestra, el procedimiento de inicialización es el mismo: decidimos qué resolución y qué cámara queremos usar, y mostramos la transmisión en vivo devuelta usando una VideoBrush
.
Nota: para grabar videos, también deberá habilitar la ID_CAP_MICROPHONE
capacidad en el archivo manifiesto.
anulación protegida vacío asíncrono OnNavigatedTo (NavigationEventArgs e) Resolución de tamaño = AudioVideoCaptureDevice.GetAvailableCaptureResolutions (CameraSensorLocation.Back) .First (); videoDevice = aguarda AudioVideoCaptureDevice.OpenAsync (CameraSensorLocation.Back, resolución); video.SetSource (videoDevice); previewTransform.Rotation = videoDevice.SensorRotationInDegrees;
Grabar un video es aún más simple ya que la AudioVideoCaptureDevice
clase expone la IniciarRecordingToStreamAsync ()
Método, que simplemente requiere que especifique dónde guardar los datos grabados. Como es un video, también necesitarás una forma de detener la grabación; este es el propósito de la StopRecordingAsync ()
método.
En el siguiente ejemplo, la grabación se almacena en un archivo creado en el almacenamiento local:
AudioVideoCaptureDevice videoDevice privado; transmisión privada IRandomAccessStream; archivo privado StorageFile; grabación de video pública () InitializeComponent (); privado async void OnRecordVideoClicked (objeto emisor, RoutedEventArgs e) file = await ApplicationData.Current.LocalFolder.CreateFileAsync ("video.wmv", CreationCollisionOption.ReplaceExisting); stream = await file.OpenAsync (FileAccessMode.ReadWrite); videoDevice.StartRecordingToStreamAsync (secuencia); privado async void OnStopRecordingClicked (objeto remitente, RoutedEventArgs e) await videoDevice.StopRecordingAsync (); espera stream.FlushAsync ();
Puede probar fácilmente el resultado de la operación utilizando el MediaPlayerLauncher
Clase para reproducir la grabación:
Private Void OnPlayVideoClicked (objeto remitente, RoutedEventArgs e) MediaPlayerLauncher launcher = new MediaPlayerLauncher Media = new Uri (file.Path, UriKind.Relative); launcher.Show ();
El SDK ofrece una lista específica de configuraciones personalizables conectadas a la grabación de video. Están disponibles en el ConocidoCameraAudioVideoPropiedades
enumerador.
El marco ofrece una clase llamada Mediateca
, que se puede utilizar para interactuar con la biblioteca multimedia del usuario (fotos, música, etc.). Veamos cómo usarlo para gestionar los escenarios más comunes..
Nota: En la versión actual, no hay manera de interactuar con la biblioteca para guardar nuevos videos en el Camera Roll, ni para acceder a la corriente de videos existentes.
los Mediateca
Esta clase se puede utilizar para acceder a las imágenes almacenadas en Photos Hub, gracias a la Imágenes
colección. Es una coleccion de Imagen
objetos, donde cada uno representa una imagen almacenada en el hub de fotos.
Nota: deberás habilitar el ID_CAP_MEDIALIB_PHOTO
Capacidad en el archivo de manifiesto para acceder a las imágenes almacenadas en Photos Hub..
los Imágenes
La colección otorga acceso a los siguientes álbumes:
No se puede acceder a todos los demás álbumes que se muestran en el People Hub que provienen de servicios remotos como SkyDrive o Facebook usando el Mediateca
clase.
Consejo: El Mediateca
clase expone una colección llamada Imágenes Guardadas
, que contiene solo las imágenes que están almacenadas en el álbum Imágenes guardadas.
Cada Imagen
El objeto ofrece algunas propiedades para obtener acceso a la información básica, como Nombre
, Anchura
, y Altura
. Una propiedad muy importante es Álbum
, que contiene la referencia del álbum donde se almacena la imagen. Además, podrá acceder a diferentes transmisiones en caso de que quiera manipular la imagen o mostrarla en su aplicación:
GetPicture ()
Método devuelve el flujo de la imagen original..GetThumbnail ()
El método devuelve el flujo de la miniatura, que es una versión de baja resolución de la imagen original..Extensiones de teléfono
espacio de nombres a su clase, podrá utilizar el GetPreviewImage ()
Método, que devuelve una imagen de vista previa. Su resolución y tamaño se encuentran entre la imagen original y la miniatura..En el siguiente ejemplo, generamos la miniatura de la primera imagen disponible en el Rollo de la cámara y la mostramos usando un Imagen
controlar:
el vacío privado OnSetPhotoClicked (remitente del objeto, RoutedEventArgs e) MediaLibrary library = new MediaLibrary (); Picture picture = library.Pictures.FirstOrDefault (x => x.Album.Name == "Camera Roll"); if (picture! = null) BitmapImage image = new BitmapImage (); image.SetSource (picture.GetThumbnail ()); Thumbnail.Source = imagen;
Consejo: Para interactuar con el Mediateca
Si utiliza el emulador, deberá abrir Photos Hub al menos una vez; de lo contrario, obtendrá una colección vacía de imágenes cuando consulte el Imágenes
propiedad.
Con el Mediateca
En clase, también podrá hacer lo contrario: tome una foto en su aplicación y guárdela en el People Hub. Ya hemos visto una muestra cuando hablamos sobre la integración de la cámara en nuestra aplicación; Podemos guardar la imagen en el rollo de la cámara (usando el SavePictureToCameraRoll ()
método) o en el álbum Imágenes guardadas (utilizando el Guardar imagen()
método). En ambos casos, los parámetros requeridos son el nombre de la imagen y su flujo.
En el siguiente ejemplo, descargamos una imagen de Internet y la guardamos en el álbum Imágenes guardadas:
privado async void OnDownloadPhotoClicked (objeto remitente, RoutedEventArgs e) cliente HttpClient = nuevo HttpClient (); Stream stream = await client.GetStreamAsync ("http://www.syncfusion.com/Content/en-US/Home/Images/syncfusion-logo.png"); Biblioteca MediaLibrary = new MediaLibrary (); library.SavePicture ("logo.jpg", flujo);
los Mediateca
La clase ofrece muchas opciones para acceder a la música, pero hay algunas limitaciones que no están presentes cuando se trabaja con imágenes..
Nota: deberás habilitar el ID_CAP_MEDIALIB_AUDIO
Capacidad en el archivo de manifiesto para acceder a las imágenes almacenadas en Photos Hub..
Las siguientes colecciones están expuestas por la Mediateca
clase para acceder a la musica:
Los álbumes
para acceder a los álbumes de música.Canciones
para acceder a todas las canciones disponibles.Géneros
Acceder a las canciones agrupadas por género..Listas de reproducción
para acceder a las listas de reproducción.Cada canción es identificada por el Canción
clase, que contiene toda la información común sobre una pista de música tomada directamente de la etiqueta ID3: Álbum
, Artista
, Título
, Número de pista
, y así.
Desafortunadamente, no hay acceso a la transmisión de una canción, por lo que la única forma de reproducir pistas es usando el Reproductor multimedia
clase, que es parte de la Microsoft.XNA.Framework.Media
espacio de nombres. Esta clase expone muchos métodos para interactuar con las pistas. los Jugar()
método acepta como parámetro a Canción
objeto, recuperado de la Mediateca
.
En el siguiente ejemplo, reproducimos la primera canción disponible en la biblioteca:
OnPlaySong (identificador de objeto, RoutedEventArgs e) vacío privado MediaLibrary library = new MediaLibrary (); Canción de la canción = library.Songs.FirstOrDefault (); MediaPlayer.Jugar (canción);
Una de las nuevas características introducidas en Windows Phone 8 le permite guardar una canción almacenada en el almacenamiento local de la aplicación en la biblioteca de medios para que pueda ser reproducida por el Centro de Música + Videos nativo. Esto requiere la Microsoft.Xna.Framework.Media.PhoneExtensions
espacio de nombres para ser añadido a su clase.
onDownloadMusicClicked privado async void (objeto remitente, RoutedEventArgs e) MediaLibrary library = new MediaLibrary (); SongMetadata metadata = new SongMetadata AlbumName = "Un torrente de sangre en la cabeza", ArtistName = "Coldplay", Name = "Clocks"; library.SaveSong (new Uri ("song.mp3", UriKind.RelativeOrAbsolute), metadatos, SaveSongOperation.CopyToLibrary);
los SaveSong ()
El método requiere tres parámetros, como se muestra en la muestra anterior:
SongMetadata
clase. Es un parámetro opcional; si pasas nulo
, Windows Phone extraerá automáticamente la información ID3 del archivo.SaveSongOperation
objeto, que indica a la biblioteca de medios si el archivo debe copiarse (CopyToLibrary
) o movido (MoveToLibrary
) para que se borre del almacenamiento.Windows Phone 8 ha introducido nuevas características específicas para aplicaciones fotográficas. Algunos de los más interesantes se llaman aplicaciones de lentes, Que aplican diferentes filtros y efectos a las imágenes. Windows Phone ofrece una forma de cambiar fácilmente entre diferentes aplicaciones de cámara para aplicar filtros sobre la marcha.
Las aplicaciones de lentes son aplicaciones normales de Windows Phone que interactúan con las API de la cámara que usamos al principio de este artículo. La diferencia es que una aplicación de lente se muestra en el lentes sección de la aplicación nativa de la cámara; cuando los usuarios presionan el botón de la cámara, se muestra una vista especial con todas las aplicaciones de lentes disponibles. De esta manera, pueden cambiar fácilmente a otra aplicación para tomar la foto..
La integración con la vista de lentes comienza desde el archivo de manifiesto, que debe editarse manualmente seleccionando Ver codigo Opción en el menú contextual. El siguiente código debe ser agregado en el Extensión sección:
Cada aplicación de lente necesita un icono específico que se muestra en la vista de lentes. Los iconos se recuperan automáticamente de la Bienes carpeta basada en una convención de nomenclatura. Se debe agregar un ícono para cada resolución compatible usando las convenciones en la siguiente tabla:
Resolución | Tamaño de ícono | Nombre del archivo |
480 × 800 | 173 × 173 | Lens.Screen-WVGA.png |
768 × 1280 | 277 × 277 | Lens.Screen-WXGA.png |
720 × 1280 | 259 × 259 | Lens.Screen-720p.png |
los UriMapper
Se requiere clase para trabajar con aplicaciones de lentes. De hecho, las aplicaciones de lentes se abren utilizando un URI especial que debe ser interceptado y administrado. El siguiente código es una muestra Uri
:
/MainPage.xaml?Action=ViewfinderLaunch
Cuando esto Uri
Se intercepta, los usuarios deben ser redirigidos a la página de la aplicación que toma la foto. En la siguiente muestra, puede ver un UriMapper
Implementación que redirige a los usuarios a una página llamada. Camara.xaml
cuando la aplicación se abre desde la vista de la lente.
clase pública MyUriMapper: UriMapperBase override pública Uri MapUri (Uri uri) string tempUri = uri.ToString (); if (tempUri.Contains ("ViewfinderLaunch")) return new Uri ("/ Camera.xaml", UriKind.Relative); else return uri;
Si ha desarrollado una aplicación que admite el uso compartido de fotos, como un cliente de red social, puede integrarla en el Compartir Menú del Hub de Fotos. Los usuarios pueden encontrar esta opción en la barra de aplicaciones en la página de detalles de la foto.
Cuando los usuarios eligen esta opción, Windows Phone muestra una lista de aplicaciones que están registradas para admitir el uso compartido. Podemos agregar nuestra aplicación a la lista simplemente agregando una nueva extensión en el archivo de manifiesto, como hicimos para agregar soporte para lentes.
Tenemos que añadir manualmente la siguiente declaración en el Extensiones sección:
Cuando los usuarios eligen nuestra aplicación de la lista, se abre con el siguiente URI:
/MainPage.xaml?Action=ShareContent&FileId=%7B1ECC86A2-CDD7-494B-A9A8-DBD9B4C4AAC7%7D
De nuevo, podemos usar un UriMapper
Implementación para redirigir a los usuarios a la página de nuestra aplicación que ofrece la función de compartir. También es importante llevar el FiledId
parámetro en esta página; Lo necesitaremos para saber qué foto ha sido seleccionada por el usuario..
La siguiente muestra muestra una UriMapper
implementación que simplemente reemplaza el nombre de la página original (MainPage.xaml
) con el nombre de la página de destino (SharePage.xaml
):
clase pública MyUriMapper: UriMapperBase override pública Uri MapUri (Uri uri) string tempUri = uri.ToString (); cadena mappedUri; if ((tempUri.Contains ("SharePhotoContent")) && (tempUri.Contains ("FileId"))) // Redirect to PhotoShare.xaml. mappedUri = tempUri.Replace ("MainPage", "SharePage"); volver nuevo Uri (mappedUri, UriKind.Relative); devolver uri;
Después de redirigir al usuario a la página para compartir, podemos usar un método llamado GetPictureFromToken ()
expuesto por el Mediateca
clase. Acepta la ID de imagen única como un parámetro y devuelve una referencia a la Imagen
Objeto que representa la imagen seleccionada por el usuario..
La ID de la imagen es el parámetro llamado FileId
que recibimos en el URI cuando se abrió la aplicación. En el siguiente ejemplo, puede ver cómo recuperamos el parámetro utilizando el OnNavigatedTo
evento que se activa cuando el usuario se redirige a la página para compartir, y lo utiliza para mostrar la imagen seleccionada con un Imagen
controlar.
anulación protegida nula OnNavigatedTo (NavigationEventArgs e) if (NavigationContext.QueryString.ContainsKey ("FileId")) string fileId = NavigationContext.QueryString ["FileId"]; Biblioteca MediaLibrary = new MediaLibrary (); Picture picture = library.GetPictureFromToken (fileId); BitmapImage image = new BitmapImage (); image.SetSource (picture.GetImage ()); ShareImage.Source = image;
Hay otras formas de integrar nuestra aplicación con Photos Hub. Todos funcionan de la misma manera:
Uri
que necesitas interceptar con un UriMapper
clase.FileId
parámetro.Esta es la integración más simple, ya que solo muestra la aplicación en la sección de Aplicaciones de Photos Hub. Para admitirlo, simplemente debe agregar la siguiente declaración en el archivo de manifiesto:
No se requiere nada más ya que este tipo de integración simplemente incluirá un enlace rápido en el Hub de Fotos. La aplicación se abrirá normalmente, como si se abriera con el icono principal de la aplicación.
Otra opción disponible en la Barra de aplicaciones de la página de detalles de la foto se llama editar. Cuando el usuario lo toca, Windows Phone muestra una lista de aplicaciones que admiten la edición de fotos. Después de elegir uno, el usuario espera ser redirigido a una página de aplicación donde se puede editar la imagen seleccionada.
La siguiente declaración debe agregarse en el archivo de manifiesto:
Cuando esta función esté habilitada, su aplicación se abrirá con el siguiente URI:
/MainPage.xaml?Action=EditPhotoContent&FileId=%7B1ECC86A2-CDD7-494B-A9A8-DBD9B4C4AAC7%7D
Este es el Uri
para interceptar y redirigir a los usuarios a la página adecuada donde podrá recuperar la imagen seleccionada utilizando el FileId
parámetro, como hicimos para la función de compartir fotos.
Las aplicaciones de medios enriquecidos son aplicaciones que pueden tomar fotos y guardarlas en la biblioteca del usuario. Cuando los usuarios abran una de estas fotos, verán:
Este enfoque es similar a las funciones de compartir y editar. La diferencia es que la integración de aplicaciones de Rich Media solo está disponible para las imágenes tomadas dentro de la aplicación, mientras que las funciones de edición y uso compartido están disponibles para cada foto, independientemente de cómo se tomaron.
La siguiente declaración debe agregarse en el manifiesto para habilitar la integración de aplicaciones de Rich Media:
En este escenario, la aplicación se abre con el siguiente URI:
/MainPage.xaml?Action=RichMediaEdit&FileId=%7B1ECC86A2-CDD7-494B-A9A8-DBD9B4C4AAC7%7D
Como puedes ver, el URI es siempre el mismo; lo que cambia es el valor de la Acción
parámetro-en este caso, RichMediaEdit
.
Este es el URI que necesitas para interceptar con tu UriMapper
implementación. Deberá redirigir a los usuarios a una página de su aplicación que pueda administrar la imagen seleccionada.
En este tutorial, hemos aprendido muchas formas de crear una excelente aplicación multimedia para Windows Phone:
Este tutorial representa un capítulo de Windows Phone 8 sucintamente, un libro electrónico gratuito del equipo de Syncfusion.