Wednesday, December 9, 2009

Usando un Viewbox para hacer un efecto “Fisheye” en TabControl/WPF

He creado un estilo para mis TabControl de manera que el icono de cada uno de sus TabItem aumente con el mouseover al estilo fisheye, esto es sencillo lograrlo combinando un ViewBox, el ContentPresenter del TabItem y un par de Storyboards. También uso DropShadowEffect para hacer el efecto de iluminación alrededor del iconos seleccionado.

Screenshots:

 imageimage

Nota:

  • No funciona en Silverlight.
  • La version minima de WPF es .NET 3.5 SP1 y la version usada para crear el ejemplo es Visual Studio 2008 y Blend3.
  • Esta optimizado para TabStripPlacement: Left
  • Libre de uso Personal y Comercial.

Descarga: FishEyeTabControl.zip 

Disfruten :)

Wednesday, November 18, 2009

Reemplazar Cursor con UIElement

He creado un pequeño metodo para reemplazar el cursor de una ventana con un UIElement de WPF:

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
 
namespace SDKSamples
{
    public static class WPFCursor
    {
        public static void ReplaceCursor(this Window wnd, UIElement cursor)
        {
            wnd.Cursor = Cursors.None;
            Grid cursorHostContainer = wnd.Content as Grid;
            if (cursorHostContainer == null) return;
 
            Canvas host = new Canvas();
            host.IsHitTestVisible = false;
            Canvas.SetZIndex(cursor, int.MaxValue);
            cursorHostContainer.Children.Add(host);
            host.Children.Add(cursor);
 
            wnd.MouseMove += (s, e) =>
                {
                    var absoluteWindowMousePosition = e.GetPosition(wnd);
                    Canvas.SetTop(cursor, absoluteWindowMousePosition.Y);
                    Canvas.SetLeft(cursor, absoluteWindowMousePosition.X);
                };
            wnd.MouseEnter += (s, e) =>
                {
                    cursor.Visibility = Visibility.Visible;
                };
            wnd.MouseLeave += (s, e) =>
                {
                    cursor.Visibility = Visibility.Collapsed;
                };
        }
    }
}

Espero les sirva, se usa de la siguiente forma en el constructor de la ventana a la cual le necesitamos reemplazar el cursor, en este ejemplo mi nuevo cursor sera una instancia de un UserControl llamado MySuperCursor.

   1: public MainWindow()
   2: {
   3:     InitializeComponent();
   4:     this.ReplaceCursor(new MySuperCursor());
   5: }

Por ultimo, el unico requerimiento que tiene es que la ventana tenga un Grid como elemento contenido principal:

   1: <Window x:Class="SDKSamples.Window2"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="Window2" Height="300" Width="300">
   5:     <Grid>
   6:     </Grid>
   7: </Window>

he aqui un screenshot:

image

Tuesday, November 10, 2009

Controles WPF Multi-Contenido con ContentPresenter

He estado usando ContentPresenter en muchos escenarios y quiero compartir algunos tips acerca de como usarlo y de la flexibilidad que este ofrece para crear controles multi-contenido. Para demostrarlo he creado un control que soporta una contenido ‘Lista’ y otro ‘Detalle o Editor’ muy común en aplicaciones de edición de datos. Al final del articulo se encuentra el vinculo de descarga.

Este es un screenshot de una aplicación de ejemplo que he creado para demostrar el control multi-contenido:

image

Soluciones con ContentPresenter

Al igual que un control HeaderedItemsControl el cual tiene propiedades Content y Header, nosotros estaremos usando el mismo ContentPresenter pero para mostrar distinta información del control establecidas en las siguientes propiedades:

  • List(object): En esta propiedad se coloca el IEnumerable(Array,List<>,etc) a mostrar como lista navegable.
  • CurrentItem(object): Esta propiedad se enlaza para mostrar el elemento seleccionado actualmente en la lista.

    Como el control debe ser full personalizable usamos las siguientes propiedades para mejorar la presentación:

  • ListTemplate(DataTemplate)
  • CurrentItemTemplate(DataTemplate)

    Podemos imaginarnos dos partes visuales en el control de la siguiente forma:

    image 

    Para usarlo aplicamos un ControlTemplate,ListTemplate y CurrentItemTemplate de la siguiente forma:

  •    1: <listeditors:ListEditor List="{Binding OrderDetails}">
       2:             <listeditors:ListEditor.ListTemplate>
       3:                 <DataTemplate>
       4:                     <ListView Width="220" ItemsSource="{Binding}"
       5:                               SelectedItem="{Binding CurrentItem,Mode=TwoWay,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type listeditors:ListEditor}}}">
       6:                         <ListView.View>
       7:                             <GridView>
       8:                                 <GridView.Columns>
       9:                                     <GridViewColumn Width="100" Header="Product" DisplayMemberBinding="{Binding ProductCode}"/>
      10:                                     <GridViewColumn Width="100" Header="Quantity" DisplayMemberBinding="{Binding Quantity}"/>
      11:                                 </GridView.Columns>
      12:                             </GridView>
      13:                         </ListView.View>
      14:                     </ListView>
      15:                 </DataTemplate>
      16:             </listeditors:ListEditor.ListTemplate>
      17:             <listeditors:ListEditor.CurrentItemTemplate>
      18:                 <DataTemplate>
      19:                     <StackPanel>
      20:                         <DockPanel>
      21:                             <TextBlock Text="Product Code" Width="100"/>
      22:                             <TextBox Text="{Binding ProductCode}"></TextBox>
      23:                         </DockPanel>
      24:                         <DockPanel>
      25:                             <TextBlock Text="Quantity" Width="100"/>
      26:                             <TextBox Text="{Binding Quantity}"></TextBox>
      27:                         </DockPanel>
      28:                         <DockPanel>
      29:                             <TextBlock Text="Comments" Width="100"/>
      30:                             <TextBox Height="30" Text="{Binding Comment}"></TextBox>
      31:                         </DockPanel>
      32:                     </StackPanel>
      33:                 </DataTemplate>
      34:             </listeditors:ListEditor.CurrentItemTemplate>
      35:             <listeditors:ListEditor.Template>
      36:                 <ControlTemplate TargetType="listeditors:ListEditor">
      37:                     <DockPanel>
      38:                         <ContentPresenter Margin="4" DockPanel.Dock="Left" ContentSource="List" />
      39:                         <ContentPresenter Margin="4" ContentSource="CurrentItem" />
      40:                     </DockPanel>
      41:                 </ControlTemplate>
      42:             </listeditors:ListEditor.Template>
      43:         </listeditors:ListEditor>

    Uso de ContentPresenter

    Preste atencion a los ContentPresenter que estan en las lineas 38 y 39. El primero mostrara los datos de la propiedad ‘List’ usando el DataTemplate configurado entre las lineas 2-16 y el Segundo ContentPresenter mostrara los datos de la propiedad ‘CurrentItem’ usando el DataTemplate configurado entre las lineas 17-34.

    Realmente quien hace todo el trabajo es el ContentPresenter con sus propiedades Content,ContentTemplate y ContentTemplateSelector que se asignan automáticamente con las propiedades del control donde pertenece el ControlTemplate basándose en el nombre establecido en la propiedad ContentSource.

    Ejemplo. Esta es una configuración correcta de ContentPresenter en un ControlTemplate:

    <ContentPresenter ContentSource="CurrentItem" ContentTemplate="{TemplateBinding CurrentItemTemplate}" ContentTemplateSelector="{TemplateBinding CurrentItemTemplateSelector}" />

    Sin embargo se puede resumir con ContentSource:

    <ContentPresenter Margin="4" ContentSource="CurrentItem" />

    Desestimare de algunos ‘Mitos’ sobre ContentPresenter:

    • ContentPresenter PUEDE usarse en cualquier ControlTemplate para mostrar cualquier propiedad del Control donde aplicamos el ControlTemplate.
    • ContentPresenter busca AUTOMATICAMENTE las propiedades ‘Content’,‘ContentTemplate’ y ‘ContentTemplateSelector’ en el control donde se aplica el ControlTemplate a menos que se asignen directamente via TemplateBinding o usando ContentSource. Esto es para minimizar la configuración en Controles de tipo ContentControl.
    • ContentPresenter NO REQUIERE que el Control al que le estamos aplicando el Template herede de la clase ContentControl. Sin embargo, es necesario configurar ‘ContentSource’ para la propiedad que necesitemos mostrar como fue el caso de nuestro control.
    • ContentPresenter SOLAMENTE REQUIERE una propiedad para Content. Tanto ContentTemplate como ContentTemplateSelector son opcionales y se usan automaticamente(via ContentSource) solo si están presentes.
    • La funcionalidad de ContentSource SOLO ESTA DISPONIBLE en ControlTemplate’s y no en DataTemplate’s esto se debe a que el mecanismo de asignación automática de propiedades se hace atreves de TemplatedParent que se asigna solo dentro del contexto de un DataTemplate.

    DataContext y Enlace de Datos

    El DataContext es asignado automaticamente dentro del DataTemplate por el ContentPresenter, es por eso que en la linea 4 podemos ver como se enlaza el ItemsSource del ListView usando la extension Binding de Forma sencilla. Recordemos que este DataTemplate es usado por el ContentPresenter de la propiedad ‘List’ asi que el DataContext contiene el mismo valor asignado a la propiedad ‘List’ del control que ha sido enlazada en la linea 1.

    El mismo caso sucede con el DataTemplate de ‘CurrentItemTemplate’ el cual en su DataContext tiene el valor del la propiedad CurrentItem(nuevamente asignado automaticamente por el ContentPresenter).

    Actualizando propiedades del Control desde el DataTemplate

    Además de Visualizar la lista de elementos, El ListTemplate debe establecer en la propiedad CurrentItem cual es el elemento seleccionado en la lista. Si en vez de trabajar con DataTemplate usáramos ControlTemplate podríamos usar TemplateBinding para hacer esto de forma sencilla pero TemplateBinding solo funciona en ControlTemplate por lo que debemos usar Binding + Relative Source/Ancestor en modo TwoWay como se mostro en la línea 5.

    SelectedItem="{Binding CurrentItem,Mode=TwoWay,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type listeditors:ListEditor}}}"

    Esto causa que cuando se cambie el elemento seleccionado en el ListView se actualice la propiedad CurrentItem.

    Nota: Inicialmente antes de estar consiente del uso tan practico del ContentPresenter yo solía usar propiedades de tipo ControlTemplate. Esta practica no tiene ningún sentido ya que aunque puedo cargar los elementos visuales del ControlTemplate usando su metodo LoadContent() el TemplatedParent de los controles internos siempre estaba en null(Nothing en Visual Basic) ya que solo los Controles deberian usar ControlTemplate directamente.

    Conclusión

    ContentPresenter es una excelente solución para desarrollar controles multi-contenido. Otros controles que hacen uso de este son los siguientes:

    Descargue el proyecto de ejemplo para Visual Studio 2008 LobListEditor.sln

    Friday, October 23, 2009

    22 Tips para un blog exitoso

    Espero les sea util.

    1. El lector de tu blog no quiere saber lo interesante que eres, te leen para saber que tan interesante pueden llegar a ser ellos. Lo que expreses en cada post debe ser de utilidad para ellos y así puedan compartirlo.
    2. Aprende lo suficiente para ser un experto en el tema.
    3. Los visitantes de tu blog son solo curiosos, los seguidores de tu blog les gusta el tema del que hablas, por eso es importante tener un tema central.
    4. Escribe con un tono adecuado para el público al que te diriges. Si el tema del blog es de interés global evita usar slangs locales de tu región.
    5. Es importante acostumbrar a los seguidores a una cierta cantidad de información en cada entrega. Tener como meta un número mínimo y máximo de palabras en cada post es una buena práctica.
    6. Posts cortos son fáciles y rápidos de digerir aunque la lectura está cargada de información. Inconscientemente el seguidor se acostumbrara a leer tu blog aunque tenga poco tiempo porque sabe que los posts son cortos y concisos. Esto requiere una mayor frecuencia de nuevos posts. Sin embargo, posts demasiado cortos con mucha frecuencia hacen el blog irrelevante para leer lo cual no es conveniente.
    7. Post largos y con mucho detalle tiene un público muy exclusivo por lo que no es necesario crear contenido con tanta frecuencia. Post demasiado largos y cargados de poca información hacen que el seguidor omita tu blog para leerlo cuando tengan más tiempo lo cual tampoco es conveniente sino es que deja de seguir tu blog.
    8. Como el tema de tu blog está centrado en un solo tema, cada post nuevo debería tener al menos 1 link a un post anterior.
    9. Da el crédito a quien te inspiro, eso le da credibilidad al tema.
    10. Al menos 1 de cada 10 posts debe invitar al lector a participar directamente. Las encuestas con un límite de tiempo son una buena opción ya que cuando finalice el resultado de la misma y su análisis son un tema para un nuevo post.
    11. Escribe solo cuando tengas algo muy relevante que decir.
    12. Tanto el titulo del blog, como el tema visual y el titulo de post deben tener un estilo y personalidad que se ajuste al tema.
    13. El titulo de cada post, además de tener un estilo particular debe ser llamativo e intrigante para llamar la atención al contenido, esto es particularmente útil si el lector es seguidor de tu blog via Feeds.
    14. La introducción de cada post debe hacer interesante y rápida (no más de dos líneas) para hacer relevante la lectura de todo el post. Evita que la introducción involucre las últimas cosas divertidas que ha hecho tu mascota favorita a menos que tu blog sea acerca de tu mascota o de mascotas divertidas.
    15. No interrumpas el contenido del post colocando más de un link sin antes introducir de que se trata cada uno de ellos o porque están relacionados.
    16. Si colocas Twitter como contacto: Considera la posibilidad de seguir a quien te sigue.
    17. Si colocas Facebook como contacto: Acepta a quien te agregue como amigo.
    18. Si colocas tu Correo electrónico como contacto: Responde los mails.
    19. Si tienes activado los comentarios en tu blog: Lee y responde los comentarios que formulen preguntas directas a ti.
    20. Cada post debe ser considerada como una obra de arte. Usa un procesador de palabras (Word,Writer,etc) para minimizar errores de ortografía y gramática y en cada post pide a un amigo que lea tu post cuanto antes en busca de incoherencias y corrige cualquier error cuanto antes.
    21. Cuando sea posible usa imágenes en cada post. Si el blog se trata de acontecimientos actuales (noticias de última hora, eventos, etc) la imagen de introducción debe ser obligatoria y de gran tamaño y aun más interesante que la lectura misma. Si el blog se trata de material complementario (Temas Geek, HowTo’s) la imagen no debe distraer al lector del contenido.
    22. Sigue este blog.

    Tuesday, October 20, 2009

    Bloggin: La superficial satisfacción de ser admirado

    Cuando inicie este blog en 2006 solo tenía un sencillo objetivo: Tener un blog.

    Eso fue fácil de lograr y sin saber que tener un ritmo de blogging y un tema centrado eran claves para tener un blog exitoso, yo hacia un post cada 2 días y solo posteaba relacionado a lenguajes de programación por lo que para mediados de 2007 tenía un promedio de 4000 visitas únicas mensuales, número que significaba mucho para mí siendo un novato en la web 2.0. Con el pasar del tiempo cambie de objetivo: Tener muchos lectores. Seis meses después casi había abandonado mi blog. ¿Porque razón? Mi primera excusa era que no tenía tiempo para escribir, la segunda fue que blogger.com no formateaba bien mi código y así sucesivamente hasta que deje de escribir. Finalmente descubrí porque razón no continuaría: Porque la superficial satisfacción de ser admirado ya no me llenaba.

    Entonces te podrás preguntar para que esté escribiendo esta vez, ¿será que es así como un blogger confundido hace su propia autobiografía? Tal vez alguna día sea así pero no esta vez, he encontrado un objetivo bien claro para bloggear el cual descubrirán mis lectores con cada post. Hablando de buenas prácticas en blogging les recomiendo leer los siguientes blogs que he venido leyendo religiosamente por más de un año:

    Ninguno de ellos tiene necesidad de que yo les haga un link, pero me siento tan agradecido que siento que de alguna forma les devuelvo el favor, en mi siguiente post les comentare que he aprendido de estos grandes Gurús en marketing y web media y como han contribuido al objetivo de mi blog: ¿y tú ya tienes claro cuál es el objetivo del tuyo?