Windows 8: Mostrar elementos de diferentes tamaños en un GridView de una aplicación de Windows Store (Metro)

Hoy me gustaría compartir con todos algo que me parece cuanto menos importante e interesante y que hará que nuestras aplicaciones de Windows Store (Metro) tengan un look más atractivo. A lo que me refiero es disponer de elementos de diferentes tamaños en dentro de un GridView sin perder la organización de los mismos tal y como se ve en la aplicación de la Windows Store de Windows 8. Para ilustrar la explicación me referiré a la aplicación de WinPhone metro y explicaré los pasos que seguí para realizar la transformación. Si queréis podéis descargaros el código fuente del ejemplo aquí.

 

  • Vamos a comenzar partiendo de un proyecto basado en la plantilla Aplicación de Cuadrícula o, en inglés, Grid App con lo que ya tendremos datos de ejemplo para poder dar algo de forma a nuestro diseño. Si la ejecutamos directamente tal y como la monta Visual Studio, obtendremos una interfaz muy cuadriculada, sin atractivo y queremos darle algo más de vida a los elementos del GridView.
  • En primer lugar, preparo la estructura en la que organizaré todas las modificaciones. Por ello, he creado una carpeta dentro del proyecto a la que he llamado “Styles” y, a continuación, creo un nuevo diccionario de recursos que llamaré Grid.xaml.
  • El siguiente paso, será agregar la referencia al nuevo diccionario de recursos dentro del fichero App.xaml
           <ResourceDictionary.MergedDictionaries>
 
                <!-- 
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
                <ResourceDictionary Source="Styles/Grid.xaml"/>
            </ResourceDictionary.MergedDictionaries>

 

  • Editamos una copia de la plantilla de elementos (Panel) o en inglés Layout of Items (Panel) del GridView y la ubicaremos en el diccionario de recursos que acabamos de crear, Grid.xaml, donde editaremos el VariableSizedWrapGrid.
  • Al VariableSizedWrapGrid le pondremos un ancho máximo por ejemplo de 750px (MaxWidth=”750”) y vamos a establecer tanto el ancho como el alto de los elementos que va a contener a 250 y 180 píxeles respectivamente (ItemWidth=»250″ ItemHeight=»180″). Con lo que tendríamos algo como esto:

 

<ItemsPanelTemplate x:Key="GroupItemsPanel">
    <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0" MaxWidth="750" ItemWidth="250" ItemHeight="180" />
</ItemsPanelTemplate>

 

  • El siguiente paso que debemos seguir es editar la plantilla de los elementos generados (ItemTemplate) del GridView, y lo haremos editando una copia que ubicaremos también en el diccionario de recursos Grid.xaml.

 

  • De la plantilla que se genera, eliminamos tanto el ancho como el alto del grid, ya que el tamaño de los elementos ya lo estamos controlando desde el VariableSizedWrapGrid. Con lo que la plantilla de los elementos nos quedaría de la siguiente forma:
    <DataTemplate x:Key="GridItemTemplate">
        <Grid HorizontalAlignment="Left" >
            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
            </Border>
            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
            </StackPanel>
        </Grid>
    </DataTemplate>

 

  • Llegado este punto, toca contar que, para controlar el tamaño de nuestros elementos debemos imaginarnos una matriz o tabla cuyas celdas son del tamaño que especificamos para los elementos del VariableSizedWrapGrid. Entonces, como ejemplo, propongo tener un elemento que ocupe 2 celdas de ancho y 2 celdas de alto, un elemento que ocupe 1 celda de ancho y 2 celdas de alto y, por último, un elemento estándar que ocupe 1 celda tanto de ancho como de alto. ¿Cómo conseguimos esto? Pues bien, nos crearemos tres plantillas de estilo para elementos del tipo GridViewItem y, tal y como  se hace en HTML, vamos a especificar las propiedades ColumnSpan que indica el número de columnas el elemento y RowSpan que indica el número de filas. Por lo tanto, abrimos el fichero Grid.xaml y creamos tres tipos de estilo diferentes. Al primero, GridItemStyleSmall, no es necesario que le establezcamos la propiedad ColumnSpan pero lo he puesto para ilustrar mejor el ejemplo
    <Style x:Key="GridItemStyleSmall" TargetType="GridViewItem">
        <Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="1" />
    </Style>
 
    <Style x:Key="GridItemStyleLarge" TargetType="GridViewItem">
        <Setter Property="VariableSizedWrapGrid.RowSpan" Value="2" />
        <Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="2" />
    </Style>
 
    <Style x:Key="GridItemStyleVertical" TargetType="GridViewItem">
        <Setter Property="VariableSizedWrapGrid.RowSpan" Value="2" />
    </Style>

 

  • El siguiente paso es el más complicado quizás de entender, pero, en definitiva es muy sencillo de implementar. Tenemos que indicar al GridView el estilo de sus elementos pero, como son varios estilos, no podemos hacerlo directamente por lo que tendremos que recurrir a un convertidor de estilos llamado StyleSelector. Creamos una nueva elemento de tipo clase dentro de la carpeta Styles  y le damos el nombre de GroupStyleSelector. El código que debería contener es el siguiente.
using GridTest.Data;
using System;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
 
namespace GridTest.Styles
{
    public class GroupStyleSelector : StyleSelector
    {
        protected override Style SelectStyleCore(object item, DependencyObject container)
        {
            var element = (SampleDataItem)item;
 
            if (element.UniqueId.Contains("Item-1"))
                return Application.Current.Resources["GridItemStyleLarge"] as Style;
            else if (element.UniqueId.Contains("Item-4"))
                return Application.Current.Resources["GridItemStyleVertical"] as Style;
 
            return Application.Current.Resources["GridItemStyleSmall"] as Style;
        }
    }
}

Las condiciones de los “bloques if” dependerán de a qué elementos queremos aplicar qué plantillas. En este caso que estamos haciendo uso de la plantilla de aplicación de tipo Grid que viene por defecto en Visual Studio, los elementos que se usan para mostrar datos en el GridView tienen un campo UniqueId que está compuesto por Grupo-X-Item-Y, con lo que yo estoy condicionando por el primero y por el cuarto elemento de cada grupo a los que aplicaré los estilos GridItemStyleLarge y GridItemStyleVertical respectivamente.

  • Nos falta un último paso para que todo esté listo, y no es más que hacer que el GridView use nuestro GroupStyleSelector para seleccionar la plantilla que tendrá cada elemento en cuestión. Para hacerlo, abriremos el fichero App.xaml y añadiremos una referencia a este archivo como contenido de un ResourceDictionary.
           <ResourceDictionary.MergedDictionaries>
                <!-- 
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
                <ResourceDictionary Source="Styles/Grid.xaml"/>
                <ResourceDictionary>
                    <styles:GroupStyleSelector x:Key="GroupStyleSelector" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>

  • Por último, ahora que ya el código XAML es capaz de acceder al selector de estilos que hemos creado, se lo asignamos al GridView añadiéndole la propiedad ItemContainerStyleSelector y le indicamos que debe acceder al recurso GroupStyleSelector.
        <GridView
            x:Name="itemGridView"
            AutomationProperties.AutomationId="ItemGridView"
            AutomationProperties.Name="Grouped Items"
            Grid.RowSpan="2"
            Padding="116,137,40,46"
            ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
            ItemTemplate="{StaticResource GridItemTemplate}"
            ItemContainerStyleSelector="{StaticResource GroupStyleSelector}" 
            SelectionMode="None"
            IsSwipeEnabled="false"
            IsItemClickEnabled="True"
            ItemClick="ItemView_ItemClick">

  • Y… todo listo para mostrar elementos de tamaño variable dentro de nuestro GridView. El resultado lo dice todo.

Código Fuente | GridTest

download

Windows 8: Cambiar el borde y la marca de selección de elementos en GridView o ListView

El artículo de hoy va más dedicado a los diseñadores que, como a mi, les gusta ponerse creativos y darle un toque “especial” a sus aplicaciones.

Por eso, vamos a ver cómo cambiar el color del borde y la marca (triángulo con el check de la esquina superior derecha) de selección de elementos tanto en GridView como en ListView

Para ilustrar el ejemplo, voy a usar una aplicación en la que he realizado el diseño y, no es otra que la aplicación para Windows 8 de CompartiMOSS (Ya en su Windows Store) a la que le he cambiado el color de selección a azul en vez del violeta que viene por defecto como podemos ver en la aplicación de ejemplo de Microsoft “Contoso Cookbook”

Para conseguir este objetivo, tenemos que empezar por editar la plantilla del contenedor de items (ItemContainerStyle) del GridView o ListView tal y haciendo click sobre el control con el botón derecho del ratón, y seleccionando “Edit Aditional Templates –> Edit Generated Item Container (ItemContainerStyle) –> Edit a Copy” como muestro en la imagen. 

En la ventana emergente que se nos muestra, voy a seleccionar crear el nuevo estilo dentro de la página actual aunque, recomiendo que pongan estos estilos en un fichero aparte para poder hacer más legible el código XAML que si sois como yo, lo vais a mirar mucho. Volviendo a lo importante, llamaré al estilo “MyListItemContainerStyle” y presiono OK.

Esto nos generará dentro de la página un bloque de estilo como el que os muestro a continuación, pero advierto que es enorme y para los que no estés muy sueltos en código XAML y “VisualStates” igual os suena a chino.

 

 

<Style x:Key="MyListItemContainerStyle" TargetType="GridViewItem">

    <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>

    <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>

    <Setter Property="Background" Value="Transparent"/>

    <Setter Property="TabNavigation" Value="Local"/>

    <Setter Property="IsHoldingEnabled" Value="True"/>

    <Setter Property="Margin" Value="0,0,2,2"/>

    <Setter Property="Template">

        <Setter.Value>

            <ControlTemplate TargetType="GridViewItem">

                <Border x:Name="OuterContainer">

                    <VisualStateManager.VisualStateGroups>

                        <VisualStateGroup x:Name="CommonStates">

                            <VisualState x:Name="Normal"/>

                            <VisualState x:Name="PointerOver">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PointerOverBorder"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="SelectionBackground">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="SelectedBorder">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedPointerOverBorderThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="SelectedEarmark">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="Pressed">

                                <Storyboard>

                                    <PointerDownThemeAnimation TargetName="ContentContainer"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="PointerOverPressed">

                                <Storyboard>

                                    <PointerDownThemeAnimation TargetName="ContentContainer"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PointerOverBorder"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="SelectionBackground">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="SelectedBorder">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedPointerOverBorderThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="SelectedEarmark">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="Disabled">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="{StaticResource ListViewItemDisabledThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="contentPresenter"/>

                                </Storyboard>

                            </VisualState>

                        </VisualStateGroup>

                        <VisualStateGroup x:Name="FocusStates">

                            <VisualState x:Name="Focused">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisual"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="Unfocused"/>

                            <VisualState x:Name="PointerFocused"/>

                        </VisualStateGroup>

                        <VisualStateGroup x:Name="SelectionHintStates">

                            <VisualStateGroup.Transitions>

                                <VisualTransition GeneratedDuration="0:0:0.65" To="NoSelectionHint"/>

                            </VisualStateGroup.Transitions>

                            <VisualState x:Name="VerticalSelectionHint">

                                <Storyboard>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="0" TargetName="SelectionBackground" ToVerticalOffset="15"/>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="0" TargetName="ContentBorder" ToVerticalOffset="15"/>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="0" TargetName="SelectedBorder" ToVerticalOffset="15"/>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="0" TargetName="SelectedCheckMark" ToVerticalOffset="15"/>

                                    <DoubleAnimationUsingKeyFrames Duration="0:0:0.500" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyph">

                                        <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0.5"/>

                                        <DiscreteDoubleKeyFrame KeyTime="0:0:0.500" Value="0"/>

                                    </DoubleAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="HorizontalSelectionHint">

                                <Storyboard>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="-23" TargetName="SelectionBackground" ToVerticalOffset="0"/>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="-23" TargetName="ContentBorder" ToVerticalOffset="0"/>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="-23" TargetName="SelectedBorder" ToVerticalOffset="0"/>

                                    <SwipeHintThemeAnimation ToHorizontalOffset="-23" TargetName="SelectedCheckMark" ToVerticalOffset="0"/>

                                    <DoubleAnimationUsingKeyFrames Duration="0:0:0.500" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyph">

                                        <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0.5"/>

                                        <DiscreteDoubleKeyFrame KeyTime="0:0:0.500" Value="0"/>

                                    </DoubleAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="NoSelectionHint"/>

                        </VisualStateGroup>

                        <VisualStateGroup x:Name="SelectionStates">

                            <VisualState x:Name="Unselecting">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyphBorder"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="Unselected">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyphBorder"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="UnselectedPointerOver">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyphBorder"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedForegroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="UnselectedSwiping">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="0.5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectingGlyph"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyphBorder"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="Selecting">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectionBackground"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedBorder"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectingGlyph"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HintGlyphBorder"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedForegroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="Selected">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectionBackground"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedBorder"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedCheckMark"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedForegroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="SelectedSwiping">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectionBackground"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedBorder"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedCheckMark"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedForegroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="SelectedUnfocused">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectionBackground"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedBorder"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedCheckMark"/>

                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="contentPresenter">

                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ListViewItemSelectedForegroundThemeBrush}"/>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                        </VisualStateGroup>

                        <VisualStateGroup x:Name="DragStates">

                            <VisualStateGroup.Transitions>

                                <VisualTransition GeneratedDuration="0:0:0.2" To="NotDragging"/>

                            </VisualStateGroup.Transitions>

                            <VisualState x:Name="NotDragging"/>

                            <VisualState x:Name="Dragging">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="{StaticResource ListViewItemDragThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InnerDragContent"/>

                                    <DragItemThemeAnimation TargetName="InnerDragContent"/>

                                    <FadeOutThemeAnimation TargetName="SelectedCheckMarkOuter"/>

                                    <FadeOutThemeAnimation TargetName="SelectedBorder"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="DraggingTarget">

                                <Storyboard>

                                    <DropTargetItemThemeAnimation TargetName="OuterContainer"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="MultipleDraggingPrimary">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="MultiArrangeOverlayBackground"/>

                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="MultiArrangeOverlayText"/>

                                    <DoubleAnimation Duration="0" To="{StaticResource ListViewItemDragThemeOpacity}" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentBorder"/>

                                    <FadeInThemeAnimation TargetName="MultiArrangeOverlayBackground"/>

                                    <FadeInThemeAnimation TargetName="MultiArrangeOverlayText"/>

                                    <DragItemThemeAnimation TargetName="ContentBorder"/>

                                    <FadeOutThemeAnimation TargetName="SelectionBackground"/>

                                    <FadeOutThemeAnimation TargetName="SelectedCheckMarkOuter"/>

                                    <FadeOutThemeAnimation TargetName="SelectedBorder"/>

                                    <FadeOutThemeAnimation TargetName="PointerOverBorder"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="MultipleDraggingSecondary">

                                <Storyboard>

                                    <FadeOutThemeAnimation TargetName="ContentContainer"/>

                                </Storyboard>

                            </VisualState>

                        </VisualStateGroup>

                        <VisualStateGroup x:Name="ReorderHintStates">

                            <VisualStateGroup.Transitions>

                                <VisualTransition GeneratedDuration="0:0:0.2" To="NoReorderHint"/>

                            </VisualStateGroup.Transitions>

                            <VisualState x:Name="NoReorderHint"/>

                            <VisualState x:Name="BottomReorderHint">

                                <Storyboard>

                                    <DragOverThemeAnimation Direction="Bottom" ToOffset="{StaticResource ListViewItemReorderHintThemeOffset}" TargetName="ReorderHintContent"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="TopReorderHint">

                                <Storyboard>

                                    <DragOverThemeAnimation Direction="Top" ToOffset="{StaticResource ListViewItemReorderHintThemeOffset}" TargetName="ReorderHintContent"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="RightReorderHint">

                                <Storyboard>

                                    <DragOverThemeAnimation Direction="Right" ToOffset="{StaticResource ListViewItemReorderHintThemeOffset}" TargetName="ReorderHintContent"/>

                                </Storyboard>

                            </VisualState>

                            <VisualState x:Name="LeftReorderHint">

                                <Storyboard>

                                    <DragOverThemeAnimation Direction="Left" ToOffset="{StaticResource ListViewItemReorderHintThemeOffset}" TargetName="ReorderHintContent"/>

                                </Storyboard>

                            </VisualState>

                        </VisualStateGroup>

                        <VisualStateGroup x:Name="DataVirtualizationStates">

                            <VisualState x:Name="DataAvailable"/>

                            <VisualState x:Name="DataPlaceholder">

                                <Storyboard>

                                    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PlaceholderTextBlock">

                                        <DiscreteObjectKeyFrame KeyTime="0">

                                            <DiscreteObjectKeyFrame.Value>

                                                <Visibility>Visible</Visibility>

                                            </DiscreteObjectKeyFrame.Value>

                                        </DiscreteObjectKeyFrame>

                                    </ObjectAnimationUsingKeyFrames>

                                    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PlaceholderRect">

                                        <DiscreteObjectKeyFrame KeyTime="0">

                                            <DiscreteObjectKeyFrame.Value>

                                                <Visibility>Visible</Visibility>

                                            </DiscreteObjectKeyFrame.Value>

                                        </DiscreteObjectKeyFrame>

                                    </ObjectAnimationUsingKeyFrames>

                                </Storyboard>

                            </VisualState>

                        </VisualStateGroup>

                    </VisualStateManager.VisualStateGroups>

                    <Grid x:Name="ReorderHintContent" Background="Transparent">

                        <Path x:Name="SelectingGlyph" Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{StaticResource ListViewItemCheckSelectingThemeBrush}" FlowDirection="LeftToRight" HorizontalAlignment="Right" Height="13" Margin="0,9.5,9.5,0" Opacity="0" Stretch="Fill" VerticalAlignment="Top" Width="15"/>

                        <Border x:Name="HintGlyphBorder" HorizontalAlignment="Right" Height="40" Margin="4" Opacity="0" VerticalAlignment="Top" Width="40">

                            <Path x:Name="HintGlyph" Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{StaticResource ListViewItemCheckHintThemeBrush}" FlowDirection="LeftToRight" HorizontalAlignment="Right" Height="13" Margin="0,5.5,5.5,0" Opacity="0" Stretch="Fill" VerticalAlignment="Top" Width="15"/>

                        </Border>

                        <Border x:Name="ContentContainer">

                            <Grid x:Name="InnerDragContent">

                                <Rectangle x:Name="PointerOverBorder" Fill="{StaticResource ListViewItemPointerOverBackgroundThemeBrush}" IsHitTestVisible="False" Margin="1" Opacity="0"/>

                                <Rectangle x:Name="FocusVisual" IsHitTestVisible="False" Opacity="0" Stroke="{StaticResource ListViewItemFocusBorderThemeBrush}" StrokeThickness="2"/>

                                <Rectangle x:Name="SelectionBackground" Fill="{StaticResource ListViewItemSelectedBackgroundThemeBrush}" Margin="4" Opacity="0"/>

                                <Border x:Name="ContentBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="4">

                                    <Grid>

                                        <ContentPresenter x:Name="contentPresenter" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>

                                        <TextBlock x:Name="PlaceholderTextBlock" Foreground="{x:Null}" IsHitTestVisible="False" Margin="{TemplateBinding Padding}" Text="Xg" Visibility="Collapsed"/>

                                        <Rectangle x:Name="PlaceholderRect" Fill="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" IsHitTestVisible="False" Visibility="Collapsed"/>

                                        <Rectangle x:Name="MultiArrangeOverlayBackground" Fill="{StaticResource ListViewItemDragBackgroundThemeBrush}" IsHitTestVisible="False" Opacity="0"/>

                                    </Grid>

                                </Border>

                                <Rectangle x:Name="SelectedBorder" IsHitTestVisible="False" Margin="4" Opacity="0" Stroke="{StaticResource ListViewItemSelectedBackgroundThemeBrush}" StrokeThickness="{StaticResource GridViewItemSelectedBorderThemeThickness}"/>

                                <Border x:Name="SelectedCheckMarkOuter" HorizontalAlignment="Right" IsHitTestVisible="False" Margin="4" VerticalAlignment="Top">

                                    <Grid x:Name="SelectedCheckMark" Height="40" Opacity="0" Width="40">

                                        <Path x:Name="SelectedEarmark" Data="M0,0 L40,0 L40,40 z" Fill="{StaticResource ListViewItemSelectedBackgroundThemeBrush}" Stretch="Fill"/>

                                        <Path Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="{StaticResource ListViewItemCheckThemeBrush}" FlowDirection="LeftToRight" HorizontalAlignment="Right" Height="13" Margin="0,5.5,5.5,0" Stretch="Fill" VerticalAlignment="Top" Width="15"/>

                                    </Grid>

                                </Border>

                                <TextBlock x:Name="MultiArrangeOverlayText" Foreground="{StaticResource ListViewItemDragForegroundThemeBrush}" FontSize="26.667" FontFamily="{StaticResource ContentControlThemeFontFamily}" IsHitTestVisible="False" Margin="18,9,0,0" Opacity="0" TextWrapping="Wrap" Text="{Binding TemplateSettings.DragItemsCount, RelativeSource={RelativeSource Mode=TemplatedParent}}" TextTrimming="WordEllipsis"/>

                            </Grid>

                        </Border>

                    </Grid>

                </Border>

            </ControlTemplate>

        </Setter.Value>

    </Setter>

</Style>

 

Como podéis ver es un estilo muy largo y nos puede resultar complejo saber qué tenemos que modificar, por eso, voy a indicaros dónde tendréis que realizar cambios.

1 –> Para  empezar vamos a cambiar cómo se verá por defecto un elemento seleccionado y, para ello debemos buscar al final de todo este estilo los elementos que componen el estilo de selección y que son el borde, el triángulo de la esquina superior derecha y el símbolo de check.

 

 

 

<Rectangle x:Name="SelectionBackground" Fill="#DE0072cb" Margin="4" Opacity="0"/>

<Border x:Name="ContentBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="4">

    <Grid>

        <ContentPresenter x:Name="contentPresenter" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>

        <TextBlock x:Name="PlaceholderTextBlock" Foreground="{x:Null}" IsHitTestVisible="False" Margin="{TemplateBinding Padding}" Text="Xg" Visibility="Collapsed"/>

        <Rectangle x:Name="PlaceholderRect" Fill="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" IsHitTestVisible="False" Visibility="Collapsed"/>

        <!-- Selected Border -->

        <Rectangle x:Name="SelectedBorder" IsHitTestVisible="False" Opacity="0" Stroke="#DE0072cb" StrokeThickness="{StaticResource GridViewItemSelectedBorderThemeThickness}"/>

        <Rectangle x:Name="MultiArrangeOverlayBackground" Fill="{StaticResource ListViewItemDragBackgroundThemeBrush}" IsHitTestVisible="False" Opacity="0"/>

    </Grid>

</Border>

<Border x:Name="SelectedCheckMarkOuter" HorizontalAlignment="Right" IsHitTestVisible="False" Margin="4" Padding="{TemplateBinding BorderThickness}" VerticalAlignment="Top">

    <Grid x:Name="SelectedCheckMark" Height="40" Opacity="0" Width="40">

        <!-- Selected Corner -->

        <Path x:Name="SelectedEarmark" Data="M0,0 L40,0 L40,40 z" Fill="#DE0072cb" Stretch="Fill"/>

        <!-- Check symbol -->

        <Path Data="F1 M133.1,17.9 L137.2,13.2 L144.6,19.6 L156.4,5.8 L161.2,9.9 L145.6,28.4 z" Fill="#FFFFFFFF" FlowDirection="LeftToRight" HorizontalAlignment="Right" Height="13" Margin="0,5.5,5.5,0" Stretch="Fill" VerticalAlignment="Top" Width="15"/>

    </Grid>

</Border>

 

Como sigue siendo un poco lioso, os he marcado en negrita y con una fuente mayor los elementos que vamos a tratar. que serán, el Rectángulo con x:Name=”SelectedBorder” que corresponde al borde de selección y también al y fondo (si el elemento tiene transparencia), el Path con x:Name=”SelectedEarmark” que es el triángulo de la esquina superior derecha y, por último, el Path sin nombre que corresponde a la marca de “Check”. En estos elementos editaremos el color con el que deseamos aplicar por defecto a la selección.

 

2—> Lo siguiente es modificar los estados visuales que correspondan a los que queremos editar, por ejemplo, el más común es el que ocurre al pasar el ratón por encima “PointerOver”, aunque en entornos táctiles quizás nos pueda interesar más el estado visual que ocurre al tener presionado un elemento “PointerOverPressed” o “Pressed”. Para cada uno de los casos, las acciones a tomar son las mismas por lo que sólo pondré el ejemplo del “PointerOver”.

 

 

<VisualState x:Name="PointerOver">

    <Storyboard>

        <!-- Establece la opacidad del Rectángulo de fondo y borde a 1 con lo que ahora será visible -->

        <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PointerOverBorder"/>

 

        <!-- Establece el color del fondo del Rectángulo que se corresponderá con el fondo del elemento seleccionado -->

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="SelectionBackground">

            <DiscreteObjectKeyFrame KeyTime="0" Value="#DE0072cb"/>

        </ObjectAnimationUsingKeyFrames>

 

        <!-- Borde del rectángulo -->

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="SelectedBorder">

            <DiscreteObjectKeyFrame KeyTime="0" Value="#DE0072cb"/>

        </ObjectAnimationUsingKeyFrames>

 

        <!-- Color de fondo del triángulo de la esquina al pasar por encima después de seleccionar -->

        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="SelectedEarmark">

            <DiscreteObjectKeyFrame KeyTime="0" Value="#DE0072cb"/>

        </ObjectAnimationUsingKeyFrames>

    </Storyboard>

</VisualState>

 

He resaltado los elementos que vamos a modificar y que paso a explicar:

  1. Se establece el valor de opacidad del rectángulo que contiene a los elementos y que define tanto el fondo como el borde de los mismos.
  2. Se establece el valor que queramos para el color de fondo del rectángulo que sólo será visible si el elemento del GridView o del ListView (según proceda) tiene transparencia.
  3. Se establece el valor que queramos para el color del borde del rectángulo y, por consiguiente, del elemento del GridView o del ListView (según proceda)
  4. Se establece el valor que queramos para el color de fondo del triángulo de la esquina superior derecha que aparece cuando un elemento ha sido seleccionado.

 

Hecho esto, y editados los estados visuales que queramos, habremos acabado de personalizar la selección de un elemento en un GridView o ListView y, en mi caso de CompartiMOSS obtengo el resultado deseado que os mostré al inicio del artículo.

SharePoint 2010: Añadir “metadatos” a las carpetas de una biblioteca de documentos

En este artículo expondré un pequeño tutorial sobre cómo añadir “metadatos” a las carpetas de una biblioteca de documentos en X pasos:

Paso 1

Para comenzar, tendremos que añadir un tipo de contenido a partir del tipo de contenido de carpeta que incluya estos metadatos, por lo que debemos ir a la administración del sitio y realizar las siguientes acciones

  • Seleccionar la opción “Tipos de contenido del sitio”.

 

  • Seleccionar “Crear” y rellenar los datos del nuevo tipo de contenido teniendo en cuenta que debemos partir del tipo de contenido de carpeta existente.

 

  • Añadir el/los campos que necesitemos como metadatos para las carpetas en el tipo de contenido.

 

Paso 2

Ahora que ya tenemos el tipo de contenido de carpeta personalizado vamos a crear una librería de documentos para probar que todo funciona correctamente.

  • Crear la librería de documentos, lo que podemos hacer desde el menú desplegable de acciones del sitio.

 

  • Configurar la librería de documentos que acabamos de crear para poder administrar los tipos de contenido de la misma.

 

  • Añadir el tipo de contenido MyFolder que creamos anteriormente a la librería de documentos.

 

  • Desplegar el menú para añadir un elemento y seleccionar MyFolder para crear una nueva carpeta a la que le podremos añadir los metadatos deseados.
  • Si queremos visualizar estos metadatos, tendremos que editar la vista de la librería para seleccionar las columnas que queremos mostrar y el orden de las mismas tal y como indico en la imagen.

 

Y esto es todo lo necesario para añadir metadatos a las carpetas en librerías y listas de SharePoint 2010. Espero que os sirva de ayuda.

Mi experiencia en el Megathon de Windows 8

Más vale tarde que nunca se suele decir y, es que he tardado dos semanas en poder escribir este artículo para expresar mi experiencia colaborando en el Megathon de Windows 8 junto con mis compañeros de TenerifeDev, Jose Fortes, Alberto Díaz y David Rodríguez.

La verdad es que fue un duro trabajo organizar el evento y preparar las charlas para intentar ofrecer la mejor formación posible a los asistentes pero… he de decir que quedé impresionado con la gran acogida recibida por parte de la comunidad de programadores de las islas.

Por mi parte, me encargué de dar 2 charlas, colaborar en alguna otra y de resolver las dudas de los asistentes que, de verdad fueron unos auténticos “cracks”.

Los datos lo dicen todo:

  • 40 asistentes comenzaron el fin de semana entre los que se encontraban algunos que sólo deseaban recibir la formación y hacer “Networking”.
  • 6 equipos llegaron al final con sus aplicaciones listas para concursar
  • 6 aplicaciones presentadas a la final local con muy buena pinta teniendo incluso que otorgar una mención especial a un chico que sin saber programar en C# y mucho menos XAML, viniendo del mundo java y con un MacBook a cuestas… se atrevió a desarrollar una idea muy buena.
  • Patrocinadores que aportaron diferentes recursos para facilitar las duras jornadas de programación.
  • Medios de comunicación que cubrieron el evento
  • ¡Y premios! Muchos premios por cortesía de Microsoft, PluralSight, Telerik, TenerifeDev,…

En definitiva, un evento que me gustaría repetir pronto. ¿Megathon de Windows Phone 8? Ojalá se organice y que pueda participar.

Aquí os dejo las fotos de los equipos participantes presentando sus aplicaciones

  • Equipo 1: Restaurant Ranking
  • Equipo 1: Photo DashBoard  (GANADORES)
  • Equipo 1: Collectables
  • Equipo 1: MetroReader
  • Equipo 1: myVitae
  • Equipo 1: GeoTag

¡¡Con Windows 8 y Windows Phone en la Tenerife Lan Party 2k12!!

Este año, pese a la crisis que azota al país, se volverá a celebrar una nueva edición de la Tenerife Lan Party 2k12 (TLP2k12) que, como ya es costumbre incluye una zona para profesionales denominada TLP+i en la que se incluyen conferencias y talleres relacionados con la tecnología, la innovación y la empresa, siempre desde el punto de vista de las TIC.

En esta edición de 2012, me “llena de orgullo y satisfacción felicitar a todos los españoles…” digo, anunciar que seré partícipe de dos conferencias y otros tantos talleres junto a Alberto Díaz Martín (vaya desastre que podemos armar) sobre Diseño y desarrollo con Windows 8 y Diseño y desarrollo con Windows Phone 7.

Esta participación ha sido posible gracias a la colaboración que año tras año viene realizando TenerifeDev, grupo de usuarios de .NET de Tenerife del que soy nuevo colaborador, con la TLP en forma de numerosos e interesantes conferencias y talleres además de atrayendo a patrocinadores como Telerik, Pluralsight y Microsoft que otra vez este año vuelven a apoyarnos aportando regalos a repartir entre los asistentes a nuestras conferencias y talleres.

Aprovecho para incidir en que sortearemos muchos y diferentes regalos entre los asistentes a las conferencias y talleres

  • Camisetas
  • Suscripciones de Pluralsight
  • Licencias de controles de Telerik
  • Algunas sorpresas más
  • Y los regalos estrella se sortearán entre los asistentes a la conferencia o al curso-taller de Windows 8, unos maravillosos ¡¡¡Nokia Lumia 800!!!

Las fechas y horarios en los que participaremos los colaboradores de TenerifeDev son los siguientes:

Jueves 19 de Julio

Viernes 20 de Julio

Espero veros a todos por allí… ¡que pasaremos lista!

SharePoint 2010: Diseño metro en un Web Part de Vínculos de Resumen (Summary Links Web Part)

 

Ahora que está de moda el estilo Metro gracias a Windows Phone 7 y ahora también por Windows 8 y Windows Phone 8, no está de más que adaptemos el diseño de nuestro sitio en SharePoint 2010. Como hay que editar varios y diferentes elementos, las explicaciones irán acompañadas con imágenes para ayudar a entender qué se está haciendo en cada momento. Las capturas de pantalla y el código usado (CSS y XSL) los pueden encontrar en esta carpeta compartida en SkyDrive

 

En esta ocasión, me gustaría compartir cómo dar algo de estilo metro a un Web Part de Vínculos de Resumen (Summary Links Web Part) con lo trataré de hacer la transformación que muestro en la imagen.

 

Como requisitos hay que indicar que es necesario tener activada la característica de la colección de sitios SharePoint Server Publishing Infraestructure tal y como muestro en la imagen.

Para lograr nuestro objetivo de dar aspecto Metro al Web Part de Vínculos de Resumen, abriremos SharePoint Designer 2012 donde crearé las carpetas Images y Css que suelo usar para los diseños personalizados con la ruta “All Files –> Style Library –> NombreDeProyecto”. En el ejemplo, NombreDeProyecto será MetroSite

 

Ahora comenzaremos creando la plantilla XSL que aplicaremos a los elementos del Web Part, con lo que deberemos editar el archivo ItemStyle.xsl que se encuentra en “All Files –> Style Library –> XSL Style Sheets –> ItemStyle.xsl” como se muestra en la imagen.

Una vez abramos ItemStyle.xsl para editar, introduciremos una nueva plantilla para los Items que en este ejemplo he llamado LinkTile y cuyo código será el siguiente:

<xsl:template name="LinkTile" match="Row[@Style='LinkTile']" mode="itemstyle">

     <xsl:variable name="SafeLinkUrl">

         <xsl:call-template name="OuterTemplate.GetSafeLink">

             <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>

         </xsl:call-template>

     </xsl:variable>

     <xsl:variable name="SafeImageUrl">

         <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">

             <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>

         </xsl:call-template>

     </xsl:variable>

     <xsl:variable name="DisplayTitle">

         <xsl:call-template name="OuterTemplate.GetTitle">

             <xsl:with-param name="Title" select="@Title"/>

             <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>

         </xsl:call-template>

     </xsl:variable>

     <div class="divLinkTile">

         <xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>

         <a class="aLinkTile" href="{$SafeLinkUrl}" title="{@LinkToolTip}">

             <xsl:if test="$SafeImageUrl=''">

                 <img src="/Style Library/MetroSite/Images/link-default.png" alt="Link" />

             </xsl:if>

             <xsl:if test="$SafeImageUrl!=''">

                 <img src="{$SafeImageUrl}" alt="Action Icon" />

             </xsl:if>

             <div class="divTitle">

                   <xsl:value-of select="$DisplayTitle" />

               </div>

             <div class="divDescription">

                 <xsl:value-of select="@Description" />

             </div>

         </a>

     </div>

 </xsl:template>

 

Como se puede comprobar, se exponen los parámetros SafeLinkUrl, SafeImageUrl y DisplayTitle que se requerirán en la configuración del Web Part. Además, en la estructura del item, se pone un icono por defecto, con lo que llegado este punto, añadiremos los iconos que necesitamos para nuestros elemento.

En el ejemplo, he desplegado los iconos en “All Files –> Style Library –> MetroSite –> Images” .

El siguiente paso será añadir una hoja de estilos para controlar el aspecto Metro del elemento. Para este paso, he creado la estructura “All Files –> Style Library –> MetroSite –> Css” y, en esta carpeta añado un fichero .css que llamaré summarylink.css con el siguiente contenido.

.divLinkTile

{

    background: #0074c0;

    margin: 12px 6px; 

}

 

.divLinkTile .aLinkTile

{

    display: block;

    height: expression(this.scrollHeight < 89 ? "88px" : "auto");

    min-height: 88px;

    padding: 6px;

    font-family: Segoe UI, Century Gothic, Cambria;

    color: #fff !important;

    text-decoration: none;    

}

 

.divLinkTile .aLinkTile:hover

{

}

 

.divLinkTile .aLinkTile img

{

    border: none;

    float: left;

    margin-right: 6px;

    vertical-align: middle;

}

 

.divLinkTile .aLinkTile .divTitle

{

    display: table-cell;

    vertical-align: middle;

    margin-left: 36px;

    min-height: 48px;

    font-size: 23px;

    font-weight: normal; 

    color: #fff !important;

    margin: 0;

}

 

.divLinkTile .divDescription

{

    margin-top: 12px;

    font-size: 11px;

}

 

Ahora que tenemos todos los elementos, nos quedaría añadir la hoja de estilos a la masterpage que, en este ejemplo es la v4.master, y lo haremos poniendo el enlace a nuestro fichero summarylinks.css con la siguiente instrucción.

<link rel="stylesheet" type="text/css" href="/Style Library/MetroSite/Css/summarylinks.css"/>

 

Por último, sólo nos queda ir aplicando los estilos a los enlaces del Web Part y, esto lo haremos editando cada uno de los enlaces para aplicarle los estilos.

Una vez hayamos aplicado la plantilla al elemento, comprobamos que se muestra con estilo metro y que aparece el icono por defecto. Finalmente podemos personalizar el icono de los elementos y aplicar la plantilla al resto.

 

Siguiendo estos pasos, ya podremos trabajar con nuestro Summay Links Web Part de estilo metro.

 

NOTA: Para ahorrarnos trabajo, el Web Part se puede configurar para que todos los elementos tengan por defecto la plantilla deseada, por lo que no habría más que especificar que se aplique por defecto la plantilla LinkTile para que todos los elementos que creemos aparezcan con estilo metro.

Espero que os haya gustado y os sirva de ayuda.

SharePoint 2010: Mostrar/Ocultar “Ver todo el contenido del sitio” y “Papelera de reciclaje”

Para entrar en contexto, estos elementos son los que aparecen en la zona de QuickLinks, en el panel que se encuentra a la izquierda y que se sitúan justo debajo de los enlaces a las Listas y Librerías.

En algunas ocasiones nos encontramos que las opciones “Ver todo el contenido del sitio” y “Papelera de reciclaje” no se encuentran visibles. Esto depende en gran medida del tipo de diseño de página que hayamos seleccionado (Page Layout).

Si queremos que estén visibles u ocultos, lo más sencillo sería establecer el diseño de página en alguno que sí lo muestre, pero… como no somos conformistas, queremos usar el diseño que queramos y que lo muestre/oculte siempre.

 

Solución

Pues bien, la solución es muy sencilla. Sólo hay que añadir algo de CSS donde queramos o necesitemos. En mi caso, tengo una hoja de estilos asociada a la MasterPage que se ejecuta después de CoreV4.css para poder sobreescribir sus reglas.

<SharePoint:CssRegistration Name="/Style Library/MyProject/Css/mystyle.css" After="corev4.css" runat="server"/>

Lo que tendremos que añadir es lo siguiente:

  • Mostrar siempre los elementos “Ver todo el contenido del sitio” y “Papelera de reciclaje”
.s4-specialNavLinkList  {
    display:block !important;
}

  • Ocultar siempre los elementos “Ver todo el contenido del sitio” y “Papelera de reciclaje”
.s4-specialNavLinkList  {
    display:none !important;
}

 

Windows Phone: Controlar el cambio de tema del móvil en nuestras aplicaciones

Uno de los aspectos más complejos de controlar cuando diseñamos/desarrollamos una aplicación para Windows Phone es el cambio de tema del móvil. Por defecto, el emulador aparece con el tema “Oscuro” o “Dark”, con lo que, los textos salen en color blanco. Si lo cambiamos al tema “Claro” o “Light”, los colores se invierten.

Dado que lo que se busca al desarrollar una aplicación para Windows Phone es conseguir muchas descargas, es importante darle un aspecto agradable, con colores, fondo de pantalla,…

Ahora bien, en mi caso, he sufrido el mismo problema con 3 aplicaciones diferentes.

 

El problema “pequeño” surge con que la imagen de fondo de las tres aplicaciones es oscuro (el de boplace es gradual de claro a oscuro) y, al cambiar el tema del móvil a “Claro”… ¡¡¡tacháaannn!!! El color de los textos se cambia a oscuro y no se puede leer nada y Microsoft no certificará nuestra aplicación para el MarketPlace (creedme que ni aunque sea uno sólo de los textos).

La solución “larga” y “tediosa” es asignar siempre un color a cada uno de los textos que hay en las aplicaciones, con lo que… si no lo vamos haciendo a medida que desarrollamos la aplicación, tendremos un largo de aburrimiento buscando, cambiando textos y probando de nuevo.

Hasta ahí, es asumible y se aprende a controlar el color siempre que se pone un control de texto o un estilo, pero… ahora llega el gran problema que me ha surgido en una nueva aplicación: ¿Qué ocurre cuando tenemos un campos de introducción de datos como TextBox? Pues ocurre que se hace muy muy complicado controlarlo y que tendremos que buscar alguien que le haya dado solución al problema y…. violà, encontré una solución en este artículo de WindowsPhoneGeek en el que se aplica un estilo a los TextBox para mantener siempre el mismo modo de visualización.

¿Qué os ha parecido? ¿Una buena solución y todo perfecto? Pues esto no ha acabado… Tengo un DatePicker y unos cuántos DigitLoopingSelector del Toolkit que siguen sin verse con el tema claro del móvil  Sad smile 

Mi primera reacción fue buscar de nuevo algún artículo en el que se resuelva el problema para alguno de estos controles pero, tras mucho buscar no encontré nada y empecé a barajar quitarle el fondo sólo a estas páginas o crear un fondo de color inverso, es decir, el mismo fondo pero en colores claros y controlar en código cuál de los dos mostrar en función del tema del móvil. Pero justo cuando ya no me quedaban esperanzas encontré la solución de todas las soluciones, que hacía innecesario el estilo aplicado al TextBox de antes y que nos hará felices a todos. Jeff Wilcox ha creado una solución que se puede encontrar en Nuget con la que podemos forzar a nuestra aplicación para que mantenga el tema que nosotros quedamos Oscuro o Claro, con lo que no tendremos que preocuparnos de si algún control de texto se nos ha quedado sin asignar color o si hemos añadido un control del toolkit y no podemos controlar su visualización en algún tema,… El ejemplo lo podéis encontrar aquí

Windows Phone: ListBoxItem con Button, obtener Item y Contexto.

Hoy me he encontrado un caso “complejo” cuya solución como casi siempre es muy sencilla.

Escenario:

Tenemos una lista “ListBox” en la que establecemos una plantilla “DataTemplate” para los elementos de la lista “ListBoxItem”. Dentro de la plantilla ponemos un botón que queremos que realice una acción diferente a la que realice la selección de un elemento de la lista.

En este caso, seleccionar un elemento de la lista realizará la acción de centrarnos en el punto correspondiente en el mapa y el botón con la flecha, nos debe enviar a otra página pasando los datos del elemento de la lista.

Estos datos están enlazados mediante el ItemSource de la lista a una colección del tipo “List<TipoEnlazado>”, así que… en el caso de la selección de un elemento de la lista es fácil, sólo hay que obtener el contexto y hacer un cast al Tipo que se enlazó. En el caso del botón… PROBLEMA

private void Pushpin_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
    var pushpin = sender as Pushpin;
    TipoEnlazado variable = (TipoEnlazado)pushpin.DataContext;
    // TODO: Aquí pondremos nuestro código
}

 

Problema:

Por lo que parece, desde el botón no podemos sacar el contexto (tan fácilmente) como podríamos hacer del elemento de lista “ListBoxItem”, así que… me tocó investigar y usar ese “gran y veloz” método de… prueba – error – traza.

 

Solución:

Bueno, como podréis observar, lo que tendremos que hacer es convertir el “sender” al tipo de control “Button” y, de ahí, sacar el contexto que tendremos que convertir en nuestro tipo enlazado. Con esto… problema resuelto, ya podemos realizar nuestras operaciones con los datos del elemento y enviarlos a la siguiente ventana.

 

private void btnPlace_Click(object sender, System.Windows.RoutedEventArgs e)
{
    TipoEnlazado item = (sender as Button).DataContext as TipoEnlazado;
    ListBoxItem pressedItem = this.lstSearchResult.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;

    //TODO: Aquí pondremos nuestro código
}

 

Además, como se puede observar entre todas mis pruebas conseguí encontrar un medio de saber a qué elemento de la lista pertenece el botón pulsado gracias a lstSearchResult.ItemContainerGenerator.ContainerFromItem(item)

Bueno, esto es todo. Espero que le sirva a alguien.

SharePoint 2010: Crear un sitio programáticamente

Escenario:

Supongamos que queremos crear sitios en SharePoint de manera programática, por ejemplo con un web part. En este caso, fue resuelto con un web part en el que se selecciona una plantilla (puede ser personalizada o de las predefinidas) y se establece un nombre de sitio.

 

Solución:

En primer lugar habrá que obtener la lista de plantillas tal y como describo en este otro artículo. De esta forma, con la plantilla seleccionada, un nombre y una descripción procederemos a crear el sitio.

Seguidamente, habrá que añadir el código al evento click del botón “Crear Proyecto”, donde tendremos que recoger los datos aportados en el Web Part para poder generar el sitio correspondiente.

Evento

protected void btnProjectCreate_Click(object sender, EventArgs e)
{
    try
    {
        CreateSite(txtProjectName.Text, txtProjectDesc.Text, "");
    }
    catch (Exception ex)
    {
        lblMessage.Text += ex.Message;
    }
}
 

En este caso, desde el evento únicamente realizo la llamada a un método que se encarga de crear el sitio.

 

Métodos

       public void CreateSite(string siteTitle, string description, string startDate)
        {
            string siteName = CreateShortSiteName(siteTitle);
 
            // Create SharePoint Customer Site
            using (SPWeb parentWeb = SPContext.Current.Web)
            {
                SPWebTemplateCollection templates = parentWeb.GetAvailableWebTemplates(LocaleId);
                SPWebTemplate siteTemplate = templates[cmbTemplates.SelectedValue];
 
                // If site with same name already exists, quit out
                if (!parentWeb.Webs.Names.Contains(siteName))
                {
                    // Create new site
                    SPWeb site = parentWeb.Webs.Add(siteName, siteTitle, description, siteTemplate.Lcid, siteTemplate, false, false);
                    lblMessage.Text = "Creado el sitio " + siteTitle + " en la dirección <a href="" + site.Url + "">" + site.Url + "</a>";
                    site.Navigation.UseShared = true;
                    parentWeb.Navigation.TopNavigationBar.AddAsLast(new SPNavigationNode(siteTitle, site.ServerRelativeUrl));
                    site.Update();
                }
            }
        }
 
        private string CreateShortSiteName(string siteTitle)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char ch in siteTitle)
                if (char.IsLetterOrDigit(ch))
                    sb.Append(ch);
            return sb.ToString();
        }

 

Explicación:

  1. En primer lugar, establecemos un nombre válido para la URL del sitio mediante la función CreateShortSiteName y, en este caso, he elegido quedarme sólo con las letras y los números.
  2. Instanciamos el sitio padre donde alojaremos este nuevo sitio
  3. Obtenemos la lista de plantillas disponibles
  4. Obtenemos la plantilla seleccionada en el Web Part
  5. Comprobamos que no exista un sitio con el mismo nombre
  6. Añadimos el nuevo sitio a la colección de sitios del sitio padre con el nombre para la URL, el títuloy la descripción especificados en el Web Part, el lenguaje de la plantilla y la plantilla seleccionada.
  7. Establecemos un texto para mostrar que el sitio fue creado y la url de acceso al mismo
  8. Añadimos el sitio en el último lugar de la barra de navegación superior, tal y como describo en este artículo
  9. Actualizamos el sitio.

 

Resumen:

Con esto, habremos creado el sitio con el nombre, la descripción y la plantilla seleccionados en el Web Part.