Как установить поведение для каждого ListViewItem, сгенерированного родительским ListView? [Дубликат]

Можете ли вы просто обернуть его одним способом? В соответствии с документами

: if - указывает метод, proc или строку для вызова, чтобы определить, должна ли выполняться проверка (например, если:: allow_validation или если: Proc.new {| user | user.signup_step> 2}). Метод, proc или string должен возвращать или оценивать истинное или ложное значение.

blockquote>
validates :description, presence: true, if: :some_validation_check

def some_validation_check
    first_step? || require_validation
end

77
задан Eric 26 November 2012 в 10:05
поделиться

9 ответов

У меня есть другая идея, чтобы избежать создания прикрепленного свойства для каждого поведения:

  1. Интерфейс создателя поведения:
    public interface IBehaviorCreator
    {
        Behavior Create();
    }
    
  2. Небольшой сбор помощников:
    public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { }
    
  3. Класс-помощник, который присоединяет поведение:
    public static class BehaviorInStyleAttacher
    {
        #region Attached Properties
    
        public static readonly DependencyProperty BehaviorsProperty =
            DependencyProperty.RegisterAttached(
                "Behaviors",
                typeof(BehaviorCreatorCollection),
                typeof(BehaviorInStyleAttacher),
                new UIPropertyMetadata(null, OnBehaviorsChanged));
    
        #endregion
    
        #region Getter and Setter of Attached Properties
    
        public static BehaviorCreatorCollection GetBehaviors(TreeView treeView)
        {
            return (BehaviorCreatorCollection)treeView.GetValue(BehaviorsProperty);
        }
    
        public static void SetBehaviors(
            TreeView treeView, BehaviorCreatorCollection value)
        {
            treeView.SetValue(BehaviorsProperty, value);
        }
    
        #endregion
    
        #region on property changed methods
    
        private static void OnBehaviorsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue is BehaviorCreatorCollection == false)
                return;
    
            BehaviorCreatorCollection newBehaviorCollection = e.NewValue as BehaviorCreatorCollection;
    
            BehaviorCollection behaviorCollection = Interaction.GetBehaviors(depObj);
            behaviorCollection.Clear();
            foreach (IBehaviorCreator behavior in newBehaviorCollection)
            {
                behaviorCollection.Add(behavior.Create());
            }
        }
    
        #endregion
    }
    
  4. Теперь ваше поведение, которое реализует IBehaviorCreator:
    public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell>, IBehaviorCreator
    {
        //some code ...
    
        public Behavior Create()
        {
            // here of course you can also set properties if required
            return new SingleClickEditDataGridCellBehavior();
        }
    }
    
  5. И теперь используйте его в xaml:
    <Style TargetType="{x:Type DataGridCell}">
      <Setter Property="helper:BehaviorInStyleAttacher.Behaviors" >
        <Setter.Value>
          <helper:BehaviorCreatorCollection>
            <behaviors:SingleClickEditDataGridCellBehavior/>
          </helper:BehaviorCreatorCollection>
        </Setter.Value>
      </Setter>
    </Style>
    
7
ответ дан Andi 21 August 2018 в 19:28
поделиться

Объявить индивидуальное поведение / триггер как ресурсы:

<Window.Resources>

    <i:EventTrigger x:Key="ET1" EventName="Click">
        <ei:ChangePropertyAction PropertyName="Background">
            <ei:ChangePropertyAction.Value>
                <SolidColorBrush Color="#FFDAD32D"/>
            </ei:ChangePropertyAction.Value>
        </ei:ChangePropertyAction>
    </i:EventTrigger>

</Window.Resources>

Вставить их в коллекцию:

<Button x:Name="Btn1" Content="Button">

        <i:Interaction.Triggers>
             <StaticResourceExtension ResourceKey="ET1"/>
        </i:Interaction.Triggers>

</Button>
1
ответ дан AnjumSKhan 21 August 2018 в 19:28
поделиться
  • 1
    Как он отвечает OP? Триггер не добавляется через стиль в вашем ответе. – Kryptos 11 September 2017 в 06:09

В статье Введение в Attached Behaviors в WPF реализуется связанное поведение с использованием только стиля, а также может быть связано или полезно.

Техника в «Введении к приложенным поведением» "статья избегает тегов интерактивности, используя стиль. Я не знаю, является ли это только потому, что это более датированная методика, или, если это еще приносит некоторые преимущества, когда в некоторых сценариях это следует предпочесть.

0
ответ дан Bill 21 August 2018 в 19:28
поделиться
  • 1
    Это не поведение Blend, это «поведение». через простое присоединенное свойство. – Stephen Drew 8 July 2012 в 23:30

Я не смог найти оригинальную статью, но мне удалось восстановить эффект.

#region Attached Properties Boilerplate

    public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(ScrollIntoViewBehavior), new PropertyMetadata(false, OnIsActiveChanged));

    public static bool GetIsActive(FrameworkElement control)
    {
        return (bool)control.GetValue(IsActiveProperty);
    }

    public static void SetIsActive(
      FrameworkElement control, bool value)
    {
        control.SetValue(IsActiveProperty, value);
    }

    private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behaviors = Interaction.GetBehaviors(d);
        var newValue = (bool)e.NewValue;

        if (newValue)
        {
            //add the behavior if we don't already have one
            if (!behaviors.OfType<ScrollIntoViewBehavior>().Any())
            {
                behaviors.Add(new ScrollIntoViewBehavior());
            }
        }
        else
        {
            //remove any instance of the behavior. (There should only be one, but just in case.)
            foreach (var item in behaviors.ToArray())
            {
                if (item is ScrollIntoViewBehavior)
                    behaviors.Remove(item);
            }
        }
    }


    #endregion
<Style TargetType="Button">
    <Setter Property="Blah:ScrollIntoViewBehavior.IsActive" Value="True" />
</Style>
4
ответ дан H.B. 21 August 2018 в 19:28
поделиться
  • 1
    Однако, чтобы написать это для каждого поведения, это немного PITA. – Stephen Drew 8 July 2012 в 23:39

Мне нравится подход, показанный в ответах Романа Двоскина и Джонатана Аллена в этой теме. Когда я впервые изучил эту технику, я воспользовался этой записью в блоге , которая дает больше объяснений по поводу этой техники. И чтобы увидеть все в контексте, здесь весь исходный код для класса, о котором говорит автор в своем сообщении в блоге.

0
ответ дан Jason Frank 21 August 2018 в 19:28
поделиться

Код поведения ожидает Visual, поэтому мы можем добавить его только на визуальный. Поэтому единственный вариант, который я мог видеть, - это добавить к одному из элементов внутри ControlTemplate, чтобы получить поведение, добавленное к стилю, и повлиять на все экземпляры определенного элемента управления.

0
ответ дан Jobi Joy 21 August 2018 в 19:28
поделиться

У меня была такая же проблема, и я придумал решение. Я нашел этот вопрос после того, как решил его, и вижу, что мое решение имеет много общего с Марком. Однако этот подход немного отличается.

Основная проблема заключается в том, что поведение и триггеры ассоциируются с определенным объектом, поэтому вы не можете использовать один и тот же экземпляр поведения для нескольких разных связанных объектов. Когда вы определяете свое поведение, встроенный XAML обеспечивает это взаимно-однозначное отношение. Однако, когда вы пытаетесь установить поведение в стиле, стиль может быть повторно использован для всех объектов, к которым он применяется, и это будет генерировать исключения в базовых классах поведения. На самом деле авторы приложили немало усилий, чтобы помешать нам даже попытаться это сделать, зная, что это не сработает.

Первая проблема заключается в том, что мы даже не можем построить значение setterter, потому что конструктор внутренний. Поэтому нам нужно собственное поведение и триггерные классы.

Следующая проблема заключается в том, что свойства поведения и триггера не имеют сеттеров, поэтому их можно добавить только с помощью встроенного XAML. Эта проблема мы решаем с помощью наших собственных подключенных свойств, которые управляют первичным поведением и свойствами запуска.

Третья проблема заключается в том, что наша коллекция поведения хороша только для единственной цели стиля. Это мы решаем, используя малоиспользуемую функцию XAML x:Shared="False", которая создает новую копию ресурса каждый раз, когда на нее ссылаются.

Конечная проблема заключается в том, что поведение и триггеры не похожи на другие стилисты; мы не хотим заменять старые поведения новым поведением, потому что они могут делать дико разные вещи. Поэтому, если мы согласны с тем, что после добавления поведения вы не можете его убрать (и в настоящее время это поведение поведения), мы можем заключить, что поведение и триггеры должны быть аддитивными, и это может быть обработано нашими приложенными свойствами.

Вот пример с использованием этого подхода:

<Grid>
    <Grid.Resources>
        <sys:String x:Key="stringResource1">stringResource1</sys:String>
        <local:Triggers x:Key="debugTriggers" x:Shared="False">
            <i:EventTrigger EventName="MouseLeftButtonDown">
                <local:DebugAction Message="DataContext: {0}" MessageParameter="{Binding}"/>
                <local:DebugAction Message="ElementName: {0}" MessageParameter="{Binding Text, ElementName=textBlock2}"/>
                <local:DebugAction Message="Mentor: {0}" MessageParameter="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}"/>
            </i:EventTrigger>
        </local:Triggers>
        <Style x:Key="debugBehavior" TargetType="FrameworkElement">
            <Setter Property="local:SupplementaryInteraction.Triggers" Value="{StaticResource debugTriggers}"/>
        </Style>
    </Grid.Resources>
    <StackPanel DataContext="{StaticResource stringResource1}">
        <TextBlock Name="textBlock1" Text="textBlock1" Style="{StaticResource debugBehavior}"/>
        <TextBlock Name="textBlock2" Text="textBlock2" Style="{StaticResource debugBehavior}"/>
        <TextBlock Name="textBlock3" Text="textBlock3" Style="{StaticResource debugBehavior}"/>
    </StackPanel>
</Grid>

В примере используются триггеры, но поведение работает одинаково. В этом примере мы показываем:

  • стиль может применяться к нескольким текстовым блокам
  • несколько типов привязки данных, которые все работают правильно
  • отладка действие, которое генерирует текст в окне вывода

Вот пример поведения, наш DebugAction. Более корректно это действие, но из-за злоупотребления языком мы называем поведение, триггеры и действия «поведения».

public class DebugAction : TriggerAction<DependencyObject>
{
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(DebugAction), new UIPropertyMetadata(""));

    public object MessageParameter
    {
        get { return (object)GetValue(MessageParameterProperty); }
        set { SetValue(MessageParameterProperty, value); }
    }

    public static readonly DependencyProperty MessageParameterProperty =
        DependencyProperty.Register("MessageParameter", typeof(object), typeof(DebugAction), new UIPropertyMetadata(null));

    protected override void Invoke(object parameter)
    {
        Debug.WriteLine(Message, MessageParameter, AssociatedObject, parameter);
    }
}

Наконец, наши коллекции и прикрепленные свойства, чтобы все это работало. По аналогии с Interaction.Behaviors целевое свойство называется SupplementaryInteraction.Behaviors, потому что, установив это свойство, вы добавите поведения к Interaction.Behaviors и аналогичным образом для триггеров.

public class Behaviors : List<Behavior>
{
}

public class Triggers : List<TriggerBase>
{
}

public static class SupplementaryInteraction
{
    public static Behaviors GetBehaviors(DependencyObject obj)
    {
        return (Behaviors)obj.GetValue(BehaviorsProperty);
    }

    public static void SetBehaviors(DependencyObject obj, Behaviors value)
    {
        obj.SetValue(BehaviorsProperty, value);
    }

    public static readonly DependencyProperty BehaviorsProperty =
        DependencyProperty.RegisterAttached("Behaviors", typeof(Behaviors), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyBehaviorsChanged));

    private static void OnPropertyBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behaviors = Interaction.GetBehaviors(d);
        foreach (var behavior in e.NewValue as Behaviors) behaviors.Add(behavior);
    }

    public static Triggers GetTriggers(DependencyObject obj)
    {
        return (Triggers)obj.GetValue(TriggersProperty);
    }

    public static void SetTriggers(DependencyObject obj, Triggers value)
    {
        obj.SetValue(TriggersProperty, value);
    }

    public static readonly DependencyProperty TriggersProperty =
        DependencyProperty.RegisterAttached("Triggers", typeof(Triggers), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyTriggersChanged));

    private static void OnPropertyTriggersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var triggers = Interaction.GetTriggers(d);
        foreach (var trigger in e.NewValue as Triggers) triggers.Add(trigger);
    }
}

и там у вас есть , полнофункциональное поведение и триггеры, применяемые через стили.

71
ответ дан Rick Sladkey 21 August 2018 в 19:28
поделиться
  • 1
    Отличный материал, это прекрасно работает. Я заметил, что если вы поместите стиль, например, в ресурсы UserControl, то e.NewValue может сначала иметь значение null (может зависеть от используемого элемента управления - я использую это в XamDataTreeNodeControl в Infragistics XamDataTree). Поэтому я добавил небольшую проверку работоспособности в OnPropertyTriggersChanged: if (e.NewValue! = Null) – MetalMikester 9 December 2011 в 19:10
  • 2
    У кого-нибудь была проблема с этим подходом при применении Setter в неявном стиле ? Я получил его для работы с неявным стилем (один с ключом), но я получаю исключение циклической ссылки, если оно в неявном стиле. – Jason Frank 1 April 2013 в 16:35
  • 3
    Хорошее решение, но, к сожалению, оно не работает в WinRT, потому что x: Shared не существует на этой платформе ... – Thomas Levesque 6 July 2013 в 02:54
  • 4
    Я могу подтвердить, что это решение работает. Большое спасибо за то, что поделились ею. Я еще не пробовал это с неявным стилем. – Golvellius 6 September 2013 в 15:40
  • 5
    @ Джейсон Фрэнк, спасибо, так же, как ссылки для других ... Я сделал это работает в обоих случаях: неявный и явный. На самом деле я задаю вопрос, где бы я поместил весь свой код, чтобы помогать другим, но кто-то оценивает, что мой вопрос был дубликатом. Я не могу ответить на свой вопрос, давая все, что нашел. Я думаю, что я обнаруживаю довольно приятные вещи. :-( ... Надеюсь, это не происходит слишком часто, потому что это поведение лишает других пользователей полезной информации. – Eric Ouellet 11 February 2016 в 18:01

Суммируя ответы и эту замечательную статью Blend Behaviors in Styles , я пришел к этому родовому короткому и удобному решению:

Я сделал общий класс, который может быть унаследован любым поведением .

public class AttachableForStyleBehavior<TComponent, TBehavior> : Behavior<TComponent>
        where TComponent : System.Windows.DependencyObject
        where TBehavior : AttachableForStyleBehavior<TComponent, TBehavior> , new ()
    {
        public static DependencyProperty IsEnabledForStyleProperty =
            DependencyProperty.RegisterAttached("IsEnabledForStyle", typeof(bool),
            typeof(AttachableForStyleBehavior<TComponent, TBehavior>), new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged)); 

        public bool IsEnabledForStyle
        {
            get { return (bool)GetValue(IsEnabledForStyleProperty); }
            set { SetValue(IsEnabledForStyleProperty, value); }
        }

        private static void OnIsEnabledForStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement uie = d as UIElement;

            if (uie != null)
            {
                var behColl = Interaction.GetBehaviors(uie);
                var existingBehavior = behColl.FirstOrDefault(b => b.GetType() ==
                      typeof(TBehavior)) as TBehavior;

                if ((bool)e.NewValue == false && existingBehavior != null)
                {
                    behColl.Remove(existingBehavior);
                }

                else if ((bool)e.NewValue == true && existingBehavior == null)
                {
                    behColl.Add(new TBehavior());
                }    
            }
        }
    }

Таким образом, вы можете просто повторно использовать его с большим количеством компонентов, таких как:

public class ComboBoxBehaviour : AttachableForStyleBehavior<ComboBox, ComboBoxBehaviour>
    { ... }

И в XAML достаточно объявить:

 <Style TargetType="ComboBox">
            <Setter Property="behaviours:ComboBoxBehaviour.IsEnabledForStyle" Value="True"/>

Так что класс AttachableForStyleBehavior сделал вещи xaml, зарегистрировав экземпляр поведения для каждого компонента в стиле. Для получения дополнительной информации см. Ссылку.

18
ответ дан Roma Borodov 21 August 2018 в 19:28
поделиться
  • 1
    Работает как шарм! С моим скроллинг-поведением в сочетании я избавился от Inner RowDetailsTemplate-Datagrid, не прокручивая родительские Datagrids. – Philipp Michalski 28 July 2015 в 11:44
  • 2
    Рад помочь, наслаждайтесь =) – Roma Borodov 6 December 2015 в 17:28
  • 3
    как насчет привязки данных к свойствам зависимостей в Поведении? – JobaDiniz 2 December 2016 в 12:09
  • 4
    – Roma Borodov 6 April 2018 в 14:44
  • 5
    @RomaBorodov, все работает в XAML. Это правильный способ определить прикрепленное свойство (которое отличается от свойства зависимостей). См. Документацию: docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/… – Der_Meister 6 April 2018 в 15:24

1.Создать прикрепленное свойство

public static class DataGridCellAttachedProperties
{
    //Register new attached property
    public static readonly DependencyProperty IsSingleClickEditModeProperty =
        DependencyProperty.RegisterAttached("IsSingleClickEditMode", typeof(bool), typeof(DataGridCellAttachedProperties), new UIPropertyMetadata(false, OnPropertyIsSingleClickEditModeChanged));

    private static void OnPropertyIsSingleClickEditModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGridCell = d as DataGridCell;
        if (dataGridCell == null)
            return;

        var isSingleEditMode = GetIsSingleClickEditMode(d);
        var behaviors =  Interaction.GetBehaviors(d);
        var singleClickEditBehavior = behaviors.SingleOrDefault(x => x is SingleClickEditDataGridCellBehavior);

        if (singleClickEditBehavior != null && !isSingleEditMode)
            behaviors.Remove(singleClickEditBehavior);
        else if (singleClickEditBehavior == null && isSingleEditMode)
        {
            singleClickEditBehavior = new SingleClickEditDataGridCellBehavior();
            behaviors.Add(singleClickEditBehavior);
        }
    }

    public static bool GetIsSingleClickEditMode(DependencyObject obj)
    {
        return (bool) obj.GetValue(IsSingleClickEditModeProperty);
    }

    public static void SetIsSingleClickEditMode(DependencyObject obj, bool value)
    {
        obj.SetValue(IsSingleClickEditModeProperty, value);
    }
}

2.Создать поведение

public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell>
        {
            protected override void OnAttached()
            {
                base.OnAttached();
                AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown;
            }

            protected override void OnDetaching()
            {
                base.OnDetaching();
                AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown;
            }

            void DataGridCellPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                 DataGridCell cell = sender as DataGridCell;
                if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
                {
                    if (!cell.IsFocused)
                    {
                        cell.Focus();
                    }
                    DataGrid dataGrid = LogicalTreeWalker.FindParentOfType<DataGrid>(cell); //FindVisualParent<DataGrid>(cell);
                    if (dataGrid != null)
                    {
                        if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
                        {
                            if (!cell.IsSelected)
                                cell.IsSelected = true;
                        }
                        else
                        {
                            DataGridRow row =  LogicalTreeWalker.FindParentOfType<DataGridRow>(cell); //FindVisualParent<DataGridRow>(cell);
                            if (row != null && !row.IsSelected)
                            {
                                row.IsSelected = true;
                            }
                        }
                    }
                }
            }    
        }

3.Создать стиль и установить прикрепленное свойство

        <Style TargetType="{x:Type DataGridCell}">
            <Setter Property="Behaviors:DataGridCellAttachedProperties.IsSingleClickEditMode" Value="True"/>
        </Style>
18
ответ дан Roman Dvoskin 21 August 2018 в 19:28
поделиться
  • 1
    Когда я пытаюсь получить доступ к DependencyProperty из стиля, он говорит, что IsSingleClickEditMode не распознан или недоступен? – Igor Meszaros 2 February 2017 в 12:36
  • 2
    Извините, мой плохой .. как только я прокомментировал, я понял, что GetIsSingleClickEditMode должен соответствовать строке, которую вы передаете DependencyProperty.RegisterAttached – Igor Meszaros 2 February 2017 в 12:40
Другие вопросы по тегам:

Похожие вопросы: