Search code examples
c#wpfvisualstatemanager

Image not change by using visual states


Why do my visual states not work when the dependency property Message is NULL or filled ?

WPF code:

<Window x:Class="VisualStateTest.MainWindow"
        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:local="clr-namespace:VisualStateTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Border x:Name="border" Background="Black">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="LocationErrorStatusVisualStateGroup">
                <VisualState x:Name="DefaultVisualState">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Image.Source)" Storyboard.TargetName="myImage">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <BitmapImage UriSource="/VisualStateTest;component/Resources/Empty.png"/>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="MessageVisualState">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Image.Source)" Storyboard.TargetName="myImage">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <BitmapImage UriSource="/VisualStateTest;component/Resources/Message.png"/>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Image x:Name="myImage"/>
    </Border>
</Window>

C# code behind:

using System.Windows;

namespace VisualStateTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Message

        /// <summary>
        /// Message Dependency Property
        /// </summary>
        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(MainWindow),
                new FrameworkPropertyMetadata(null,
                    new PropertyChangedCallback(OnMessageChanged)));

        /// <summary>
        /// Gets or sets the Message property. This dependency property 
        /// indicates the message.
        /// </summary>
        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }

        /// <summary>
        /// Handles changes to the Message property.
        /// </summary>
        private static void OnMessageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MainWindow target = (MainWindow)d;
            string oldMessage = (string)e.OldValue;
            string newMessage = target.Message;
            target.OnMessageChanged(oldMessage, newMessage);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the Message property.
        /// </summary>
        protected virtual void OnMessageChanged(string oldMessage, string newMessage)
        {
            if (newMessage == oldMessage)
                return;

            if (string.IsNullOrEmpty(newMessage))
            {
                VisualStateManager.GoToState(this, "DefaultVisualState", true);
            }
            else
            {
                VisualStateManager.GoToState(this, "MessageVisualState", true);
            }
        }

        #endregion

        public MainWindow()
        {
            Message = "test";
            InitializeComponent();
        }
    }
}

Solution

  • The build action of the images should be set to Resource. The images should then be referenced using Pack URIs which would be

    pack://application:,,,/Resources/Empty.png
    pack://application:,,,/Resources/Message.png
    

    There are two ways to use the VisualStateManager. The visual states can be defined:

    • either in the template of a control, or
    • directly in the control (as you are doing here)

    When the visual states are not defined within a template, you have to call the GoToElementState method rather than the GoToState method.

    Also, you have to call the GoToElementState method on the control that actually defines the corresponding visual states, so you need to call it on the Border and not on the MainWindow

    VisualStateManager.GoToElementState(border, "DefaultVisualState", true);
    

    See here for more details on the VisualStateManager