2

标题说明了我正在尝试做的事情。这是我所拥有的:

我正在使用CodeProject 贡献将 RubberBand 行为附加到 ListBox,以便我可以使用鼠标进行拖动选择。我能够对其进行修改,以便在 ListBox 的实例化过程中禁用它,以便在我需要 ListBox 非交互式且仅显示项目时禁用它。

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

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

这是我正在使用行为并绑定到“IsEditable”:

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

我也试过这个,它也不起作用:

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

要禁用 ListBox 的命中检测,我还绑定到“IsEditable”,它确实可以正常工作:

        <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>

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

修改后的 RubberBandBehavior.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);
    }
}

RubberBandAdorner.cs:

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

更新:

这是IsEditableViewModel 属性的代码。请注意,我使用的RaisePropertyChanged是 MvvmLight 中的方法:

private bool isEditable;
public bool IsEditable
{
    get { return isEditable; }
    set {
        if(value != isEditable)
        {
            isEditable = value;
            RaisePropertyChanged("IsEditable");
        }
    }
}
4

1 回答 1

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;
        }
    }
}
于 2016-06-03T16:14:29.903 回答