Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WPF, Silverlight Новый топик    Ответить
 MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Хотел изменять вид ListBoxItem в зависимости от контента и внешних свойств, но то ли я что-то не так делаю, то ли ...
В общем у меня сложилось впечатление, что MultiDataTrigger в стилях ListBoxItem не работает. Специально сделал конвертер и точку останова в нём для проверки. Конвертер даже не вызывается.
Действительно, MultiDataTrigger в ListBoxItem не работает? Или я опять что-то не так делаю?
22 окт 18, 00:11    [21710576]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19959
вы искренне считаете, что можно ответить на ваш вопрос без кода?
22 окт 18, 00:35    [21710587]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Shocker.Pro
вы искренне считаете, что можно ответить на ваш вопрос без кода?
Ну, вообще-то, я думал да. Ответят - да, не работаю. И я брошу эту затею.
Но по Вашему вопросу - похоже, возможны варианты.
Вот пример.
Класс для заполнения Listbox.ItemsSorce
    public class Example
    {
        public String PropStr { get; set; }
        public bool PropBool { get; set; }
    }

ListBox со стилем ItemContainerStyle
        <ListBox DisplayMemberPath="PropStr" 
                 SelectedIndex="1" 
                 >
            <ListBox.Resources>
                <SolidColorBrush x:Key="BackCol" Color="LightPink"/>
                <SolidColorBrush x:Key="IsMouseBackCol" Color="Violet"/>
                <SolidColorBrush x:Key="IsMouseCol" Color="Red"/>
            </ListBox.Resources>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Foreground" Value="Blue"/>
                        </Trigger>
                        <DataTrigger Binding="{Binding Content.PropBool, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="False">
                            <Setter Property="Background" Value="{StaticResource BackCol}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsMouseOver, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True">
                            <Setter Property="Background" Value="{StaticResource IsMouseCol}"/>
                        </DataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Content.PropBool, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="False"/>
                                <Condition Binding="{Binding IsMouseOver, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" Value="{StaticResource IsMouseBackCol}"/>
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemsSource>
                <sys:ArrayList >
                    <local:Example PropStr="Строка 1" PropBool="True"/>
                    <local:Example PropStr="Строка 2" PropBool="False"/>
                    <local:Example PropStr="Строка 3" PropBool="True"/>
                    <local:Example PropStr="Строка 4" PropBool="True"/>
                    <local:Example PropStr="Строка 5" PropBool="False"/>
                </sys:ArrayList>
            </ListBox.ItemsSource>
        </ListBox>

В примере.
Просто Trigger на IsSelected работает нормально.
Первый DataTrigger - попытка установить цвет фона строки в зависимости от свойства содержащегося в Content объекта.
Второй DataTrigger - установить цвет фона строки в зависимости от наведения мыши.
MultiDataTrigger - установить фон в зависимости от свойства объекта и наведения мыши.

Конечно, можно попробовать сделать это через StyleSelector - но он точно не обновляется при изменении объектов источника. не подходит для той цели, что мне нужна.
22 окт 18, 03:11    [21710617]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Вот это:
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}

- с какого перепугу? Стиль для ListBoxItem, и у него в данном случае выше по визуальному дереву нет никаких ancestors с типом ListBoxItem. В стиле везде должно быть:
RelativeSource={RelativeSource Self}

а то, что у вас, могло бы использоваться в стиле внутри DataTemplate, либо если бы у стиля был переопределен ControlTemplate.
22 окт 18, 06:17    [21710633]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны
Вот это:
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}

- с какого перепугу? Стиль для ListBoxItem, и у него в данном случае выше по визуальному дереву нет никаких ancestors с типом ListBoxItem. В стиле везде должно быть:
RelativeSource={RelativeSource Self}

а то, что у вас, могло бы использоваться в стиле внутри DataTemplate, либо если бы у стиля был переопределен ControlTemplate.
Когда в конструкторе создаю привязку, при выборе RelativeSource Self - показывает привязку к самому триггеру DataTrigger. Исправил в "ручную"
                        <DataTrigger Binding="{Binding Content.PropBool, RelativeSource={RelativeSource Self}}" Value="False">
                            <Setter Property="Background" Value="{StaticResource BackCol}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsMouseOver, Mode=OneWay, RelativeSource={RelativeSource Self}}" Value="True">
                            <Setter Property="Background" Value="{StaticResource IsMouseCol}"/>
                        </DataTrigger>
Первый DataTrigger - стал работать как задумано. Получается конструктор даёт неверные привязки?
Но второй DataTrigger на привязывание свойства IsMouseOver - всё равно не работает. С ним что не так?

С использованием DataTemplate - тоже делал. Но там также не работало. Здесь привёл вариант без DataTemplate - для сокращения примера. Сначала хочу разобрать с тем что по проще - потом усложнять.
22 окт 18, 11:06    [21710810]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 3171
Вот еще вариант, без всяких DataTrigger'ов:
<Window x:Class="WpfApp17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp17"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainModel/>
    </Window.DataContext>
    <Window.Resources>
        <Style x:Key="MyListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="local:CanHoverHelper.CanHover" Value="{Binding CanHover}"/>
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True"/>
                        <Condition Property="local:CanHoverHelper.CanHover" Value="True"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="TextElement.Foreground" Value="Green"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding Persones}" 
                 ItemContainerStyle="{StaticResource MyListBoxItemStyle}"
                 DisplayMemberPath="Name"/>
    </Grid>
</Window>

using System.Collections.Generic;
using System.Windows;

namespace WpfApp17
{
    public class CanHoverHelper
    {
        public static bool GetCanHover(DependencyObject obj) { return (bool)obj.GetValue(CanHoverProperty); }
        public static void SetCanHover(DependencyObject obj, bool value) { obj.SetValue(CanHoverProperty, value); }
        public static readonly DependencyProperty CanHoverProperty =
            DependencyProperty.RegisterAttached("CanHover", typeof(bool), typeof(CanHoverHelper), new PropertyMetadata(true));
    }

    public class Person
    {
        public string Name { set; get; }
        public bool CanHover { set; get; }
    }

    public class MainModel
    {
        public List<Person> Persones { get; } = new List<Person>
        {
            new Person { Name = "Roman", CanHover = true },
            new Person { Name = "Nikolay", CanHover = false },
            new Person { Name = "Dmitriy", CanHover = true }
        };
    }
}
22 окт 18, 11:11    [21710814]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Eld Hasp
Первый DataTrigger - стал работать как задумано. Получается конструктор даёт неверные привязки?

В каком конструкторе? В UI-дизайнере? Я лично им не пользуюсь вообще, где он там может ошибаться - понятия не имею..
Eld Hasp
Но второй DataTrigger на привязывание свойства IsMouseOver - всё равно не работает. С ним что не так?

Вообще тут и обычный триггер будет работать, т.к. IsMouseOver - это DP самого контрола, для которого предназначен стиль.
Но у меня на win7 вполне работает и в DataTrigger'е. Может, опять какие-то нюансы отличий в реализациях, зависящих от версии ОС.
22 окт 18, 11:29    [21710845]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 3171
для того, чтоб задний фон менялся, нужно переопределить шаблон элемента ListBoxItem.ControlTemplate, тогда всё будет гуд. У меня в Win10 свойство Backgroud для этого элемента со стандартным шаблоном тоже не работает.
Вообще не часто, но сталкивался с такой проблемой, что в одной версии Windows элемент выглядит одним образом, а в другой совершенно иначе. По причине дефолтных шаблонов. Довольно неприятная особенность, если используешь стандартный вид интерфейса, именно по этому в примере, что я сюда закинул, я меняю свойство TextElement.Foreground :D
22 окт 18, 11:41    [21710859]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Roman Mejtes
Вот еще вариант, без всяких DataTrigger'ов:
Здорово! Мне такая мысль даже в голову не пришла! Для того что мне нужно - отлично подходит.

Но всё же, уже из чистого интереса, а что же с DataTrigger не так? Хотелось бы, раз уже столкнулся с этим, понять до конца.
22 окт 18, 11:46    [21710872]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19959
Roman Mejtes
Вообще не часто, но сталкивался с такой проблемой, что в одной версии Windows элемент выглядит одним образом, а в другой совершенно иначе.
Да, нахрен, постоянно.
Складывается впечатление, что нужно для всех стандартных контролов скачать стандартные же шаблоны и положить их широковещательно в ресурсы приложения, чтобы не иметь в дальнейшем головняка.
22 окт 18, 11:56    [21710892]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 3171
<Window x:Class="WpfApp17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp17"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainModel/>
    </Window.DataContext>
    <Window.Resources>
        <Style x:Key="MyListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
            <Setter Property="local:CanHoverHelper.CanHover" Value="{Binding CanHover}"/>
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True"/>
                        <Condition Property="local:CanHoverHelper.CanHover" Value="True"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="TextElement.Foreground" Value="Green"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <ListBox SelectedIndex="1" 
                 ItemsSource="{Binding Persones}"
                 DisplayMemberPath="Name">
            <ListBox.Resources>
                <SolidColorBrush x:Key="BackCol" Color="LightPink"/>
                <SolidColorBrush x:Key="IsMouseBackCol" Color="Violet"/>
                <SolidColorBrush x:Key="IsMouseCol" Color="Red"/>
            </ListBox.Resources>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <Border Background="{TemplateBinding Background}"
                                        BorderThickness="{TemplateBinding BorderThickness}"
                                        BorderBrush="{TemplateBinding BorderBrush}"
                                        Padding="{TemplateBinding Padding}">
                                    <ContentPresenter/>
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Foreground" Value="Blue"/>
                        </Trigger>
                        <DataTrigger Binding="{Binding Content.PropBool, RelativeSource={RelativeSource Self}}" Value="False">
                            <Setter Property="Background" Value="{StaticResource BackCol}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
                            <Setter Property="Background" Value="{StaticResource IsMouseCol}"/>
                        </DataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Content.CanHover, RelativeSource={RelativeSource Self}}" Value="True"/>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" Value="{StaticResource IsMouseBackCol}"/>
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>
</Window>

Вот рабочий пример, скопированный с вашего и подправленный, естественно триггеры надо доработать, иначе там какая то новогодняя елка. И модель я заменил на свою, так как вашу пилить лень
22 окт 18, 12:00    [21710897]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Shocker.Pro
Да, нахрен, постоянно.
Складывается впечатление, что нужно для всех стандартных контролов скачать стандартные же шаблоны и положить их широковещательно в ресурсы приложения, чтобы не иметь в дальнейшем головняка.

Ну, у меня еще с тех времен, когда была нужда поддерживать проекты для XP, в App.xaml сидит вот такое:
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="pack://application:,,,/PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component\themes/aero.normalcolor.xaml" />
        .........................
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>
22 окт 18, 12:08    [21710908]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны
Eld Hasp
Первый DataTrigger - стал работать как задумано. Получается конструктор даёт неверные привязки?

В каком конструкторе? В UI-дизайнере? Я лично им не пользуюсь вообще, где он там может ошибаться - понятия не имею..

С этим ясность полная - конструктор вещь не надёжная...
Сон Веры Павловны
Eld Hasp
Но второй DataTrigger на привязывание свойства IsMouseOver - всё равно не работает. С ним что не так?

Вообще тут и обычный триггер будет работать, т.к. IsMouseOver - это DP самого контрола, для которого предназначен стиль.
Да, обычный, конечно, будет работать. Но мне нужен MultiDataTrigger в котором два условия. А DataTrigger'а - это для проверки работы условий по отдельности.
Сон Веры Павловны
Но у меня на win7 вполне работает и в DataTrigger'е. Может, опять какие-то нюансы отличий в реализациях, зависящих от версии ОС.
Да, я на 10-ке делаю.
22 окт 18, 12:12    [21710910]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Roman Mejtes
Вот рабочий пример, скопированный с вашего и подправленный, естественно триггеры надо доработать, иначе там какая то новогодняя елка. И модель я заменил на свою, так как вашу пилить лень
Новогодняя ёлка и модель не важны. Мне бы только суть понять.
На сколько понял (в т.ч. и из Вашей переписки с Сон Веры Павловны) в данном случае (для Win 10) для переопределения цвета фона надо явно в шаблоне прописывать Border (или иной элемент с фоном).

Спасибо за внимание и помощь.
22 окт 18, 12:24    [21710922]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны, Roman Mejtes, Shocker.Pro - ещё раз СПАСИБО вам!
Окончательный вариант составленный с Вашей помощью.
    <Window.Resources>
        
        <SolidColorBrush x:Key="StatusBackCol" Color="#1EFF69B4"/>
        <SolidColorBrush x:Key="StatusBackColIsMouse" Color="#3FEE82EE"/>
        <SolidColorBrush x:Key="StatusBackColIsSelected" Color="#3D9400D3"/>
        <SolidColorBrush x:Key="ForegroundSelected" Color="Blue"/>


        <SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
        <SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
        <SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
        <SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
        <SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
        <SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
        <Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
            <Setter Property="local:StatusHelper.Status" Value="{Binding Status}"/>
            
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="Padding" Value="4,1"/>
            <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Setter Property="BorderThickness" Value="1"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="False"/>
                                    <Condition Property="IsSelected" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
                                <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="True"/>
                                    <Condition Property="IsSelected" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
                                <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="local:StatusHelper.Status" Value="True">
                                <Setter Property="Background" TargetName="Bd" Value="{StaticResource StatusBackCol}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="local:StatusHelper.Status" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{StaticResource StatusBackColIsMouse}"/>
                                <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="True"/>
                                    <Condition Property="local:StatusHelper.Status" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{StaticResource StatusBackColIsSelected}"/>
                                <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
                            </MultiTrigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{StaticResource ForegroundSelected}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Grid>
        <ListBox DisplayMemberPath="Display" ItemContainerStyle="{StaticResource ListBoxItemStyle1}">
            <ListBox.ItemsSource>
                <sys:ArrayList>
                    <local:ListBoxContentClass DisplayOrder="1" Display="Первый клиент"  ID="Clients"/>
                    <local:ListBoxContentClass DisplayOrder="3" Display="Лучший клиент"  ID="Warehouses"/>
                    <local:ListBoxContentClass DisplayOrder="2" Display="Ахмед"  ID="Accounts" Status="True"/>
                    <local:ListBoxContentClass DisplayOrder="4" Display="ТОО Oil"  ID="Products"/>
                    <local:ListBoxContentClass DisplayOrder="5" Display="АЗС №4"  ID="TypesInvoices" Status="True"/>
                </sys:ArrayList>
            </ListBox.ItemsSource>
        </ListBox>
        
    </Grid>

namespace WpfQuestions
{
    /// <summary>
    /// Логика взаимодействия для MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    public class StatusHelper
    {
        public static bool GetStatus(DependencyObject obj) { return (bool)obj.GetValue(StatusProperty); }
        public static void SetStatus(DependencyObject obj, bool value) { obj.SetValue(StatusProperty, value); }
        public static readonly DependencyProperty StatusProperty =
            DependencyProperty.RegisterAttached("Status", typeof(bool), typeof(StatusHelper), new PropertyMetadata(true));
    }

    public class ListBoxContentClass
    {
        /// <summary>Сортируемое значение</summary>
        public string DisplayOrder { get; set; }
        /// <summary>Идентификатор</summary>
        public string ID { get; set; }
        /// <summary>Отображаемое значение</summary>
        public string Display { get; set; }
        /// <summary>Состояние статуса </summary>
        public bool Status { get; set; } = false;
    }
}
22 окт 18, 21:16    [21711692]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 3171
а) обычно переменная статус, состояние имеет не только 2 значения true\false, а те переменные\поля которые имеют логическое состояние верно\неверно называют иначе. Типа IsMouseOver, CanHide и т.д. Тогда тем, кто будет разрабатывать после вас, будет понятно, что это за поле и за что оно отвечает. А Статус, это хер пойми вообще, что. Что отражает это AttachedProperty не ясно, а в комментарии написано "состояние статуса"...
б) в ControlTemplate делать Binding и DataTrigger'ы, это не очень круто. Лучше делать это в стиле или в макете. Сам по себе ControlTemplate не должен быть привязан к какой то модели. Ведь элементы управления имеют абстракцию от вашей системы. В идеале, в ControlTemplate лучше использовать {TemplateBinding} или {Property, RelativeSource={RelativeSource ParentTemplate}}. Или относительное связывание. Но его имеет смысл избегать вообще.
22 окт 18, 22:52    [21711725]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Roman Mejtes
а) обычно переменная статус, состояние имеет не только 2 значения true\false, а те переменные\поля которые имеют логическое состояние верно\неверно называют иначе. Типа IsMouseOver, CanHide и т.д. Тогда тем, кто будет разрабатывать после вас, будет понятно, что это за поле и за что оно отвечает. А Статус, это хер пойми вообще, что. Что отражает это AttachedProperty не ясно, а в комментарии написано "состояние статуса"...
б) в ControlTemplate делать Binding и DataTrigger'ы, это не очень круто. Лучше делать это в стиле или в макете. Сам по себе ControlTemplate не должен быть привязан к какой то модели. Ведь элементы управления имеют абстракцию от вашей системы. В идеале, в ControlTemplate лучше использовать {TemplateBinding} или {Property, RelativeSource={RelativeSource ParentTemplate}}. Или относительное связывание. Но его имеет смысл избегать вообще.

Подправил по Вашим рекомендациям. Посмотрите так правильнее будет?
    <Window.Resources>

        <!--Цвета для стиля-->
        <SolidColorBrush x:Key="IsHighlightedBackCol" Color="#1EFF69B4"/>
        <SolidColorBrush x:Key="IsHighlightedBackCol.MouseOver" Color="#3FEE82EE"/>
        <SolidColorBrush x:Key="IsHighlightedBackColIs.SelectedInactive" Color="#1E9400D3"/>
        <SolidColorBrush x:Key="IsHighlightedBackColIs.SelectedActive" Color="#3D9400D3"/>

        <!--Цвета для шаблона-->
        <SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
        <SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
        <SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#1E26A0DA"/>
        <SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
        <SolidColorBrush x:Key="Item.SelectedInactive.Foreground" Color="#BF0000FF"/>
        <SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
        <SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
        <SolidColorBrush x:Key="Item.SelectedActive.Foreground" Color="Blue"/>

        <!-- ************ Шаблон ************ -->
        <Style x:Key="ListBoxItemStyleTemplate" TargetType="{x:Type ListBoxItem}">
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="Padding" Value="4,1"/>
            <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Setter Property="BorderThickness" Value="1"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                BorderThickness="{TemplateBinding BorderThickness}" 
                                Background="{TemplateBinding Background}" 
                                Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background"  Value="{StaticResource Item.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" Value="{StaticResource Item.MouseOver.Border}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="False"/>
                                    <Condition Property="IsSelected" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" Value="{StaticResource Item.SelectedInactive.Background}"/>
                                <Setter Property="BorderBrush" Value="{StaticResource Item.SelectedInactive.Border}"/>
                                <Setter Property="TextElement.Foreground" Value="{StaticResource Item.SelectedInactive.Foreground}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="True"/>
                                    <Condition Property="IsSelected" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" Value="{StaticResource Item.SelectedActive.Background}"/>
                                <Setter Property="BorderBrush" Value="{StaticResource Item.SelectedActive.Border}"/>
                                <Setter Property="TextElement.Foreground" Value="{StaticResource Item.SelectedActive.Foreground}"/>
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
        <!-- ********* Стиль ****************** -->
        <Style x:Key="ListBoxItemStyleIsHighlighted" TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItemStyleTemplate}">
            <Setter Property="local:IsHighlightedHelper.IsHighlighted" Value="{Binding IsHighlighted}"/>

            <Style.Triggers>
                <Trigger Property="local:IsHighlightedHelper.IsHighlighted" Value="True">
                    <Setter Property="Background"  Value="{StaticResource IsHighlightedBackCol}"/>
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True"/>
                        <Condition Property="local:IsHighlightedHelper.IsHighlighted" Value="True"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="Background"  Value="{StaticResource IsHighlightedBackCol.MouseOver}"/>
                </MultiTrigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="Selector.IsSelectionActive" Value="False"/>
                        <Condition Property="IsSelected" Value="True"/>
                        <Condition Property="local:IsHighlightedHelper.IsHighlighted" Value="True"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="Background"  Value="{StaticResource IsHighlightedBackColIs.SelectedInactive}"/>
                </MultiTrigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="Selector.IsSelectionActive" Value="True"/>
                        <Condition Property="IsSelected" Value="True"/>
                        <Condition Property="local:IsHighlightedHelper.IsHighlighted" Value="True"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="Background"  Value="{StaticResource IsHighlightedBackColIs.SelectedActive}"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Grid>
        <ListBox x:Name="listBox" DisplayMemberPath="Display" ItemContainerStyle="{StaticResource ListBoxItemStyleIsHighlighted}">
            <ListBox.ItemsSource>
                <sys:ArrayList>
                    <local:ListBoxContentClass DisplayOrder="1" Display="Первый клиент"  ID="Clients"/>
                    <local:ListBoxContentClass DisplayOrder="3" Display="Лучший клиент"  ID="Warehouses"/>
                    <local:ListBoxContentClass DisplayOrder="2" Display="Ахмед"  ID="Accounts" IsHighlighted="True"/>
                    <local:ListBoxContentClass DisplayOrder="4" Display="ТОО Oil"  ID="Products"/>
                    <local:ListBoxContentClass DisplayOrder="5" Display="АЗС №4"  ID="TypesInvoices" IsHighlighted="True"/>
                </sys:ArrayList>
            </ListBox.ItemsSource>
        </ListBox>
        <TextBox Text="{Binding SelectedItem.Display, ElementName=listBox}" VerticalAlignment="Bottom"/>
    </Grid>
    public class IsHighlightedHelper
    {
        public static bool GetIsHighlighted(DependencyObject obj) { return (bool)obj.GetValue(IsHighlightedProperty); }
        public static void SetIsHighlighted(DependencyObject obj, bool value) { obj.SetValue(IsHighlightedProperty, value); }
        public static readonly DependencyProperty IsHighlightedProperty =
            DependencyProperty.RegisterAttached("IsHighlighted", typeof(bool), typeof(IsHighlightedHelper), new PropertyMetadata(true));
    }

    public class ListBoxContentClass
    {
        /// <summary>Сортируемое значение</summary>
        public string DisplayOrder { get; set; }
        /// <summary>Идентификатор</summary>
        public string ID { get; set; }
        /// <summary>Отображаемое значение</summary>
        public string Display { get; set; }
        /// <summary>Состояние статуса </summary>
        public bool IsHighlighted { get; set; } = false; 
    }


И я не совсем понял на счёт разделения шаблона и стиля. Я воспринимал, что шаблон это часть стиля. Ведь шаблон всё равно применяется через стиль. Получается такое понимание - не верно.
В чём смысл разделения шаблона и стиля? Ведь потом для их совместно применения всё равно приходится их объединять. Что не так я понимаю?
23 окт 18, 13:01    [21712233]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Roman Mejtes, и ещё такой вопрос.
Как я понял из дефолтного шаблона ListBoxItem, встроенный Border имеет имя BD. И его стиль не связан со стилем ControlTemplate. Поэтому приходится создавать свой шаблон где эта связь есть.
Верно ли, что в случае такого дефолтного шаблона невозможно получить доступ к свойствам встроенного Border ? И создание своего шаблона - обязательно?
23 окт 18, 13:08    [21712244]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 3171
Как говнякать XAML файлы обычно определено на уровне политики компании или личных, не претендую на истину, пишу !очевидные вещи!.

Сам я, как правило, разделяю XAML файл на 3 группы. Сразу скажу, что при этом я не использую CodeBehind классы, такие как Window и UserControl, потому, что мне это не очень удобно, но всё это относится и к обычному подходу, которые предлагает нам VS изначально. Файл формы\страницы\user control'а, такие файлы относятся к 1 группе. Да и можно комбинировать оба подхода.
Все файлы во всех 3 группа, у меня, имеют корневой элемент ResourceDictionary. Которые собраны в некую иерархию ресурсов приложения с корнем в App.xaml файле. За создание форм, отвечает простенький класс, которые знает, для какого окна какой корневой шаблон отображения мне в нём нужно отобразить.

1) ресурсные xaml файлы с макетом приложения, это файлы которые содержат в основном шаблоны данных (модели представления) DataTemplate'ы. Для каждого DataTemplate'а указано свойство DataType. Никаких ресурсов, шаблонов или стилей, в идеале, в этом файле быть не должно. В шаблоне данных я определяю расположение всех элементов управления (макет), Binding'и свойств и команд к модели представления, DataTrigger'ы, ссылки на ресурсы приложения, вполне допустимы конвертеры, особенно если они используются только в этом файле.
Так как в макете определено только это, он легко читается, не перегружен ненужными стилями и шаблонами. Мне совершенно без разницы как будет выглядеть кнопка, главное знать, как она расположена в макете, с какой командой связана и что на ней отображается.

При таком подходе для каждой модели я создаю отдельный файл, то есть файл DataTemplates\Person.xaml будет отображать шаблон для объекта класса Person, в котором будет отображаться свойство Person.DisplayName или какое то еще, в зависимости от названия шаблона. В файле может быть несколько шаблонов отображения Person с датой рождения, с телефоном и так далее. Каждый из них я могу применять в списке, дереве, комбобоксе и легко их переключать, делает интерфейс более динамичным.

2) вторая группа это ресурсные файлы стилей в которых я храню элементы Style, ControlTemplate, Storyboard. Как правило, на 1 xaml файл, названный в честь соответствующего типа элемента управления (к примеру, ProgressBar), в нём будет один стиль и один шаблон, определяющий внешний вид этого элемента управления. Если стилей необходимо несколько, можно и в 1 файл всё упихать, найти будет не очень сложно. В этих же файлах ресурсах можно хранить анимацию которые вы используйте в стиле\шаблоне и т.д.

В этих файлах я максимально абстрагируюсь от модели представления. Ведь совершенно не важно в какой модели отображается кнопка или другой элемент управления. То есть на уровне описания стиля и шаблона я не должен думать, в какой модели этот стиль будет применяться. Он должен быть применим везде. Связывание можно определять только на уровне визуального дерева, между свойствами других элементов управления и само собой собственных свойств определяющих состояние, типа IsMouseOver или IsEnable

Так как для каждого элемента управления у меня свой файл или группа файлов, мне не приходится долго искать тот участок кода, который отвечает за внешний вид элемента управления. Мне не нужно будет копаться во всем дереве или использовать какие то примочки типа VisualTree Explorer'а и прочей ерунды (в крайних случаях), копировать стили и шаблоны в разные файлы

Таким же образом, в библиотеках с элементами управления (контролами) стиль по умолчанию и его шаблон хранятся в Themes\Generic.xaml (как корневой xaml файл). И могут быть переопределены.

3) файлы ресурсов, в которых хранится наборы строк локализации интерфейса, кисти, геометрию для иконок, другие ресурсы, которые как правило подключаются как динамические.

Всё это в конечном счете объединяется через MergeDictionary. При этом нужно помнить, что сами по себе ресурсы не создаются, когда загружен XAML ResourceDictionary, ресурс загружается только в момент обращения к нему.

Есть у такого подхода некоторые минусы:

Шаблон данных сам по себе не является элементом управления и его внутреннее состояние элементов управление (к примеру, фокус на кнопки) определен внутри ContentPresenter'а, если ControlPresenter пересоздаст свое визуальное дерево на основе шаблона, это состояние кнопки будет потеряно, ведь появится совершенно другая кнопка. Для примера, смотрите как работает TabControl. Но этот всё решаемо.
24 окт 18, 13:24    [21713444]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Roman Mejtes
Как говнякать XAML файлы обычно определено на уровне политики компании или личных, не претендую на истину, пишу !очевидные вещи!.

Сам я, как правило, разделяю XAML файл на 3 группы. ..........
Огромное спасибо за такой подробный и развёрнутый ответ!
24 окт 18, 13:46    [21713495]     Ответить | Цитировать Сообщить модератору
 Re: MultiDataTrigger в ListBoxItem  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19959
Раз уж тут мастер-класс, позволю себе напомнить
Roman Mejtes
Я готовлю маленькую статью на эту тему :( готово процентов на 50, когда закончу пока не знаю, давно обещал
Суть в том, что на базе представления коллекции иерархия разворачивается в список, для отображения дерева можно использовать любые списочные контролы. То есть добавить дерево можно в combobox, listbox, datagrid с минимальными затратами и переработками. работает быстрее, поддерживает мультиселект, иерархические команды, вставку элементов в любую ветку и многое другое.
24 окт 18, 14:06    [21713544]     Ответить | Цитировать Сообщить модератору
Все форумы / WPF, Silverlight Ответить