Editando Datos en un DataTemplate

En el anterior post vimos como cambiar el DataTemplate dinámicamente, en este vamos a ver como podemos hacer que el usuario pueda editar datos y modificarlos. Siguiendo con la aplicación de ejemplo imaginar que le ponemos un campo de rating de la persona y que el usuario pueda cambiar este rating, pulsando a un botón de editar.

 

image

De manera que cuando pulsemos editar podamos cambiar el Rating de la persona. El control de rating es muy sencillo, crearemos un control de usuario cuyo xaml seran las 5 estrellas

<UserControl x:Class="WPFDataTemplates.FiveStarRating"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <StackPanel Orientation="Horizontal">
            <Path x:Name="Star1" Margin="0,15,5,0" Stroke="Black" StrokeThickness="2"  StrokeLineJoin="Round" Data="M 0,0 l 10,0 l 5,-10 l 5,10 l 10,0 l -7,10 l 2,10 l -10,-5 l -10,5 l 2,-10 Z" />
            <Path x:Name="Star2" Margin="0,15,5,0" Stroke="Black" StrokeThickness="2"  StrokeLineJoin="Round" Data="M 0,0 l 10,0 l 5,-10 l 5,10 l 10,0 l -7,10 l 2,10 l -10,-5 l -10,5 l 2,-10 Z" />
            <Path x:Name="Star3" Margin="0,15,5,0" Stroke="Black" StrokeThickness="2"  StrokeLineJoin="Round" Data="M 0,0 l 10,0 l 5,-10 l 5,10 l 10,0 l -7,10 l 2,10 l -10,-5 l -10,5 l 2,-10 Z" />
            <Path x:Name="Star4" Margin="0,15,5,0" Stroke="Black" StrokeThickness="2"  StrokeLineJoin="Round" Data="M 0,0 l 10,0 l 5,-10 l 5,10 l 10,0 l -7,10 l 2,10 l -10,-5 l -10,5 l 2,-10 Z" />
            <Path x:Name="Star5" Margin="0,15,5,0" Stroke="Black" StrokeThickness="2"  StrokeLineJoin="Round" Data="M 0,0 l 10,0 l 5,-10 l 5,10 l 10,0 l -7,10 l 2,10 l -10,-5 l -10,5 l 2,-10 Z" />
        </StackPanel>
    </Canvas>

</UserControl>

 

Crearemos una DependencyProperty donde le pasaremos el rating y rellenara las estrellas dependiendo del valor

 

  public partial class FiveStarRating : UserControl
    {
        public int Rating
        {
            get { return (int)this.GetValue(RatingProperty); }
            set
            {
                if (value > 5)
                    value = 5;
                if (value < 0)
                    value = 0;

                this.SetValue(RatingProperty, value);
            }
        }

        public static readonly DependencyProperty RatingProperty = DependencyProperty.Register("Rating",
                    typeof(int), typeof(FiveStarRating),
                    new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(RatingValueChanged)
                    ));

        public FiveStarRating()
        {
            InitializeComponent();


        }

        private static void RatingValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {

            FiveStarRating x = sender as FiveStarRating;
            int ratingValue = (int)e.NewValue;

            for (int i = 1; i <= 5; i++)
            {
                Path clearStar = (Path)x.FindName("Star" + i);
                clearStar.Fill = new SolidColorBrush(Colors.Gray);
            }
            for (int i = 1; i <= ratingValue; i++)
            {

                Path starFilled = (Path)x.FindName("Star" + i);
                starFilled.Fill = new SolidColorBrush(Colors.Goldenrod);

            }

        }
    }

 

 

Una vez que tenemos el control añadimos al objeto persona la propiedad de rating

 private int _imageRating;
        public int ImageRating
        {
            get { return _imageRating; }
            set { _imageRating = value; }
        }

Creamos un converter para el Binding del rating ya que para mostrar el rating vamos a utilizar un label en el cual enlazaremos la propiedad Content del label con la propiedad ImageRating de la persona y a través del converter devolveremos el control de usuario de rating creado anteriormente. Tranquilos viendo el código se entiende.

El DataTemplate quedaria

 

 <DataTemplate x:Key="TemplatePerson">
        <DataTemplate.Resources>
            <LinearGradientBrush x:Key="backBrush" StartPoint="0,0.5" EndPoint="1,0.5">
                <GradientStop Color="#1100CC22" Offset="0" />
                <GradientStop Color="#8800CC22" Offset="0.97" />
                <GradientStop Color="#AA10FF18" Offset="0.999" />
                <GradientStop Color="#44FFFFFF" Offset="1" />
            </LinearGradientBrush>
            <local:PersonImageConverter x:Key="imageConverter" />
        </DataTemplate.Resources>

        <Border x:Name="personsBorder" Style="{StaticResource PersonBorderStyle}" Width="450">
            <Grid>
                <StackPanel Orientation="Horizontal">

                    <Image Width="40" Height="40" 
                           Source="{Binding Path=ImageRef, 
                        Converter={StaticResource imageConverter}}">
                        <Image.BitmapEffect>
                            <DropShadowBitmapEffect />
                        </Image.BitmapEffect>

                    </Image>

                    <TextBlock x:Name="personName" 
                               Text="{Binding Name}"  
                               Padding="15,15"
                               Foreground="Black" />
                    <Label Content="{Binding Converter={StaticResource RatingConverter}}" Width="200" />
                    <Button Height="30"  Command="local:MyCommands.ChangeRating" Content="Editar"
                                    CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
                       
                    </Button>


                </StackPanel>
            </Grid>
        </Border>
    </DataTemplate>

Si nos fijamos en las nuevas líneas

 

<Label Content="{Binding Converter={StaticResource RatingConverter}}" Width="200" />

<Button Height="30" Command="local:MyCommands.ChangeRating" Content="Editar" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">

Observamos el Converter en el binding sobre el content del label , este converter es muy sencillo

public class RatingConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //in order to handle design time problems, handle null value case
            if (value == null)
                return new FiveStarRating();

            Person obj = (Person)value;

            FiveStarRating uc = new FiveStarRating();
            uc.Rating = (int)obj.ImageRating;

            return uc;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            FiveStarRating obj = (FiveStarRating)value;
            return obj.Rating;
        }

        #endregion
    }

La segunda línea importante es el Botón de edición al cual en vez de utilizar el evento Click, no podríamos porque estamos en un fichero de recursos, utilizamos un comando en este caso MyCommandsChangeRating

public class MyCommands
    {
        public static RoutedCommand ChangeRating =
     new RoutedCommand("ChangeRating", typeof(WpfWindow));

    }

En la ventana creamos el binding al comando

 <Window.CommandBindings>
    <CommandBinding Command="local:MyCommands.ChangeRating" Executed="OnChangeRating" />
    </Window.CommandBindings>

 

De manera que cuando se pulse un botón se ejecute el método OnChangeRating con el parámetro del item de la lista donde esta el botón y cambiaremos el DataTemplate para que el usuario pueda meter el Rating

 

private void OnChangeRating(object sender, ExecutedRoutedEventArgs e)
        {

            ListBoxItem itm = (ListBoxItem)e.Parameter;
            itm.ContentTemplate = this.FindResource("TemplatePersonEdit") as DataTemplate;
        }

 

El DataTemplate en este caso

 

<DataTemplate x:Key="TemplatePersonEdit">
        <DataTemplate.Resources>
            <LinearGradientBrush x:Key="backBrush" StartPoint="0,0.5" EndPoint="1,0.5">
                <GradientStop Color="#1100CC22" Offset="0" />
                <GradientStop Color="#8800CC22" Offset="0.97" />
                <GradientStop Color="#AA10FF18" Offset="0.999" />
                <GradientStop Color="#44FFFFFF" Offset="1" />
            </LinearGradientBrush>
            <local:PersonImageConverter x:Key="imageConverter" />
        </DataTemplate.Resources>

        <Border x:Name="personsBorder" Style="{StaticResource PersonBorderStyle}" BorderThickness="8" CornerRadius="5" BorderBrush="#FF201F1F" Width="450">
            <Grid>
                <StackPanel Orientation="Horizontal">

                    <Image Width="60" Height="60" 
                           Source="{Binding Path=ImageRef, 
                        Converter={StaticResource imageConverter}}">
                        <Image.BitmapEffect>
                            <DropShadowBitmapEffect />
                        </Image.BitmapEffect>

                    </Image>

                    <TextBlock x:Name="personName" 
                               Text="{Binding Name}"  
                               Padding="15,15"
                               Foreground="Black" FontSize="20" />
                    <TextBox  Height="30"   FontSize="20" Foreground="Blue" Text="{Binding Path=ImageRating}" HorizontalAlignment="Left" />

                    <Button Height="30"  Margin="10,0,0,0" Command="local:MyCommands.SaveRating" Content="Salvar"
                                    CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
                    </Button>

                </StackPanel>
            </Grid>
        </Border>
    </DataTemplate>

 

Quedándonos

 

image

 

En el botón salvar haríamos los mismos pasos que con el de editar, crear un comando, enlazarlo….

 

El código lo podéis bajar, por cierto tiene un comportamiento extraño cuando….?

 

Animo que queda poco para las vacaciones!!!!!!!

 

Un comentario sobre “Editando Datos en un DataTemplate”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *