Hace algún tiempo tuve una conversación por Twitter con los grandes Lluis Franco y Josue Yeray en la que discutíamos sobre cómo almacenar datos en aplicaciones móviles y, después de un par de mensajes sobre SQLite, apareció el nombre de Akavache, basada en la anterior pero que aporta algunas ventajas importantes.
¿Qué es Akavache?
Tal y como lo describen sus creadores, Akavache es un “motor” de almacenamiento “clave-valor”, asíncrono y persistente, basado en SQLite3 creado para aplicaciones tanto de escritorio como móviles que nos permite almacenar tanto datos importantes persistentes como datos locales cacheados que deben expirar.
A simple vista, la primera ventaja que tiene Akavache es la posibilidad de escribir datos de manera asíncrona, cosa que en aplicaciones móviles es muy importante y necesario además de que dispone de Akavache Explorer, que nos permite ver los datos almacenados de una forma muy simple.
Además, Akavache es multiplataforma con lo que podremos usarlo en nuestras aplicaciones con Xamarin para iOS y Android.
Instalando Akavache en Apps UWP
Podemos instalarlo desde Nuget de una forma muy sencilla. Buscamos Akavache y seleccionamos la primera opción
Stores disponibles con Akavache
Para usar Akavache, debemos usar el objeto BlobCache que nos permitirá realizar todas las operaciones necesarias. Este objeto expone de varias formas de interactuar con los datos. Por defecto, al establecer el nombre de la aplicación BlobCache.ApplicationName la información se alojará en %LocalAppData%\[ApplicationName] y, en el caso de una aplicación de escritorio, en %AppData%\[ApplicationName]
Por usuario: BlobCache.UserAccount con SQLite. Es usada para compartir entre diferentes máquinas.
Por máquina: BlobCache.LocalMachine con SQL en la máquina local
Securizada: BlobCache.Secure encriptada con SQLite
En memoria: BlobCache.InMemory es volátil y se libera en cada reinicio
Obtener datos
Con unas pocas líneas de código obtendremos nuestros objetos serializados de forma sencilla
public async Task<IEnumerable<T>> Get()
{
var items = await BlobCache.UserAccount.GetAllObjects<T>();
return items;
}
Ejemplo guardando datos
Guardar datos se hace de una forma muy simple gracias a los métodos expuestos por Akavache. En este ejemplo usaré UserAccount como destino del guardado de los datos.
Durante el desarrollo de nuestros proyecto de SharePoint, es habitual que entre despliegue y despliegue podamos dejar características huérfanas, esto es, características cuya referencia a la solución se haya perdido por haber cambiado su configuración, porque se nos haya quedado un despliegue a medias, … y que eso nos impida volver a desplegar la solución con éxito debido a que el GUID que tiene asignado ya esté en uso y nos sea imposible hacer incluso un “Retract” desde Visual Studio.
¿Cómo lo solucionamos?
La solución, cómo no, pasa por usar PowerShell y con unas cuántas instrucciones podremos eliminar esa característica y poder realizar nuestro despliegue de forma limpia.
Obtener características huérfanas
El primer paso es obtener el listado de características y filtrarlas por las que no tienen un “scope” asignado. El resultado nos debería devolver las características huérfanas, si las hubiera. Para ello usaremos el comando Get-SPFeatures y filtraremos por “scope” igual a nulo
Get-SPFeature | ? { $_.Scope -eq $null }
En este caso, podemos observar que me aparecen dos características huérfanas (sin scope). Es importante quedarse con el nombre de las mismas para eliminarlas posteriormente.
Eliminar características huérfanas
Una vez obtenidas las características huérfanas, tan solo tendremos que ejecutar los siguientes comandos de PowerShell, sustituyendo “MyFeatureName” por el nombre de la característica a eliminar que habremos obtenido anteriormente.
Si intentamos obtener ahora las características huérfanas, yo lo he ejecutado para las dos que me aparecían, no debería mostrarnos nada.
Hecho! Ahora podremos seguir trabajando sin problemas y volver a desplegar nuestras soluciones de forma limpia.
Consejo
Si estamos en pleno proceso de desarrollo de soluciones para SharePoint y vamos a estar constantemente realizando despliegues para probar, sería bueno que tuviéramos estos comandos en un script de PowerShell para realizar esta tarea de forma automatizada .
Algo habitual cuando diseñamos una página es tener que dibujar triángulos y siempre andamos rebanándonos los sesos hasta que, cansados, creamos una imagen con el triángulo deseado y así nos evitamos más quebraderos de cabeza, pero, ¿es óptimo?
Problema
Cuando usamos imágenes tenemos los siguientes problemas
Mayor peso en KB de la página, con lo que, aunque el navegador las pueda cachear o usemos técnicas de precarga de las mismas, en definitiva se produce una mayor transferencia de datos en cada petición.
No son adaptables en tamaño por mucho que podamos cambiar sus dimensiones y pensemos que no pierden calidad porque, aunque en algunos casos sea así, generalmente obtendremos visualizaciones difusas o pixeladas.
No permiten el cambio de color a priori. Se podrían usar filtros mediante CSS pero no obtendremos un resultado óptimo, la implementación es compleja y finalmente, obligaremos al navegador a realizar mayor procesado de la interfaz, ralentizando y haciendo más pesada la página.
Solución
Con CSS podemos jugar con los bordes o con el efecto :after de los elementos para dibujar triángulos a nuestro antojo, reduciendo el procesado del navegador y permitiéndonos cambiarle la forma, el tamaño y el color según nos convenga con lo que se convierte en la solución más óptima.
Para ello, he encontrado y recopilado diversos enlaces (por orden de gusto) que nos pueden ayudar a generar nuestros triángulos según las necesidades que tengamos:
En este artículo escribiré un truco sencillo para leer la extensión de un fichero mediante JavaScript pensando, no sólo en conocer dicha extensión, sino además en guardarla en una variable para poder realizar acciones como agregar un icono, emparejarlo con una clase de CSS, etc.
Cuando desarrollamos Display Templates para dar un look&feel diferente a los elementos resultantes de las búsquedas en SharePoint, muchas veces es necesario ejecutar código JavasScript tras la carga de los mismos ya sea para añadir funcionalidad extra o simplemente para establecer algunas propiedades visuales que requieran de conocer los resultados para mostrar una mejor distribución. Continue reading…
Continuando el artículo anterior en el que explicaba cómo cambiar el usuario que está autenticado en Visual Studio, seguramente necesitaremos cambiar el usuario de algún servidor de TFS al que estuviera conectado para usar el nuevo usuario que está conectado a Visual Studio o simplemente para usar un usuario diferente.
Una vez hecho el reseteo de la configuración del usuario conectado a Visual Studio, cuando intentemos conectarnos a un servidor de TFS, si el anterior usuario ya lo tenía mapeado, Visual Studio cogerá esas credenciales para conectarse y no nos permitirá cambiarlo. ¿Qué hacemos? Para eliminar ese «cacheo» de las credenciales, debemos seguir los siguientes pasos:
Abrir el Panel de control
Buscar «Manage Network Passwords» (o su traducción si lo tenéis en español)
Seleccionar la/s que corresponda/n con nuestro servidor de TFS y eliminarla/s
¡Listo! Hecho esto, al abrir Visual Studio, si aún continuara teniendo la referencia al servidor de TFS nos pedirá las credenciales y, en caso de que el reseteo de la configuración hubiera dejado vacía la lista de servidores, nos permitirá agregarlo estableciendo el nuevo usuario y contraseña.
Cuando disponemos de un Visual Studio que ha estado usando otra persona y queremos cambiar las credenciales para usar nuestro usuario, es posible que Visual Studio nos dé problemas a la hora de insertar las nuevas credenciales porque mantiene una referencia al anterior usuario.
La solución es muy sencilla:
Cerrar Visual Studio
Abrir la consola de comandos «cmd.exe» en modo administrador
Situarnos en la carpeta de Visual Studio «C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE» donde se encuentra el ejecutable «devenv.exe»
Ejecutar el comando «devenv /resetuserdata»
Abrir nuevamente Visual Studio y listo
NOTA: Este reseteo borrará también la configuración que hayamos realizado en Visual Studio con lo que si queremos mantenerla debemos exportarla antes de ejecutar los pasos anteriores
Espero que este pequeño «tip» os libre de más de un dolor de cabeza
Es bastante común en las aplicaciones que muestran listados de registros, disponer de un menú contextual que extienda las opciones disponibles a realizar sobre cada registro, o simplemente que contenga algunas de ellas que podamos considerar como más usadas para que el usuario no tenga que desplazarse por la pantalla en busca de la opción necesaria.
Para esto, prácticamente todos los controles de WPF disponen de menú contextual, fácilmente gestionable pero que presenta algunos retos cuando estamos trabajando con el patrón MVVM ya que, el Binding se tiene que resolver con el ViewModel que contiene las acciones para que los comandos asociados a las opciones funcionen correctamente, así que vamos a ver paso a paso cómo podemos solventarlo.
Montando el Menú Contextual en nuestro DataGrid
Montar el menú contextual es una labor muy sencilla ya que, con sólo acceder a la propiedad ContextMenu del DataGrid podríamos tenerlo pero, como lo queremos a nivel de registros, debemos asociárselo a cada una de las filas del DataGrid y no al DataGrid en sí mismo tal y como se muestra en el siguiente snippet de código.
<Window x_Class="WPFSamples.DataGridContextMenu.MainWindow"
><img style="padding-top: 0px;padding-left: 0px;padding-right: 0px;border-width: 0px" title="quote-marks1" src="http://geeks.ms/santypr/wp-content/uploads/sites/242/2016/08/quote-marks1_thumb.png" alt="quote-marks1" width="80" height="70" border="0" /></a><strong>NOTA</strong>: <em><span style="color: #666666">Como buena práctica, es importante asignar un valor a la propiedad x:Name de aquellos elementos más relevantes para poder acceder a ellos en Bindings y Storyboards que establezcamos en nuestro código XAML</span></em>.<a href="http://geeks.ms/santypr/wp-content/uploads/sites/242/2016/08/quote-marks1-1.png"><img style="padding-top: 0px;padding-left: 0px;padding-right: 0px;border-width: 0px" title="quote-marks1" src="http://geeks.ms/santypr/wp-content/uploads/sites/242/2016/08/quote-marks1_thumb-1.png" alt="quote-marks1" width="80" height="70" border="0" /></a>
De esta forma, al ejecutar la aplicación, ya tendremos disponible un menú contextual en cada uno de los registros del DataGrid tal y como se puede ver en la siguiente imagen aunque de momento no ejecutará accíón alguna.
<a href="http://geeks.ms/santypr/wp-content/uploads/sites/242/2016/08/image.png"><img style="float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px" title="image" src="http://geeks.ms/santypr/wp-content/uploads/sites/242/2016/08/image_thumb.png" alt="image" width="454" height="304" border="0" /></a>
<h2>Añadiendo acciones a las opciones del menú contextual</h2>
En este punto es donde comienza a complicarse la situación porque <strong>debemos establecer un contexto para poder hacer un Binding con nuestro ViewModel y así poder ejecutar los comandos que contiene</strong>. Buscando información al respecto, encontré este artículo sobre ContextMenu a nivel de filas <a title="https://blog.gisspan.com/2012/11/contextmenu-for-wpf-datagrid-on-row.html" href="https://blog.gisspan.com/2012/11/contextmenu-for-wpf-datagrid-on-row.html">https://blog.gisspan.com/2012/11/contextmenu-for-wpf-datagrid-on-row.html</a> que ofrece una buena explicación pero da por hecho que cada uno de los registros está asociado a un ViewModel en sí y que, por lo tanto, cada uno de ellos contiene las acciones a ejecutar. Pero en realidad, el caso es más complejo si estamos siguiendo el patrón MVVM de una forma más estricta y que, por lo tanto, disponemos de un <strong>ViewModel global para la vista que contiene toda la información que queremos mostrar en pantalla</strong> en forma de propiedades <strong>además de los comandos que se pueden ejecuta</strong>r. Es decir, este ViewModel contiene una colección que no necesariamente tiene que ser también de ViewModels y que, de hecho, por rendimiento no debería serlo, además de los comandos que se pueden ejecutar tal y como muestro en el código a continuación.
<div id="scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:d8fbf2fa-bebf-4c50-bbf3-c98f5484c2d5" class="wlWriterEditableSmartContent" style="float: none;margin: 0px;padding: 0px">
public class FakeViewModel : INotifyPropertyChanged
{
public List<MyGridModel> MyGridModelCollection
{
get; set;
}
private DelegateCommand<object> myCommand;
public ICommand MyCommand => myCommand;
public FakeViewModel()
{
MyGridModelCollection = InitializeCollection();
this.myCommand = new DelegateCommand<object> (MyCommandMethod);
}
private void MyCommandMethod(object parameter)
{
var myModel = parameter as MyGridModel;
MessageBox.Show("You selected a command");
}
private List<MyGridModel> InitializeCollection()
{
var collection = new List<MyGridModel>
{
new MyGridModel { Title = "Item #1", Desc="Desc for item #1 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #2", Desc="Desc for item #2 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #3", Desc="Desc for item #3 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #4", Desc="Desc for item #4 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #5", Desc="Desc for item #5 ", Created=System.DateTime.Now },
};
return collection;
}
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
var Handler = PropertyChanged;
Handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Es importante tener en cuenta que los ViewModels deberían implementar la interfaz INotifyPropertyChanged para poder comunicar a la vista cualquier cambio que se produzca en las propiedades que están enlazadas con Binding.
Una vez que tenemos nuestro ViewModel, vamos a enlazar nuestros comandos a través de la propiedad Commandque tienen los elementos del ContextMenu de la siguiente forma.
Como se puede ver, estamos haciendo un Binding al comando definido en el ViewModel. ¿Funcionará? La respuesta es que No porque el contexto de cada una de las filas es el modelo que tiene que representar, es decir, una instancia de tipo MyGridModel que no contiene el comando, con lo que no encontrará lo que debe ejecutar.
Estableciendo el contexto adecuado
Para que sea posible encontrar el comando a ejecutar en cada una de las opciones del menú contextual, es necesario establecer su contexto real, es decir, el ViewModel global. Hay varios workarounds un poco complejos o mal pensados desde mi punto de vista, así que voy a dar mi versión óptima o la que creo que es más óptima. En primer lugar, le indicaremos al menú contextual que su contexto debe ser el ViewModel de la vista y esto lo haremos estableciendo el mismo que el del propio DataGrid. Esto implica que, al ser el contexto de cada fila el ViewModel completo, debemos pasar como parámetro del comando el elemento actual para que la acción se pueda ejecutar sobre ese elemento en cuestión. Estos cambios de contexto son los que cuesta algo más entender y establecer, pero se ven de forma clara en el código.
Como podéis ver, el parámetro del comando será el elemento seleccionado del DataGrid ya que, al hacer click con el botón derecho sobre el elemento, éste quedará seleccionado.
Con esta solución, ya podremos ejecutar los comandos necesarios dentro de nuestro ContextMenuy seguir trabajando con nuestro ViewModel respetando las características de MVVM.
Descarga
Si queréis ver el código completo, podéis acceder a mi repositorio de GitHub y descargároslo libremente
Es bastante común en las aplicaciones que muestran listados de registros, disponer de un menú contextual que extienda las opciones disponibles a realizar sobre cada registro, o simplemente que contenga algunas de ellas que podamos considerar como más usadas para que el usuario no tenga que desplazarse por la pantalla en busca de la opción necesaria.
Para esto, prácticamente todos los controles de WPF disponen de menú contextual, fácilmente gestionable pero que presenta algunos retos cuando estamos trabajando con el patrón MVVM ya que, el Binding se tiene que resolver con el ViewModel que contiene las acciones para que los comandos asociados a las opciones funcionen correctamente, así que vamos a ver paso a paso cómo podemos solventarlo.
Montando el Menú Contextual en nuestro DataGrid
Montar el menú contextual es una labor muy sencilla ya que, con sólo acceder a la propiedad ContextMenu del DataGrid podríamos tenerlo pero, como lo queremos a nivel de registros, debemos asociárselo a cada una de las filas del DataGrid y no al DataGrid en sí mismo tal y como se muestra en el siguiente snippet de código.
Como se puede observar en el código XAML, tendremos una colección asignada al DataGrid mediante un Binding a una colección de objetos de nuestro ViewModel. En el próximo artículo os hablaré sobre cómo establecer MVVM en WPF, así que de momento estableceremos el DataContext de nuestra vista de forma directa añadiendo el ViewModel tal y como se puede apreciar antes de comenzar el Grid con la etiqueta “<Window.DataContext>…”. Posteriormente definimos el ContextMenu como un recurso del DataGrid y le asignaremos un valor a la propiedad x:Name que debe ser único en el contexto del DataGrid y que en mi ejempo es “MyContextMenu” para después crear un estilo para las filas del DataGrid mediante la etiqueta “<DataGrid.RowStyle>…” donde establecemos mediante un “<Setter>” la propiedad ContextMenu de las filas y le asignamos el ContextMenu que definimos en los recursos del DataGrid haciendo uso de su Name.
NOTA: Como buena práctica, es importante asignar un valor a la propiedad x:Name de aquellos elementos más relevantes para poder acceder a ellos en Bindings y Storyboards que establezcamos en nuestro código XAML.
De esta forma, al ejecutar la aplicación, ya tendremos disponible un menú contextual en cada uno de los registros del DataGrid tal y como se puede ver en la siguiente imagen aunque de momento no ejecutará accíón alguna.
Añadiendo acciones a las opciones del menú contextual
En este punto es donde comienza a complicarse la situación porque debemos establecer un contexto para poder hacer un Binding con nuestro ViewModel y así poder ejecutar los comandos que contiene. Buscando información al respecto, encontré este artículo sobre ContextMenu a nivel de filas https://blog.gisspan.com/2012/11/contextmenu-for-wpf-datagrid-on-row.html que ofrece una buena explicación pero da por hecho que cada uno de los registros está asociado a un ViewModel en sí y que, por lo tanto, cada uno de ellos contiene las acciones a ejecutar. Pero en realidad, el caso es más complejo si estamos siguiendo el patrón MVVM de una forma más estricta y que, por lo tanto, disponemos de un ViewModel global para la vista que contiene toda la información que queremos mostrar en pantalla en forma de propiedades además de los comandos que se pueden ejecutar. Es decir, este ViewModel contiene una colección que no necesariamente tiene que ser también de ViewModels y que, de hecho, por rendimiento no debería serlo, además de los comandos que se pueden ejecutar tal y como muestro en el código a continuación.
public class FakeViewModel : INotifyPropertyChanged
{
public List<MyGridModel> MyGridModelCollection
{
get; set;
}
private DelegateCommand<object> myCommand;
public ICommand MyCommand => myCommand;
public FakeViewModel()
{
MyGridModelCollection = InitializeCollection();
this.myCommand = new DelegateCommand<object> (MyCommandMethod);
}
private void MyCommandMethod(object parameter)
{
var myModel = parameter as MyGridModel;
MessageBox.Show("You selected a command");
}
private List<MyGridModel> InitializeCollection()
{
var collection = new List<MyGridModel>
{
new MyGridModel { Title = "Item #1", Desc="Desc for item #1 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #2", Desc="Desc for item #2 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #3", Desc="Desc for item #3 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #4", Desc="Desc for item #4 ", Created=System.DateTime.Now },
new MyGridModel { Title = "Item #5", Desc="Desc for item #5 ", Created=System.DateTime.Now },
};
return collection;
}
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
var Handler = PropertyChanged;
Handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Es importante tener en cuenta que los ViewModels deberían implementar la interfaz INotifyPropertyChanged para poder comunicar a la vista cualquier cambio que se produzca en las propiedades que están enlazadas con Binding.
Una vez que tenemos nuestro ViewModel, vamos a enlazar nuestros comandos a través de la propiedad Commandque tienen los elementos del ContextMenu de la siguiente forma.
Como se puede ver, estamos haciendo un Binding al comando definido en el ViewModel. ¿Funcionará? La respuesta es que No porque el contexto de cada una de las filas es el modelo que tiene que representar, es decir, una instancia de tipo MyGridModel que no contiene el comando, con lo que no encontrará lo que debe ejecutar.
Estableciendo el contexto adecuado
Para que sea posible encontrar el comando a ejecutar en cada una de las opciones del menú contextual, es necesario establecer su contexto real, es decir, el ViewModel global. Hay varios workarounds un poco complejos o mal pensados desde mi punto de vista, así que voy a dar mi versión óptima o la que creo que es más óptima. En primer lugar, le indicaremos al menú contextual que su contexto debe ser el ViewModel de la vista y esto lo haremos estableciendo el mismo que el del propio DataGrid. Esto implica que, al ser el contexto de cada fila el ViewModel completo, debemos pasar como parámetro del comando el elemento actual para que la acción se pueda ejecutar sobre ese elemento en cuestión. Estos cambios de contexto son los que cuesta algo más entender y establecer, pero se ven de forma clara en el código.
Como podéis ver, el parámetro del comando será el elemento seleccionado del DataGrid ya que, al hacer click con el botón derecho sobre el elemento, éste quedará seleccionado.
Con esta solución, ya podremos ejecutar los comandos necesarios dentro de nuestro ContextMenuy seguir trabajando con nuestro ViewModel respetando las características de MVVM.
Descarga
Si queréis ver el código completo, podéis acceder a mi repositorio de GitHub y descargároslo libremente
Cuando estamos realizando la navegación dentro de un Frame en una aplicación WPF, de forma automática nos aparecerá la barra de navegación que ocasiones puede que no deseemos mantenerla pese a las ventajas que nos podría aportar, ya sea porque el diseño que debemos implementar no contemple esa barra de navegación o simplemente porque no queremos que el usuario la use y proveemos nosotros mismos la navegación funcional requerida.