The title says what I am attempting to do. Here is what I have:
I am using this CodeProject contribution to attach a RubberBand behavior to a ListBox, so that I can drag-select using the mouse. I was able to modify it, so that I can disable it during instantiation of the ListBox for when I need the ListBox to be non-interactive and only show items.
The ListBox is embeded in a UserControl and contains a canvas that displays elements and in one section of my program I needed the UserControl to be a non-interactive representation of those elements, whereas in the other I needed it to be interactive. However now, I need to be able to toggle between these two states and unfortunately that does not work with the implementation I have ATM and I do not understand why.
I have bound the attached property 'IsActive', which I added in my modified RubberBand-version (see code below) to the property 'IsEditable' of my UserControl-ViewModel, but for some reason the method 'IsActiveProperty_Changed' does not execute, when 'IsEditable' changes.
This is I am using the behavior and binding to 'IsEditable':
<i:Interaction.Behaviors>
<behavior:RubberBandBehavior IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable}"/>
</i:Interaction.Behaviors>
I have also tried this, which also does not work:
<behavior:RubberBandBehavior IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable, UpdateSourceTrigger=PropertyChanged}"/>
To disable the hit-detection of the ListBox, I am also binding to 'IsEditable', which does work fine:
<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>
I therefore suspect, that it has to do with my implementation/modification of RubberBandBehavior, since I am still unexperienced to implementing Attached Properties. I hope somebody can spot my error.
Modified 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;
}
}
Update:
Here is the code for the IsEditable
property of the ViewModel. Note that I am using the RaisePropertyChanged
method from MvvmLight:
private bool isEditable;
public bool IsEditable
{
get { return isEditable; }
set {
if(value != isEditable)
{
isEditable = value;
RaisePropertyChanged("IsEditable");
}
}
}