1
votes

I am working on expression Blend for VS2015, I have aListBox binded to an ObservableCollection of custom objects. Those objects expose Properties that arise the NotifyPropertyChanged, and everything works nice.

I can bind parts if the ItemTemplate to those Properties and my list work nice but what I want to do is to set the VisualState according to a certain bool (already configured or not). I also created some events (configured, confLost) and tried to target those events in the triggers panel but .. nothing worked.

How do I bind VisualStates to members of the bound object ??

3
Hi Javirs, Can you post your object class code? Or a snippet of it. I'll be able to help you thenMaster
@Master as shown in my answer, code to the object was not needed at all.javirs

3 Answers

2
votes

ItemTemplate property works like any other DependencyProperty, it can be set/reset anytime and it's visual impact will be reflected on UI. see below example where I have bound a bool value to ToggleButton state and ItemControl's ItemTemplate is changed accordingly rendering different visual.

Update: I designed a Device class that has device name and it's state to make a similar situation. And another class MyVisualStateManager to create a bindable property. Cause VisualStateManager class doesn't expose any property to bind directly. code is as below:

XMAL

<Window x:Class="WpfStackOverflowTempProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"  Width="525"
    DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
    xmlns:local="clr-namespace:WpfStackOverflowTempProject"
    >
    <ItemsControl ItemsSource="{Binding list}" >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:UserControl1  DataContext="{Binding Name}" Width="200" BorderBrush="Black" BorderThickness="2" Padding="2">
                    <local:UserControl1.Style>
                        <Style TargetType="{x:Type local:UserControl1}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.DeviceState}" Value="0">
                                    <Setter Property="local:MyVisualStateManager.VisualState" Value="State1" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.DeviceState}" Value="1">
                                    <Setter Property="local:MyVisualStateManager.VisualState" Value="State2" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </local:UserControl1.Style>
                </local:UserControl1>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

UserControl:

<UserControl x:Class="WpfStackOverflowTempProject.UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>        
    <VisualStateManager.VisualStateGroups>            
        <VisualStateGroup x:Name="Common">                
            <VisualState x:Name="State1">                    
                <Storyboard>                        
                    <DoubleAnimation To="1" Duration="0:00:2" Storyboard.TargetName="State1Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />                        
                    <DoubleAnimation To="0" Duration="0:00:3" Storyboard.TargetName="State2Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />                        
                </Storyboard>                    
            </VisualState>                
            <VisualState x:Name="State2">                    
                <Storyboard>                        
                    <DoubleAnimation To="0" Duration="0:00:3" Storyboard.TargetName="State1Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />                        
                    <DoubleAnimation To="1" Duration="0:00:2" Storyboard.TargetName="State2Panel" Storyboard.TargetProperty="(UIElement.Opacity)" />                        
                </Storyboard>                    
            </VisualState>                
        </VisualStateGroup>            
    </VisualStateManager.VisualStateGroups>        
    <Border Name="State2Panel" Background="Green" Opacity="0"/>        
    <Border Name="State1Panel" Background="Red" Opacity="1"/>
    <TextBlock Text="{Binding Path=.}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>        
</Grid>

DataContext:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        list = new List<Device>();
        list.Add(new Device() {Name="Device 1",DeviceState = 0 });
        list.Add(new Device() { Name = "Device 2", DeviceState = 1 });
        list.Add(new Device() { Name = "Device 3", DeviceState = 0 });
        list.Add(new Device() { Name = "Device 4", DeviceState = 2 });
        list.Add(new Device() { Name = "Device 5", DeviceState = 1 });
        InitializeComponent();
    }

    public List<Device> list { get; set; }

}

public class Device : INotifyPropertyChanged
{
    private string name;

    public string Name
    {
        get { return name; }
        set 
        { 
            name = value;
            updateProperty("Name");
        }
    }
    private int deviceState;

    public int DeviceState
    {
        get { return deviceState; }
        set 
        { 
            deviceState = value;
            updateProperty("DeviceState");
        }
    }



    public event PropertyChangedEventHandler PropertyChanged;
    public void updateProperty(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

Helper Class: This class exposes an attached property VisualState that could be bound to any value in xaml.

public class MyVisualStateManager
{        
    public static string GetVisualState(DependencyObject obj)
    {
        return (string)obj.GetValue(VisualStateProperty);
    }

    public static void SetVisualState(DependencyObject obj, string value)
    {
        obj.SetValue(VisualStateProperty, value);
    }

    // Using a DependencyProperty as the backing store for VisualState.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty VisualStateProperty =
        DependencyProperty.RegisterAttached("VisualState", typeof(string), typeof(MyVisualStateManager), new PropertyMetadata(new PropertyChangedCallback(VisualStateChanged)));

    public static void VisualStateChanged(DependencyObject Do, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            string state = e.NewValue.ToString();
            var control = Do as FrameworkElement;
            VisualStateManager.GoToState(control, state, true);
        }
    }
}

Output

Different Item representing different devices and visual is changed on basis of theirDevicestateproperty which causes aTriggerto get executed inUserControl1.

list Item

1
votes

Although Kylo's solution would provably work, the people at Microsoft already worked a code-free, 3-clicks-away solution for such a simple action to do.

Solution is on Behaviors, there is one behavior called GoToStateAction you have to add one of them to your control and there you can set your trigger (that can be set as DataTrigger). In my case I binded to a property of the type enum. Then you can set the Comparison and the value (equals to "ReadyToUse")

Then as an outcome of the comparison you can trigger a state change for a particular object, you set your object, and you select the state from a nice combobox. There is even a checbox for using your transitions.

VS screenshot

0
votes

I mean in the user interface of Blend, where to click to get the conditional working,

  1. In Blend look for the Objects and Timeline panel.
  2. Select the control in your defined ItemTemplate (you have set one up right?) which you want to have the state changes. In Kylo's example above we would have selected the TextBox.
  3. Right click and select Edit Style the either create a new style (most likely) or Edit a Copy which works of any existing inherited styles.

enter image description here

From there one can work on the properties tab to change specifics. Most likely you will be working directly in the xaml to do specific operations.

enter image description here


Even though these docs for Version 2, they still apply, if nothing else can give you an overview of Blend

Create a style resource

Style and template overview