Элемент управления ToggleButton VisualStateManager: обработка нескольких состояний наведения - PullRequest
4 голосов
/ 18 февраля 2011

По поводу предыдущего вопроса ( Silverlight MVVM Confusion: Обновление изображения на основе состояния ) я начал с нового подхода. Я оставил существующий вопрос, потому что не хотел точно сказать, что мой новый подход был правильным (я все еще приветствую комментарии по моей первоначальной проблеме).

Если вы читаете мой предыдущий вопрос, пожалуйста, не стесняйтесь пропустить этот абзац: я пытаюсь создать элемент управления, который обеспечивает функциональность, аналогичную кнопке воспроизведения звука. Когда приложение находится в режиме «Play», оно должно отображать изображение «Pause.png». Когда он приостановлен, он должен отобразить изображение «Play.png». Есть также два дополнительных изображения для учета любого состояния, когда пользователь наводит курсор на элемент управления (например, «Play_Hover.png» и «Pause_Hover.png»). Состояние определяется свойством IsPlaying в моей модели представления.

Я решил использовать ToggleButton, чтобы определить, какое изображение отображать на основе текущего состояния. Помните, что когда IsPlaying имеет значение false, отображается кнопка воспроизведения, а когда это правда, отображается кнопка паузы. Таким образом, я придумал следующий XAML. Работает кроме парения:

<UserControl x:Class="Foo.BarMyControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    mc:Ignorable="d"
    d:DesignHeight="100" d:DesignWidth="200">
    <UserControl.Resources>
        <Style x:Key="MyButtonStyle" TargetType="ToggleButton">
            <Setter Property="IsEnabled" Value="true"/>
            <Setter Property="IsTabStop" Value="true"/>
            <Setter Property="Background" Value="#FFA9A9A9"/>
            <Setter Property="Foreground" Value="#FF000000"/>
            <Setter Property="MinWidth" Value="5"/>
            <Setter Property="MinHeight" Value="5"/>
            <Setter Property="Margin" Value="0"/>
            <Setter Property="HorizontalAlignment" Value="Left" />
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Top" />
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CheckStates">
                                    <VisualState x:Name="Checked">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Pause">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Play">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unchecked">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Play">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Pause">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Indeterminate" />
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Image x:Name="Play" Source="/Foo.Bar;component/Resources/Icons/Bar/Play.png" />
                            <Image x:Name="Pause" Source="/Foo.Bar;component/Resources/Icons/Bar/Pause.png" Visibility="Collapsed" />
                            <Image x:Name="PlayHover" Source="/Foo.Bar;component/Resources/Icons/Bar/Play_Hover.png" Visibility="Collapsed" />
                            <Image x:Name="PauseHover" Source="/Foo.Bar;component/Resources/Icons/Bar/Pause_Hover.png" Visibility="Collapsed" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <ToggleButton Style="{StaticResource MyButtonStyle}" IsChecked="{Binding LiveEnabled}" Command="{Binding ChangeStatus}" Height="30" Width="30" />
    </Grid>
</UserControl>

Как у вас разные изображения при наведении для обоих состояний? Если я добавлю состояние наведения в группу CommonStates, я смогу учитывать наведение только на одно из состояний (отмечено или не отмечено).

Ответы [ 3 ]

4 голосов
/ 18 февраля 2011

С помощью переключателя невозможно иметь другое состояние при наведении / наведении мыши, так как это является общим для кнопки. Общие состояния: Нормальное (то, что вы видите изначально), Наведение мыши, Нажатие и Отключение

Другие состояния, связанные с отмеченными, непроверенными или промежуточными. Здесь, как вы сделали, вы можете установить различные изображения и т. Д. Для различных состояний. При наведении курсора мыши всегда происходит возврат к общему состоянию.

Если вам нужна эта функция, вы можете создать свой собственный элемент управления для нее и обрабатывать анимацию при наведении курсора на основе активного состояния. Для этого потребуется больше кода на стороне сервера, так как вам нужно будет переопределить класс кнопки для этого объекта и вставить тестирование для различных состояний, чтобы разрешить воспроизведение анимации для каждого состояния. Это может быть сделано, я просто не знаю, стоило ли это так много усилий.

3 голосов
/ 15 февраля 2012

Вы также можете достичь этого без пользовательского контроля, например:

        <ToggleButton  x:Name="PlayButton"
                       IsChecked="{Binding Path=IsPlayChecked, Mode=TwoWay}"
                         Width="30" Height="30"
                        Style="{StaticResource ToggleButtonStyle}">
        <ToggleButton.Content>
            <Grid Margin="3">
                <Grid Visibility="{Binding ElementName=PlayButton, Path=IsChecked, Converter={StaticResource visConverter}, ConverterParameter=Invert}" Margin="0">
                    <Image Source="../Resources/play_normal.png"></Image>
                    <Image Source="../Resources/play_hover.png" Visibility="{Binding ElementName=PlayButton, Path=IsMouseOver, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/play_hit.png" Visibility="{Binding ElementName=PlayButton, Path=IsPressed, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/play_disabled.png"  Visibility="{Binding ElementName=PlayButton, Path=IsEnabled, Converter={StaticResource visConverter}, ConverterParameter=Invert}"></Image>
                </Grid>
                <Grid Visibility="{Binding ElementName=PlayButton, Path=IsChecked, Converter={StaticResource visConverter}}">
                    <Image Source="../Resources/pause_normal.png"></Image>
                    <Image Source="../Resources/pause_hover.png" Visibility="{Binding ElementName=PlayButton, Path=IsMouseOver, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/pause_hit.png" Visibility="{Binding ElementName=PlayButton, Path=IsPressed, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/pause_disabled.png"  Visibility="{Binding ElementName=PlayButton, Path=IsEnabled, Converter={StaticResource visConverter}, ConverterParameter=Invert}"></Image>
                </Grid>
            </Grid>
        </ToggleButton.Content>
    </ToggleButton>

Где:

<Converters:BooleanToVisibilityConverter x:Key="visConverter"/>

И определено BooleanToVisibilityConverter как это:

public class BooleanToVisibilityConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var flag = (bool)value;
        if (parameter != null)
            flag = !flag;
        return (flag ? Visibility.Visible : Visibility.Collapsed);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
    }
}

Надеюсь, это поможет!

1 голос
/ 11 сентября 2017

Я знаю, что это очень старо.Но я только что столкнулся с той же проблемой, и я решил по-другому.(Обратите внимание, что решение работает для UWP, я не тестировал его на WPF или Silverlight)

  1. Создание нового шаблонного элемента управления на основе кнопки
  2. Копирование значения по умолчанию для кнопки-переключателястиль и шаблон из MSDN и вставьте его в Themes / Generic.xaml
  3. Добавьте ниже eventHandler для событий: PointerExited, PointerReleased, GotFocus, LostFocus, PointerEntered и LayoutUpdated.EventHandler вызовет ChangeVisualState метод
  4. Создать новый метод ChangeVisualState

    public void ChangeVisualState(bool pointerEnter = false)
    {
        string stateName = "Normal";
        if (isChecked)
        {
            if (pointerEnter)
            {
                stateName = "CheckedPointerOver";
            }
            else
            {
                stateName = "Checked";
            }
        }
        else
        {
            if (pointerEnter)
            {
                stateName = "PointerOver";
            }
            else
            {
                stateName = "Normal";
            }
        }
        VisualStateManager.GoToState(this, stateName, true);
    }
    

Окончательный код:

public sealed class MyToggleButton : Button
{
    bool isChecked = false;

    public MyToggleButton()
    {
        this.DefaultStyleKey = typeof(MyToggleButton);
        this.Tapped += MyToggleButton_Tapped;
        this.PointerExited += MyToggleButton_PointerExited;
        this.PointerReleased += MyToggleButton_PointerReleased;
        this.GotFocus += MyToggleButton_GotFocus;
        this.LostFocus += MyToggleButton_LostFocus;
        this.PointerEntered += MyToggleButton_PointerEntered;
        this.LayoutUpdated += ToggleButtonEx_LayoutUpdated;
    }

    public void ChangeVisualState(bool pointerEnter = false)
    {
        string stateName = "Normal";
        if (isChecked)
        {
            if (pointerEnter)
            {
                stateName = "CheckedPointerOver";
            }
            else
            {
                stateName = "Checked";
            }
        }
        else
        {
            if (pointerEnter)
            {
                stateName = "PointerOver";
            }
            else
            {
                stateName = "Normal";
            }
        }
        VisualStateManager.GoToState(this, stateName, true);
    }

    private void MyToggleButton_PointerEntered(object sender, PointerRoutedEventArgs e)
    {
        ChangeVisualState(true);
    }

    private void MyToggleButton_LostFocus(object sender, RoutedEventArgs e)
    {
        ChangeVisualState();
    }

    private void MyToggleButton_GotFocus(object sender, RoutedEventArgs e)
    {
        ChangeVisualState();
    }

    private void MyToggleButton_PointerReleased(object sender, PointerRoutedEventArgs e)
    {
        ChangeVisualState();
    }

    private void MyToggleButton_PointerExited(object sender, PointerRoutedEventArgs e)
    {
        ChangeVisualState();
    }

    private void MyToggleButton_LayoutUpdated(object sender, object e)
    {
        ChangeVisualState();
    }

    private void MyToggleButton_Tapped(object sender, TappedRoutedEventArgs e)
    {
        isChecked = !isChecked;
        ChangeVisualState(true);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...