Change VisualState of a VisualElement with Prism, in Xamarin.Forms

MattiaDurliMattiaDurli ITMember ✭✭

How can I change the VisualState of an element from the Prism ViewModel?
Is there a Bindable property I can use?

Thanks!

Tagged:

Answers

  • NMackayNMackay GBInsider, University mod
    edited January 15

    @MattiaDurli said:
    How can I change the VisualState of an element from the Prism ViewModel?
    Is there a Bindable property I can use?

    Thanks!

    There's lot's of approach but they aren't any different in Prism to a regular Forms app, Prism just serves but the bindings.

    Check out triggers

    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/triggers

                   <Grid RowSpacing="0" ColumnSpacing="0">
                                <Label  FontSize="{x:OnPlatform Android=16, UWP=16, iOS=14}"                               
                                       Text="{Binding .Contact.ContactKindName}"                             
                                       Style="{StaticResource LabelListviewBaseStyle}" Margin="16" InputTransparent="True" />
                                <Grid.Triggers>
                                    <DataTrigger TargetType="Grid" Binding="{Binding IsSelected}" Value="True">
                                        <Setter Property="BackgroundColor" Value="{StaticResource light_gray}" />
                                    </DataTrigger>
                                    <DataTrigger TargetType="Grid" Binding="{Binding IsSelected}" Value="False">
                                        <Setter Property="BackgroundColor" Value="{StaticResource list_no_Selection}" />
                                    </DataTrigger>
                                </Grid.Triggers>
    
    
  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    To add on to what @NMackay said...
    You shouldn't be dealing with Visual State from the ViewModel.
    ViewModels are supposed to be ignorant of the 0:n views that might be using them as their binding context. Remember that you could have 20 different views all binded back to this ViewModel. So how would you tell view #14 to make something visible, but not the others - from the VM?

    The View should be handling itself. That's its concern. The ViewModel deals strictly in logic and data.

  • MattiaDurliMattiaDurli ITMember ✭✭
    edited January 15

    To answer both @NMackay and @ClintStLaurent:

    Yes Triggers may be an alternative, but let's say I want to use the VisualStates on an Entry:

        <Entry
                            x:Name="UserNameEntry"                    
                            Placeholder="UserName">  
                 <VisualStateGroupList x:Name="CommonStates">
                            <VisualStateGroup>
                                <VisualState x:Name="Valid">
                                    <VisualState.Setters>
                                        <Setter Property="BackgroundColor" Value="White"></Setter>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Invalid">
                                    <VisualState.Setters>
                                        <Setter Property="BackgroundColor" Value="Red"></Setter>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateGroupList>                                   
        </Entry>
    

    I agree that from the ViewModel I shouldn't access the VisualElement directly, so I won't call:

    VisualStateManager.GoToState(UserNameEntry, "Invalid");

    But in my ViewModel I want the logic to decide if the data I entered is a valid UserName, and change the VisualState accordingly.

    So is there a way like this:

    <Entry
                        x:Name="UserNameEntry"   
                    VisualState="{Binding ValidUserEntry}"                
                        Placeholder="UserName">  
                VisualStateGroupList stuff...
    </Entry>
    

    To change the aspect of the control from the viewmodel through the VisualStateManager?

  • NMackayNMackay GBInsider, University mod

    @MattiaDurli said:
    To answer both @NMackay and @ClintStLaurent:

    Yes Triggers may be an alternative, but let's say I want to use the VisualStates on an Entry:

        <Entry
                            x:Name="UserNameEntry"                    
                            Placeholder="UserName">  
               <VisualStateGroupList x:Name="CommonStates">
                            <VisualStateGroup>
                                <VisualState x:Name="Valid">
                                    <VisualState.Setters>
                                        <Setter Property="BackgroundColor" Value="White"></Setter>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Invalid">
                                    <VisualState.Setters>
                                        <Setter Property="BackgroundColor" Value="Red"></Setter>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateGroupList>                                   
        </Entry>
    

    I agree that from the ViewModel I shouldn't access the VisualElement directly, so I won't call:

    VisualStateManager.GoToState(UserNameEntry, "Invalid");

    But in my ViewModel I want the logic to decide if the data I entered is a valid UserName, and change the VisualState accordingly.

    So is there a way like this:

    <Entry
                        x:Name="UserNameEntry"   
                  VisualState="{Binding ValidUserEntry}"                
                        Placeholder="UserName">  
              VisualStateGroupList stuff...
    </Entry>
    

    To change the aspect of the control from the viewmodel through the VisualStateManager?

    I was doing the same thing in a Prism app earlier, really it's bust to wrap the the visual state triggers into a control based on entry for example and use Bindable Properties to get the control to trigger the visual state changes, that's what I did.

    I can't post my code but here's a sample from Telerik that might give an give you an approach.

    https://www.telerik.com/blogs/custom-visual-state-management-for-xamarin-forms

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    But in my ViewModel I want the logic to decide if the data I entered is a valid UserName, and change the VisualState accordingly.

    Stop. That's the problem with your thinking about this.

    But in my ViewModel I want the logic to decide if the data I entered is a valid UserName,

    Yes. Good.

    and change the VisualState accordingly.

    No. Bad. ViewModels don't know about UI, let alone make changes to it. Not it's area of concern.

    The view should change its state based on the data being valid. That's the UI's area of responsibility: What it looks like.
    You could do something like
    IsVisible = {Binding DataIsValid}
    or
    BackgroundColor = {Binding DataIsValid}, Converter = {ValidToColorConverter}
    At least with a converter you have all the rules in C# in one place.
    or with a DataTrigger and soemthing like

    DataTrigger...
    DataIsValid is false - flip color to red
    DataIsValies is true - flip color to green
    

    But do you see the conceptual difference here? The UI handles the UI. The VM handles the data.

    A quick question to ask yourself about if you should be handling something in the ViewModel is this:

    Does this work if I have 10 different views using this ViewModel and they may each have a different rule about what to do if the data is valid?

  • NMackayNMackay GBInsider, University mod

    Yeah,

    I have a control that controls navigation, the viewmodel just sets a property (an enum in this case), a converter converts it into the controls known state and then the visual state is altered, the control has no knowledge os teh data layer but is fully binabable.

    <customControls:PageNavigator.NextButton>
          <customControls:PageNavigatorButton Text="{Binding NextText}"
                          NavCommand="{Binding NextCommand}"
                          ButtonState="{Binding NextButtonState,Converter={converters:PageNavButtonStateConverter}}" 
                          ButtonFontSize="{x:OnPlatform Android=16,iOS=14,UWP=16}" />
         </customControls:PageNavigator.NextButton>
    

    The button state property change in the control triggers the visual state change, not the ViewModel, the vm just says "I'm at the last record", it's up to the UI layer to deal with that is presented.

  • MattiaDurliMattiaDurli ITMember ✭✭
    edited January 15

    First of all, I solved with this:
    https://forums.xamarin.com/discussion/127531/behavior-bindableproperty-not-update

    @ClintStLaurent said:

    But in my ViewModel I want the logic to decide if the data I entered is a valid UserName, and change the VisualState accordingly.

    Stop. That's the problem with your thinking about this.

    Ok, I expressed myself badly.
    What I meant is "I want the logic to decide if the data I entered is a valid UserName, and set a property accordingly"
    The VisualState will change based on that property.

    But in my ViewModel I want the logic to decide if the data I entered is a valid UserName,

    Yes. Good.

    and change the VisualState accordingly.

    No. Bad. ViewModels don't know about UI, let alone make changes to it. Not it's area of concern.

    Yes of course, but in the VM I set properies that are interpreted by the view. The View is rendered upon that properties too.

    The view should change its state based on the data being valid. That's the UI's area of responsibility: What it looks like.
    You could do something like
    IsVisible = {Binding DataIsValid}
    or
    BackgroundColor = {Binding DataIsValid}, Converter = {ValidToColorConverter}
    At least with a converter you have all the rules in C# in one place.
    or with a DataTrigger and soemthing like

    DataTrigger...
    DataIsValid is false - flip color to red
    DataIsValies is true - flip color to green
    

    Ok, but the data is not always just Valid or NotValid.
    For example, in the case of a password I can have "insufficient, weak, sufficient, strong", and have 4 VisualStates (different colors for example, but may be more).

    But do you see the conceptual difference here? The UI handles the UI. The VM handles the data.

    Yes, but ultimately, the UI is presented in different ways based on the data.
    So I'm not changing directly the visualstate, but the visualstate changes with the data.

    I don't think that using the VisualState this way breaks the separation of concerns, is not much different than the DataTrigger.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    What I meant is "I want the logic to decide if the data I entered is a valid UserName, and set a property accordingly"

    The VisualState will change based on that property.
    Perfect

    Yes of course, but in the VM I set properies that are interpreted by the view. The View is rendered upon that properties too.

    Great - so long as those properties are not view-specific properties like "ButtonIsEnabled" or "UserCanClickSave" or "MastheadBackgroundColor"

    Ok, but the data is not always just Valid or NotValid.
    For example, in the case of a password I can have "insufficient, weak, sufficient, strong", and have 4 VisualStates (different colors for example, but may be more).

    Yep. Enums

    Yes, but ultimately, the UI is presented in different ways based on the data.
    So I'm not changing directly the visualstate, but the visualstate changes with the data.

    Sounds like we are in agreement.

    I don't think that using the VisualState this way breaks the separation of concerns,

    If you're using VisualState in the ViewModel which isn't visual at all... Then yeah, it breaks the concerns. Data does not know anything about UI.

    You have a car.
    Its paint job is a property
    Its steering wheel material is a property.
    If your have the deluxe model that means you have a blue paint job and steel steering wheel.
    If you have the basic model that means you have a red paint job and a plastic steering wheel.
    At no time does the steering wheel know if it is in a basic or deluxe model, nor does it choose what color paint is on the body of the car - even though there is a business rule relating these things - it is not the concern of the steering wheel property to decide what color to paint the outside.

Sign In or Register to comment.