如何使用ViewModel中的布尔属性启用/禁用附加行为

时间:2016-06-03 14:11:08

标签: c# wpf attached-properties attachedbehaviors rubber-band

标题说明了我要做的事情。这就是我所拥有的:

我正在使用this CodeProject贡献将RubberBand行为附加到ListBox,以便我可以使用鼠标进行拖动选择。我能够修改它,以便我可以在实例化ListBox期间禁用它,因为我需要ListBox是非交互式的,只显示项目。

ListBox嵌入在UserControl中并包含一个显示元素的画布,在我的程序的一个部分中,我需要UserControl是这些元素的非交互式表示,而在另一个中我需要它是交互式的。但是现在,我需要能够在这两种状态之间切换,不幸的是,这与我有ATM的实现无关,我不明白为什么。

我已将附加属性'IsActive'绑定到我的UserControl-ViewModel的属性'IsEditable'中,我将修改后的RubberBand版本(请参阅下面的代码)添加到该属性中,但由于某种原因,方法'IsActiveProperty_Changed'没有当'IsEditable'改变时执行。

这是我使用行为并绑定到'IsEditable':

--deploy-cluster

我也试过这个,这也行不通:

<i:Interaction.Behaviors>
    <behavior:RubberBandBehavior IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable}"/>
</i:Interaction.Behaviors>

要禁用ListBox的命中检测,我还绑定到'IsEditable',其中 正常工作:

<behavior:RubberBandBehavior IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable, UpdateSourceTrigger=PropertyChanged}"/>

因此我怀疑它与我对RubberBandBehavior的实施/修改有关,因为我仍然没有实施附加属性的经验。我希望有人能发现我的错误。

修改后的RubberBandBehavior.cs

        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">

                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable}" Value="False">
                        <Setter Property="IsHitTestVisible" Value="False" />
                        <Setter Property="Focusable" Value="False" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListBox.ItemContainerStyle>

RubberBandAdorner.cs:

public class RubberBandBehavior : Behavior<ListBox>
{
    private RubberBandAdorner band;
    private AdornerLayer adornerLayer;

    public static readonly DependencyProperty IsActiveProperty =
        DependencyProperty.Register("IsActive", typeof(bool), typeof(RubberBandBehavior),
        new PropertyMetadata(IsActiveProperty_Changed));

    private static void IsActiveProperty_Changed(DependencyObject sender,
        DependencyPropertyChangedEventArgs args)
    {
        RubberBandBehavior rubberBandBehavior = (RubberBandBehavior)sender;

        if (args.Property.Name == "IsActive")
        {
            bool newIsActiveValue = (bool)args.NewValue;
            bool oldIsActiveValue = (bool)args.OldValue;

            if (newIsActiveValue != oldIsActiveValue)
            {
                rubberBandBehavior.IsActive = newIsActiveValue;

                if (rubberBandBehavior.AssociatedObject != null)
                {
                    if (newIsActiveValue == true)
                    {
                        rubberBandBehavior.AttachBehavior();
                    }
                    else
                    {
                        rubberBandBehavior.DetachBehavior();
                    }
                }
            }
        }
    }

    public bool IsActive
    {
        get { return (bool)GetValue(IsActiveProperty); }
        set { SetValue(IsActiveProperty, value); }
    }

    protected override void OnAttached()
    {
        AssociatedObject.Loaded += new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
        base.OnAttached();
    }

    void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        if (IsActive == true)
        {
            AttachBehavior();
        }
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
        base.OnDetaching();
    }

    private void AttachBehavior()
    {
        band = new RubberBandAdorner(AssociatedObject);
        adornerLayer = AdornerLayer.GetAdornerLayer(AssociatedObject);
        adornerLayer.Add(band);
    }

    private void DetachBehavior()
    {
        adornerLayer.Remove(band);
    }
}

更新

以下是ViewModel的public class RubberBandAdorner : Adorner { private Point startpoint; private Point currentpoint; private Brush brush; private bool flag; private ScrollViewer viewer; private ScrollBar scrollbar; public RubberBandAdorner(UIElement adornedElement) :base(adornedElement) { IsHitTestVisible = false; adornedElement.MouseMove += new MouseEventHandler(adornedElement_PreviewMouseMove); adornedElement.MouseLeftButtonDown += new MouseButtonEventHandler(adornedElement_PreviewMouseLeftButtonDown); adornedElement.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(adornedElement_PreviewMouseLeftButtonUp); brush = new SolidColorBrush(SystemColors.HighlightColor); brush.Opacity = 0.3; } void adornedElement_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { DisposeRubberBand(); } void adornedElement_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { ListBox _selector = AdornedElement as ListBox; if (_selector.SelectedItems != null && (_selector.SelectionMode == SelectionMode.Extended || _selector.SelectionMode == SelectionMode.Multiple)) { _selector.SelectedItems.Clear(); } startpoint = Mouse.GetPosition(this.AdornedElement); Mouse.Capture(_selector); flag = true; } public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject { // Search immediate children first (breadth-first) for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is childItem) return (childItem)child; else { childItem childOfChild = FindVisualChild<childItem>(child); if (childOfChild != null) return childOfChild; } } return null; } void adornedElement_PreviewMouseMove(object sender, MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed && flag) { currentpoint = Mouse.GetPosition(AdornedElement); Selector _selector = AdornedElement as Selector; if (viewer == null) { viewer = FindVisualChild<ScrollViewer>(_selector); } if (scrollbar == null) { scrollbar = FindVisualChild<ScrollBar>(viewer); } if (_selector.Items.Count > 0) { if (currentpoint.Y > ((FrameworkElement)AdornedElement).ActualHeight && viewer.VerticalOffset < _selector.ActualHeight && scrollbar.Visibility == System.Windows.Visibility.Visible) { startpoint.Y -= 50; } else if (currentpoint.Y < 0 && viewer.VerticalOffset > 0 && scrollbar.Visibility == System.Windows.Visibility.Visible) { startpoint.Y += 50; } } InvalidateVisual(); foreach (var obj in _selector.Items) { ListBoxItem item = _selector.ItemContainerGenerator.ContainerFromItem(obj) as ListBoxItem; if (item != null) { Point point = item.TransformToAncestor(AdornedElement).Transform(new Point(0, 0)); Rect bandrect = new Rect(startpoint, currentpoint); Rect elementrect = new Rect(point.X, point.Y, item.ActualWidth, item.ActualHeight); if (bandrect.IntersectsWith(elementrect)) { item.IsSelected = true; } else { item.IsSelected = false; } } } } } protected override void OnRender(DrawingContext drawingContext) { Rect rect = new Rect(startpoint, currentpoint); drawingContext.DrawGeometry(brush, new Pen(SystemColors.HighlightBrush, 1), new RectangleGeometry(rect)); base.OnRender(drawingContext); } private void DisposeRubberBand() { currentpoint = new Point(0, 0); startpoint = new Point(0, 0); AdornedElement.ReleaseMouseCapture(); InvalidateVisual(); flag = false; } } 属性的代码。请注意,我使用的是MvvmLight中的IsEditable方法:

RaisePropertyChanged

1 个答案:

答案 0 :(得分:0)

你的问题是IsActive不是AttachedProperty,只是一个常规的DependencyProperty。

删除DP。应删除此代码:

public static readonly DependencyProperty IsActiveProperty =
        DependencyProperty.Register("IsActive", typeof(bool), typeof(RubberBandBehavior),
        new PropertyMetadata(IsActiveProperty_Changed));

public bool IsActive
{
    get { return (bool)GetValue(IsActiveProperty); }
    set { SetValue(IsActiveProperty, value); }
}

然后将IsActive添加为附加属性:

public static bool GetIsActive(DependencyObject obj)
{
    return (bool)obj.GetValue(IsActiveProperty);
}
public static void SetIsActive(DependencyObject obj, bool value)
{
    obj.SetValue(IsActiveProperty, value);
}
public static readonly DependencyProperty IsActiveProperty =
    DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(RubberBandBehavior), new PropertyMetadata(IsActiveProperty_Changed));

您还必须更改设置/获取IsActive的代码:

rubberBandBehavior.IsActive = newIsActiveValue;

变为

rubberBandBehavior.SetValue(RubberBandBehavior.IsActiveProperty, newIsActiveValue);

if (IsActive == true)

变为

if (this.GetValue(IsActiveProperty).Equals(true))

虽然,我应该提一下,没有必要执行SetValue行,因为它已经设置为newIsActiveValue ...不应该伤害任何东西,但它也没有真正做任何事情。也没有必要检查旧值和新值是否不同,如果它们没有不同,则不会调用IsActiveProperty_Changed。

编辑:

这是完整的RubberBandBehavior.cs:

public class RubberBandBehavior : Behavior<ListBox>
{
    private RubberBandAdorner band;
    private AdornerLayer adornerLayer;

    public static bool GetIsActive(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsActiveProperty);
    }
    public static void SetIsActive(DependencyObject obj, bool value)
    {
        obj.SetValue(IsActiveProperty, value);
    }
    public static readonly DependencyProperty IsActiveProperty =
        DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(RubberBandBehavior), new PropertyMetadata(IsActiveProperty_Changed));

    private static void IsActiveProperty_Changed(DependencyObject sender,
        DependencyPropertyChangedEventArgs args)
    {
        RubberBandBehavior rubberBandBehavior = (RubberBandBehavior)sender;
        if (args.Property.Name == "IsActive")
        {
            bool newIsActiveValue = (bool)args.NewValue;
            if (rubberBandBehavior.AssociatedObject != null)
            {
                if (newIsActiveValue == true)
                {
                    rubberBandBehavior.AttachBehavior();
                }
                else
                {
                    rubberBandBehavior.DetachBehavior();
                }
            }
        }
    }

    protected override void OnAttached()
    {
        AssociatedObject.Loaded += new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
        base.OnAttached();
    }

    void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        if (this.GetValue(IsActiveProperty).Equals(true))
        {
            AttachBehavior();
        }
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
        base.OnDetaching();
    }

    private void AttachBehavior()
    {
        band = new RubberBandAdorner(AssociatedObject);
        adornerLayer = AdornerLayer.GetAdornerLayer(AssociatedObject);
        adornerLayer.Add(band);
    }

    private void DetachBehavior()
    {
        adornerLayer.Remove(band);
    }
}

RubberBandAdorner正在使用ContainerFromItem,当您的项目相同时(例如,具有相同文本的字符串列表),它将无法工作。我修改了代码以使用ContainerFromIndex。外部foreach已经变为for循环。

在RubberBandAdorner.cs中,在adornedElement_PreviewMouseMove方法中更新部分代码:

//foreach (var obj in _selector.Items)
//{
//    ListBoxItem item = _selector.ItemContainerGenerator.ContainerFromItem(obj) as ListBoxItem;
for (int i=0; i<_selector.Items.Count; i++)
{
    ListBoxItem item =_selector.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
    if (item != null)
    {
        Point point = item.TransformToAncestor(AdornedElement).Transform(new Point(0, 0));
        Rect bandrect = new Rect(startpoint, currentpoint);
        Rect elementrect = new Rect(point.X, point.Y, item.ActualWidth, item.ActualHeight);
        if (bandrect.IntersectsWith(elementrect))
        {
            item.IsSelected = true;
        }
        else
        {
            item.IsSelected = false;
        }
    }
}