Silverlight MVVM Confusion: State Based Image Update

I am developing a Silverlight application and I am trying to adhere to the principles of MVVM, but I am having problems changing the image source based on the state of the property in the ViewModel. In every sense and purpose, you can think of the functionality that I implement as a play / pause button for an audio application. When in the "Play" mode, IsActive is true in the ViewModel, and the "Pause.png" image on the button should be displayed. When paused, IsActive is false in the ViewModel, and "Play.png" is displayed on the button. Naturally, there are two additional images for processing when the mouse hangs over the button.

I thought I could use Style Trigger , but apparently they are not supported in Silverlight. I was looking at a forum post with a question similar to mine, where he suggested using VisualStateManager . Although this may help in changing the image for freezes / normal states, the missing part (or I don’t understand) is how it will work with the state of the state through the view model. The message seems to apply only to events, not to the properties of the view model. Having said that, I was also unable to successfully complete normal / freezing attacks.

Below is my Silverlight 4 XAML. It should also be noted that I work with MVVM Light.

<UserControl x:Class="Foo.Bar.MyUserControl" 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="Button"> <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="Button"> <Grid> <Image Source="/Foo.Bar;component/Resources/Icons/Bar/Play.png"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="Active"> <VisualState x:Name="MouseOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Source" Storyboard.TargetName="/Foo.Bar;component/Resources/Icons/Bar/Play_Hover.png" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Image> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <Button Style="{StaticResource MyButtonStyle}" Command="{Binding ChangeStatus}" Height="30" Width="30" /> </Grid> </UserControl> 

What is the correct way to update images on buttons with a state determined by the view model?

+1
source share
3 answers

On the advice of a colleague, and since I already used MVVM Light , I was able to use EventToCommand to handle mouse and mouse input events in the view model, rather than relying on the built-in VisualStateManager to handle these events. I also changed my button to ToggleButton. This allowed me to use checked and unchecked states to handle the display of the play or pause buttons. Since the state was controlled by the view model, I was then able to determine which image to display by binding the ToggleButton Visibility attribute to a property in the view model that checked the state. Updated XAML is as follows:

 <UserControl x:Class="Foo.Bar.MyControl" 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" xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4" mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="200"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseEnter"> <cmd:EventToCommand Command="{Binding MouseEnterCommand}" PassEventArgsToCommand="True"/> </i:EventTrigger> <i:EventTrigger EventName="MouseLeave"> <cmd:EventToCommand Command="{Binding MouseLeaveCommand}" PassEventArgsToCommand="True"/> </i:EventTrigger> </i:Interaction.Triggers> <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="{Binding PlayHoverVisible,FallbackValue=Collapsed}" /> <Image x:Name="PauseHover" Source="/Foo.Bar;component/Resources/Icons/Bar/Pause_Hover.png" Visibility="{Binding PauseHoverVisible,FallbackValue=Collapsed}" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <ToggleButton Style="{StaticResource MyButtonStyle}" IsChecked="{Binding IsPlaying}" Command="{Binding ChangeStatus}" Height="30" Width="30" /> </Grid> </UserControl> 
+1
source

An easy way is to bind the boolean properties of IsActive and IsNotActive on your virtual machine to visibility on two image controls inside the content of your button.

Of course, you will need to use the BooleanToVisiblityConverter.

Second thought: Could you associate IsActive with IsEnabled on your button and make the style show the correct image. Not sure if the restriction specified in Silverlight can prevent this.

+1
source

We have some customizable converters that modify logical (and other types) specific images. Thus, we keep the view / model as separate as possible.

Converters are easy to write, with lots of examples on the Internet.

So in xaml, this happens something like this:

 <Image Source={Binding IsActive, Converter={StaticResource "boolToPlayImageConverter"}}/> 
+1
source

Source: https://habr.com/ru/post/1340368/


All Articles