1

In a UWP application with MVVM Light I'm trying to bind the current state of a VisualStateGroup to a property in the ViewModel. The idea is to change the current state by changing the value of a property in the view model according to the application logic.

This problem has been already addressed in this question several years ago. I'm interested in the approach suggested by Olav (second answer), which cannot be directly followed because my application is a UWP app.

Here comes my xaml:

<Page
    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:Interactivity="using:Microsoft.Xaml.Interactivity" 
    xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
    x:Class="Smallrobots.Ev3RemoteController.Views.Settings"
    mc:Ignorable="d"

    DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">

    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ConnectionStatus">
                <Interactivity:Interaction.Behaviors>
                    <Core:DataTriggerBehavior Binding="{Binding VsmConnectionStatus}" 
                                              Value="{Binding VsmConnectionStatus}">
                        <Core:GoToStateAction StateName="{Binding VsmConnectionStatus}"/>
                    </Core:DataTriggerBehavior>
                </Interactivity:Interaction.Behaviors>
                <VisualState x:Name="Connected">
                    <VisualState.Setters>
                        <Setter Target="button.(Control.IsEnabled)" Value="False"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Button x:Name="button" Content="Connect"/>
    </Grid>
</Page>

and the MainViewModel:

public class MainViewModel : ViewModelBase
{
    private string vsmConnectionStatus = "";
    public string VsmConnectionStatus
    {
        get => vsmConnectionStatus;
        set
        {
            if (!vsmConnectionStatus.Equals(value))
            {
                vsmConnectionStatus = value;
                RaisePropertyChanged("VsmConnectionStatus");
            }
        }
    }

    public MainViewModel()
    {
        VsmConnectionStatus = "Connected";
    }
}

However with this code, after the constructor has been executed, the current state remains null and further assignments have no effect. It could be the use of the DataTriggerBehavior or of the GoToStateAction.

What is wrong? Many thanks!

EDIT: I added some more code to the xaml, to better clarify the question.

1 Answers1

3

However with this code, after the constructor has been executed, the current state remains null and further assignments have no effect. It could be the use of the DataTriggerBehavior or of the GoToStateAction.

Since I have not saw your whole code, I'm not sure if there're some issues in your other code blocks. But checking your above code, you would need to do some changes. You need to move the Interactivity:Interaction.Behaviors out of the VisualStateGroup like the following:

<Grid>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="ConnectionStatus">
            <VisualState x:Name="Connected">
                <VisualState.Setters>
                    <Setter Target="button.(Control.IsEnabled)" Value="False"/>
                </VisualState.Setters>
            </VisualState>
            <VisualState x:Name="NotConnected">
                <VisualState.Setters>
                    <Setter Target="button.(Control.IsEnabled)" Value="True"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <Interactivity:Interaction.Behaviors>
        <Core:DataTriggerBehavior Binding="{Binding VsmConnectionStatus}" 
                                      Value="{Binding VsmConnectionStatus}">
            <Core:GoToStateAction StateName="{Binding VsmConnectionStatus}"/>
        </Core:DataTriggerBehavior>
    </Interactivity:Interaction.Behaviors>
    <Button Content="{Binding VsmConnectionStatus}" x:Name="button">
    </Button>
</Grid>

Second, if you want to use Binding, please make sure that you have sepcified the 'DataContext' for your page class.

public MainPage()
{
    this.InitializeComponent();
    this.DataContext = new MainViewModel();
}

Then, according to my test, I found that the 'DataTriggerBehavior' will not be triggered until the bound value has changed. I do not know if it's a default behavior, there's a same issue on GitHub.

So, you could change the 'VsmConnectionStatus' value in page's loaded event instead of doing it in viewmodel's constructor method.

For example,

public MainPage()
{
    this.InitializeComponent();
    this.DataContext = new MainViewModel();
    this.Loaded += MainPage_Loaded;
}

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    MainViewModel mvm = this.DataContext as MainViewModel;
    mvm.VsmConnectionStatus = "Connected";
}
public class MainViewModel : ViewModelBase
{
    private string vsmConnectionStatus = "";
    public string VsmConnectionStatus
    {
        get => vsmConnectionStatus;
        set
        {
            vsmConnectionStatus = value;
            RaisePropertyChanged("VsmConnectionStatus");
        }
    }

    public MainViewModel()
    {}
}
Xie Steven
  • 8,544
  • 1
  • 9
  • 23
  • Thank you Xavier! Moving `Interactivity:Interaction.Behaviors` out of the `VisualStateGroup` block and more importantly setting the `VsmConnectionStatus` property out of the ViewModel constructor solve the issue. – Oreste Riccardo Natale Jan 17 '18 at 09:15