суббота, 2 февраля 2013 г.

Windows 8 FlipControl


Стандартный элемент управления FlipControl (от слова flip - переворачивать) имеет переднюю и заднюю часть для содержания контента и может переворачиваться от одного вида к другому. Как правило реализация подобного элемента управления сводится к созданию 2-х свойств зависимостей: FronContent и BackContent. И соответственно в качестве контента устанавливается нужный вам элемент. Я же решил пойти дальше и не ограничивать себя количеством элементов, которые может содержать FlipControl. Вот что получилось:


Реализация моего элемента управления довольно проста. FlipControl я унаследовал от ItemsControl и добавил несколько свойств зависимостей:

FlipControl.cs:
#region DependencyProperty Members

/// <summary>
/// Using a DependencyProperty as the backing store for front content.
/// </summary>
public static readonly DependencyProperty FirstContentProperty = DependencyProperty.Register("FirstContent", typeof(Object), typeof(FlipControl), new PropertyMetadata(null));

/// <summary>
/// 
/// </summary>
public Object FirstContent
{
    get { return GetValue(FirstContentProperty); }
    private set { SetValue(FirstContentProperty, value); }
}

/// <summary>
/// Using a DependencyProperty as the backing store for back content.
/// </summary>
public static readonly DependencyProperty SecondContentProperty = DependencyProperty.Register("SecondContent", typeof(Object), typeof(FlipControl), new PropertyMetadata(null));

/// <summary>
/// 
/// </summary>
public Object SecondContent
{
    get { return GetValue(SecondContentProperty); }
    private set { SetValue(SecondContentProperty, value); }
}

/// <summary>
/// Using a DependencyProperty as the current index of content.
/// </summary>
public static readonly DependencyProperty CurrentIndexProperty = DependencyProperty.Register("CurrentIndex", typeof(Int32), typeof(FlipControl), new PropertyMetadata(0, CurrentIndexChangedCallback));

/// <summary>
/// 
/// </summary>
public Int32 CurrentIndex
{
    get { return (Int32)GetValue(CurrentIndexProperty); }
    set { SetValue(CurrentIndexProperty, value); }
}

/// <summary>
/// 
/// </summary>
/// <param name="target"></param>
/// <param name="args"></param>
private static void CurrentIndexChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
    var flipControl = target as FlipControl;
    if (flipControl == null) return;
    flipControl.FlipTo((Int32)args.NewValue);
}

#endregion

Так же как и везде - FirstContent, SecondContent (оба свойства только для чтения) + CurrentIndex (для хранения индекса текущего элемента из коллекции ItemsSource). И как видно при изменении индекса элемент управления "переворачивается" на нужный контент:

FlipControl.cs:
#region Private Members

/// <summary>
/// 
/// </summary>
/// <param name="index"></param>
private void FlipTo(Int32 index)
{
    if (CurrentIndex == oldIndex) return;
    if (index < 0 || index >= Items.Count) return;
    oldIndex = CurrentIndex;
    if (flag)
    {
        flag = false;
        SecondContent = Items[CurrentIndex];
        VisualStateManager.GoToState(this, "FirstState", true);
    }
    else
    {
        flag = true;
        FirstContent = Items[CurrentIndex];
        VisualStateManager.GoToState(this, "SecondState", true);
    }
}

#endregion

Осталось теперь только глянуть на XAML-разметку и убедиться, что VisualStateManager имеет нужные VisualState:

Theme.xaml:
<Style TargetType="local:FlipControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:FlipControl">
                <Grid x:Name="PART_Root">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="VisualStateGroup">
                            <VisualState x:Name="DefaultState">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="GridFront">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="GridBack">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="FirstState">
                                <Storyboard >
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="GridBack">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="GridBack">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="GridBack">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.2">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="GridFront">
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="GridFront">
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="GridFront">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.2">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="SecondState">
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="GridBack">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="0"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="GridBack">
                                        <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="GridBack">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.2">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="GridFront">
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="GridFront">
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="GridFront">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.2">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid x:Name="GridFront" RenderTransformOrigin="0.5,0.5">
                        <Grid.RenderTransform>
                            <CompositeTransform/>
                        </Grid.RenderTransform>
                        <ContentPresenter x:Name="PART_Front" Content="{TemplateBinding FirstContent}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                    </Grid>
                    <Grid x:Name="GridBack" RenderTransformOrigin="0.5,0.5">
                        <Grid.RenderTransform>
                            <CompositeTransform/>
                        </Grid.RenderTransform>
                        <ContentPresenter x:Name="PART_Back" Content="{TemplateBinding SecondContent}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

А в качестве идей для развития можно реализовать различные эффекты при "переворачивании" с контента на контент.

Комментариев нет:

Отправить комментарий