WPF прикрепляет VisualState к свойству объекта

Я работаю над выражением Blend для VS2015, у меня есть aListBox привязанный к ObservableCollection пользовательских объектов. Те объекты выставляют Properties, которые вызывают NotifyPropertyChanged, и все работает хорошо.

Я могу привязать части, если ItemTemplate к этим Properties, и мой список работает хорошо, но я хочу установить VisualState в соответствии с определенным bool (уже настроенным или нет). Я также создал некоторые события (настроенные, confLost) и попытался настроить таргетинг на эти события на панели триггеров, но... ничего не сработало.

Как привязать VisualStates к членам связанного объекта??


person javirs    schedule 25.04.2016    source источник
comment
Привет, Джавиры! Можете ли вы опубликовать свой код объектного класса? Или его фрагмент. Тогда я смогу тебе помочь   -  person Master    schedule 02.05.2016
comment
@Master, как показано в моем ответе, код для объекта вообще не нужен.   -  person javirs    schedule 06.05.2016


Ответы (3)


Свойство ItemTemplate работает так же, как и любое другое DependencyProperty, его можно установить / сбросить в любое время, и его визуальное воздействие будет отражено на UI. см. пример ниже, где я привязал значение bool к состоянию ToggleButton, а ItemControl's ItemTemplate соответствующим образом изменил, отображая другой visual.

Обновление: я разработал класс Device с именем устройства и его состоянием, чтобы создать аналогичную ситуацию. И еще один класс MyVisualStateManager для создания привязываемого свойства. Причина: класс VisualStateManager не предоставляет никаких свойств для прямой привязки. код, как показано ниже:

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));
        }
    }
}

Вспомогательный класс. Этот класс предоставляет присоединенное свойство VisualState, которое может быть привязано к любому значению в 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);
        }
    }
}

Вывод

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

элемент списка

person Kylo Ren    schedule 03.05.2016
comment
Любая подсказка о том, как это сделать из интерфейса смешивания выражений? кстати, я хочу изменить VisualState каждого из элементов отдельно в соответствии с логическим значением для его конкретного объекта, но я думаю, из вашего примера легко сделать вывод, как туда добраться. Можете ли вы дать некоторые подробности о том, как это сделать из интерфейса Blend выражения? - person javirs; 03.05.2016
comment
@javirs в выражении blend ... вы имеете в виду, читая это значение bool из некоторой конфигурации? - person Kylo Ren; 03.05.2016
comment
Я имею в виду в пользовательском интерфейсе Blend, где щелкнуть, чтобы получить условную работу, переключение между VisualStates (с определенной анимацией) в зависимости от переменной Data. Пока я вообще не редактирую xaml, просто использую панели Blend. А дела вполне в порядке. Я хотел бы продолжать делать это. - person javirs; 03.05.2016
comment
@javirs, кстати, когда вы сказали, что я хочу изменить VisualState каждого из элементов отдельно в соответствии с логическим значением, вы имеете в виду, что хотите установить визуальное представление каждого элемента отдельно в вашем ListBox. т.е. первый элемент в списке может иметь другое визуальное оформление, чем второй элемент списка, в зависимости от этого логического значения. Я прав? - person Kylo Ren; 03.05.2016
comment
@javirs Где щелкнуть, чтобы получить условную рабочую точку. Я полностью обновлю ответ, как только вы очистите мою точку зрения. - person Kylo Ren; 03.05.2016
comment
точно, у меня есть одна запись в списке для каждого устройства, подключенного к системе, и я хочу разные визуальные состояния для тех устройств, которые работают правильно, не работают, заняты и т. д. и т. д. - person javirs; 03.05.2016
comment
@javirs, и каждое ли устройство представлено одним и тем же визуалом в ListBox, когда все работают правильно? Да или нет? и второй вопрос касается того логического значения, о котором мы говорим, представляет ли он состояние машины (ов)? - person Kylo Ren; 03.05.2016
comment
да, все устройства имеют одинаковые свойства и одинаковый внешний вид, когда они работают нормально. Затем у меня есть разные визуальные состояния для сломанного состояния (должно быть вызвано логическим значением) и другое состояние для выбранного в данный момент (которое может изменить визуальное состояние либо из ListBox (если есть способ), либо, если необходимо, также есть логическое в устройстве с именем CurrentSelected, которое я отлаживал и которое работает должным образом. - person javirs; 03.05.2016
comment
@javirs посмотрите мой обновленный ответ, дайте мне знать, если я пропустил какую-либо часть. Важной частью вывода будет то, насколько вы хотите изменить визуализацию при изменении состояния устройства, я просто использую цвет фона. - person Kylo Ren; 03.05.2016
comment
вы устанавливаете свойство фона, я хотел бы изменить визуальное состояние, включая анимацию, которую я установил для этих изменений визуального состояния на панели состояний Blend. Я перемещаю метки и меняю тени и другие вещи, что довольно сложно. Есть ли способ установить визуальное состояние? - person javirs; 03.05.2016
comment
@javirs, извините, мне потребовалось немного времени, чтобы принять участие в этом мероприятии. пожалуйста, посмотрите мое обновление в ответе, где я меняю визуальное состояние на основе свойства привязки. пожалуйста, дайте мне знать, если что-то еще не завершено. - person Kylo Ren; 04.05.2016
comment
@javirs помогло? - person Kylo Ren; 05.05.2016
comment
Извините, я сейчас очень занят, поэтому, думаю, я не смогу протестировать до выходных, но ваш код имеет смысл и соответствует именно тому, что я искал. Я СУПЕР удивлен, что состояния не будут меняться автоматически с некоторыми встроенными вещами (поведением?), но использование дополнительного класса поможет. Я протестирую его как можно скорее, и я дам вам знать (и выберите свой ответ) - person javirs; 06.05.2016
comment
как и ожидалось, есть более простое решение, поведение GoToStateAction позволяет использовать триггер (который может быть DataTrigger), и, как следствие, вы можете изменить состояние объекта.. 0 строк кода, 4 клика ... я опубликую хороший снимок экрана с подробностями в качестве ответа. Как бы то ни было, я хотел поблагодарить вас лично за усилия, которые вы приложили в своем ответе. - person javirs; 06.05.2016
comment
@javirs добро пожаловать, я тоже учился ... и жду твоего обновления ..... :) - person Kylo Ren; 06.05.2016
comment
Я добавил метод в качестве ответа и выбрал правильный ответ, поскольку это лучший подход к решению, но все равно отправил вам 50 реплик. Если бы вы могли проголосовать за ответ, он появится перед этой темой. Я думаю, это лучше для будущих посетителей - person javirs; 06.05.2016

Хотя решение Кайло доказуемо сработает, люди в Microsoft уже разработали решение, позволяющее всего за 3 клика выполнить такое простое действие без кода.

Решение относится к поведению, есть одно поведение, называемое GoToStateAction, вы должны добавить одно из них в свой элемент управления, и там вы можете установить свой триггер (который может быть установлен как DataTrigger). В моем случае я привязался к свойству типа enum. Затем вы можете установить сравнение и значение (равное «ReadyToUse»)

Затем в результате сравнения вы можете инициировать изменение состояния для определенного объекта, вы устанавливаете свой объект и выбираете состояние из красивого списка. Есть даже чекбокс для использования ваших переходов.

Скриншот VS

person javirs    schedule 06.05.2016
comment
хорошо... вы также должны опубликовать код, который изменился после этого... это фактический ключ. - person Kylo Ren; 07.05.2016

Я имею в виду в пользовательском интерфейсе Blend, где щелкнуть, чтобы получить условную работу,

  1. В Blend найдите панель Objects and Timeline.
  2. Выберите элемент управления в вашем определенном ItemTemplate (вы правильно настроили?), Состояние которого вы хотите изменить. В приведенном выше примере Кайло мы выбрали бы TextBox.
  3. Щелкните правой кнопкой мыши и выберите Edit Style либо создать новый стиль (скорее всего), либо Edit a Copy, который работает с любыми существующими унаследованными стилями.

введите описание изображения здесь

Оттуда можно работать на вкладке свойств, чтобы изменить особенности. Скорее всего, вы будете работать непосредственно в xaml для выполнения определенных операций.

введите описание изображения здесь


Несмотря на то, что эти документы для Версии 2, они все еще применимы, если ничто другое не может дать вам обзор Blend

Создайте ресурс стилей

Обзор стилей и шаблонов

person ΩmegaMan    schedule 03.05.2016