I'm implementing a way to rename elements in a treeview. My thought was not to build a special TreeView, but instead a special Label which can also be used in ListBox and other controls. It sets the focus to itself by clicking it, so I changed Focusable to true. If focused it gets editable by [F2] and when it gets clicked while already having focus. Editable means, a text box shows (all similar to Windows Explorer). Unfortunately the TreeViewItem does not get selected, when I click the label. When clicking the Icon to the left, the TreeViewItem is selected.
I do not handle any event setting Handled=true, but I think the Label itself handles the click, so the treeviewItem does not get the opportunity to react to it. If I am right here, is there a way to modify the label (own class?) in a way it does not handle the event at all?
I rethought my attempt. The solution is not to take the focus at all. Instead I bound a handler to the KeyUp event of the observed element currently having the focus (see the comment in EditableLabel.cs).
EditableLabel.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:myown.Wpf"
xmlns:localCtrls="clr-namespace:myown.Wpf.Controls">
<Style TargetType="{x:Type localCtrls:EditableLabel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type localCtrls:EditableLabel}">
<StackPanel>
<Label x:Name="label"
Content="{TemplateBinding Text}"
Visibility="Visible"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="{TemplateBinding Padding}"
Foreground="{TemplateBinding Foreground}"/>
<!-- TemplateBinding is always OneWay! -->
<TextBox x:Name="textbox"
Text="{Binding Path=Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Visibility="Collapsed"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="{TemplateBinding Padding}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
EditableLabel.cs:
public partial class EditableLabel : Label
{
#region DependencyProperties
public static readonly DependencyProperty IsEditingProperty =
DependencyProperty.Register("IsEditing", typeof(bool), typeof(EditableLabel),
new UIPropertyMetadata(false, IsEditingUpdate));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(EditableLabel),
new FrameworkPropertyMetadata("",
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool IsEditing
{
get { return (bool)GetValue(IsEditingProperty); }
set { SetValue(IsEditingProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private string m_lastValue = "";
private IInputElement m_lastFocus = null;
private Label m_label = null;
private TextBox m_textBox = null;
#endregion
static EditableLabel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EditableLabel),
new FrameworkPropertyMetadata(typeof(EditableLabel)));
}
public EditableLabel()
{
LostFocus += (s, e) => { };
LostKeyboardFocus += EditableLabel_LostKeyboardFocus;
}
private void EditableLabel_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (e.NewFocus != null)
Debug.WriteLine(e.NewFocus.GetType().Name);
}
public override void OnApplyTemplate()
{
m_label = GetTemplateChild("label") as Label;
m_textBox = GetTemplateChild("textbox") as TextBox;
m_label.MouseUp += Label_MouseUp;
m_textBox.LostFocus += (s, e) => IsEditing = false;
m_textBox.KeyUp += (s, e) => { TextBox_KeyUp(s, e); e.Handled = true; };
m_textBox.GotKeyboardFocus += (s, e) => m_textBox.SelectAll();
base.OnApplyTemplate();
}
private void Label_MouseUp(object sender, MouseButtonEventArgs e)
{
DependencyObject scope = FocusManager.GetFocusScope(this);
IInputElement currFocus = FocusManager.GetFocusedElement(scope);
if ((currFocus != null))
{
if (currFocus == m_lastFocus)
{
IsEditing = true;
}
else
{
m_lastFocus = currFocus;
DependencyObject observedObject = currFocus as DependencyObject;
if (observedObject != null)
{
m_lastFocus.LostKeyboardFocus += ObservedElementLostFocus;
m_lastFocus.KeyUp += ObservedElement_KeyUp; // THIS IS THE TRICK
}
}
}
}
private void ObservedElement_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.F2)
{
IsEditing = true;
e.Handled = true;
}
}
private void ObservedElementLostFocus(object sender, RoutedEventArgs e)
{
KeyboardFocusChangedEventArgs kfc = e as KeyboardFocusChangedEventArgs;
if (kfc.NewFocus != null)
{
if ((kfc.NewFocus != m_textBox) && (kfc.NewFocus != m_lastFocus))
{
m_lastFocus.LostKeyboardFocus -= ObservedElementLostFocus;
m_lastFocus.KeyUp -= ObservedElement_KeyUp;
m_lastFocus = null;
}
}
}
private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
IsEditing = false;
Text = m_lastValue;
}
else if (e.Key == Key.Enter)
{
IsEditing = false;
}
}
private static void IsEditingUpdate(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
EditableLabel self = obj as EditableLabel;
if ((bool)e.NewValue)
{
self.m_lastValue = self.Text;
self.m_textBox.Visibility = Visibility.Visible;
self.m_textBox.Focus();
self.m_label.Visibility = Visibility.Collapsed;
}
else
{
self.m_textBox.Visibility = Visibility.Collapsed;
self.m_label.Visibility = Visibility.Visible;
if (self.m_lastFocus != null)
self.m_lastFocus.Focus();
}
}
}
Related
I would like to show/activate a hidden window and set the (keyboard-)focus to a textbox programmatically (without violating mvvm if possible).
So if the the user presses a hotkey (already implemented) following should happen:
Window gets shown + activated
Textbox gets focused + keyboard focus (so the user can type some text)
Pressing the hotkey again should hide the window.
I tried binding Activated and Visibility to a boolean value, but I ran into some problems with TwoWay-Binding. Thank you for any ideas on how to solve this problem.
Edit:
MainWindow.xml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>
<i:Interaction.Behaviors>
<bh:ActivateBehavior Activated="{Binding Visible, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<Window.Visibility>
<Binding Path="Visible" Mode="TwoWay" Converter="{StaticResource BoolToVisConverter}"/>
</Window.Visibility>
<TextBox Grid.Column="1" x:Name="SearchBar" bh:FocusExtension.IsFocused="{Binding Visible, Mode=TwoWay}"/>
ActivateBehavior.cs:
used from this https://stackoverflow.com/a/12254217/13215602
public class ActivateBehavior : Behavior<Window> {
Boolean isActivated;
public static readonly DependencyProperty ActivatedProperty =
DependencyProperty.Register(
"Activated",
typeof(Boolean),
typeof(ActivateBehavior),
new PropertyMetadata(OnActivatedChanged)
);
public Boolean Activated {
get { return (Boolean) GetValue(ActivatedProperty); }
set { SetValue(ActivatedProperty, value); }
}
static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
var behavior = (ActivateBehavior) dependencyObject;
if (!behavior.Activated || behavior.isActivated)
return;
// The Activated property is set to true but the Activated event (tracked by the
// isActivated field) hasn't been fired. Go ahead and activate the window.
if (behavior.AssociatedObject.WindowState == WindowState.Minimized)
behavior.AssociatedObject.WindowState = WindowState.Normal;
behavior.AssociatedObject.Activate();
}
protected override void OnAttached() {
AssociatedObject.Activated += OnActivated;
AssociatedObject.Deactivated += OnDeactivated;
}
protected override void OnDetaching() {
AssociatedObject.Activated -= OnActivated;
AssociatedObject.Deactivated -= OnDeactivated;
}
void OnActivated(Object sender, EventArgs eventArgs) {
this.isActivated = true;
Activated = true;
}
void OnDeactivated(Object sender, EventArgs eventArgs) {
this.isActivated = false;
Activated = false;
}
}
FocusExtension.cs: used from this https://stackoverflow.com/a/1356781/13215602
public static class FocusExtension
{
public static bool GetIsFocuses(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement) d;
if ((bool) e.NewValue)
{
Keyboard.Focus(uie);
uie.Focus();
}
}
}
MainViewModel.cs:
private bool mVisible;
//Binding to this variable
public bool Visible
{
get { return mVisible; }
set
{
if (mVisible == value)
return;
mVisible = value;
RaisePropertyChanged(nameof(Visible));
}
}
I have two ScrollViewer and I need to sync position of those ScrollViewer when any of the ScrollViewer gets changes, but right now suppose when anyone scrollviewer2 is changed then on calling ChangeView event of ScrollViewer1 it's firing its ViewChangedEvent which is resetting ScrollViewer2 position back.
private void Scroll(ScrollViewer changedScrollViewer)
{
var group = ScrollViewers[changedScrollViewer];
VerticalScrollOffsets[group] = changedScrollViewer.VerticalOffset;
HorizontalScrollOffsets[group] = changedScrollViewer.HorizontalOffset;
foreach (var scrollViewer in ScrollViewers.Where(s => s.Value == group && s.Key != changedScrollViewer))
{
scrollViewer.Key.ViewChanged -= ScrollViewer_ViewChanged;
if (scrollViewer.Key.VerticalOffset != changedScrollViewer.VerticalOffset)
{
scrollViewer.Key.ChangeView(null, changedScrollViewer.VerticalOffset, null, true);
}
if (scrollViewer.Key.HorizontalOffset != changedScrollViewer.HorizontalOffset)
{
scrollViewer.Key.ChangeView(changedScrollViewer.HorizontalOffset, null, null, true);
}
//Commenting this line works. But I need to set ViewChange event back.
scrollViewer.Key.ViewChanged += ScrollViewer_ViewChanged;
}
}
#Nico's solution is much preferable. If you still need something with a flag, it 'll look like this:
bool is_programmatic_call = false;
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (is_programmatic_call)
{
is_programmatic_call = false;
return;
}
if(sender == ScrollViewer1)
{
ScrollViewer2.ViewChanged -= ScrollViewer_ViewChanged;
is_programmatic_call = true;
ScrollViewer2.ChangeView(ScrollViewer1.HorizontalOffset, ScrollViewer1.VerticalOffset, null, true);
ScrollViewer2.ViewChanged += ScrollViewer_ViewChanged;
}
else
{
ScrollViewer1.ViewChanged -= ScrollViewer_ViewChanged;
is_programmatic_call = true;
ScrollViewer1.ChangeView(ScrollViewer2.HorizontalOffset, ScrollViewer2.VerticalOffset, null, true);
ScrollViewer1.ViewChanged += ScrollViewer_ViewChanged;
}
}
both the ScrollViewer's ViewChanged event is handled by this ScrollViewer_ViewChanged
For sync two ScrollViewers, the better way is make a new Dependency Property, and bind it with the same value. It will notify the ScrollViewer to scroll automatically when the Dependency Property value changed. This solution will stop Circular Reference happening in the ViewChanged event.
I have implemented it for ListView in this code sample. You could refer segment code. But for ScrollViewer, you need to make xaml Behavior, because ScrollViewer is sealed class, it could not be inherited.
public class SyncBehavior : Behavior<ScrollViewer>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnAssociatedObjectLoaded;
AssociatedObject.LayoutUpdated += OnAssociatedObjectLayoutUpdated;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
}
private void OnAssociatedObjectLayoutUpdated(object sender, object o)
{
SyncPointOffSetY();
}
private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs routedEventArgs)
{
SyncPointOffSetY();
AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
}
private void SyncPointOffSetY()
{
if (AssociatedObject == null) return;
AssociatedObject.ViewChanged += AssociatedObject_ViewChanged;
}
private void AssociatedObject_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var MyScrollViewer = sender as ScrollViewer;
this.SetValue(PointOffSetYProperty, MyScrollViewer.VerticalOffset);
}
public double PointOffSetY
{
get { return (double)GetValue(PointOffSetYProperty); }
set { SetValue(PointOffSetYProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointOffSetYProperty =
DependencyProperty.Register("PointOffSetY", typeof(double), typeof(SyncBehavior), new PropertyMetadata(0.0, CallBack));
private static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var current = d as SyncBehavior;
var temScrollViewer = current.AssociatedObject;
if (e.NewValue != e.OldValue & (double)e.NewValue != 0)
{
temScrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
}
}
Usage
<ScrollViewer >
<Interactivity:Interaction.Behaviors>
<local:SyncBehavior PointOffSetY="{Binding PointY,Mode=TwoWay}"/>
</Interactivity:Interaction.Behaviors>
<StackPanel >
<Rectangle Height="500" Fill="Red"/>
<Rectangle Height="500" Fill="Black"/>
<Rectangle Height="500" Fill="Yellow"/>
</StackPanel>
</ScrollViewer>
<ScrollViewer Grid.Column="1" >
<Interactivity:Interaction.Behaviors>
<local:SyncBehavior PointOffSetY="{Binding PointY,Mode=TwoWay}"/>
</Interactivity:Interaction.Behaviors>
<StackPanel >
<Rectangle Height="500" Fill="Red"/>
<Rectangle Height="500" Fill="Black"/>
<Rectangle Height="500" Fill="Yellow"/>
</StackPanel>
</ScrollViewer>
And I have also added the above code to the sample that you could refer easily.
I am trying to create a user move-able expander for my WPF project. Ideally, if the user clicks the expander button, s/he should be able to expand. If one clicks and drags the text box that say "Advanced Options" I want that person to be able to move the expander around. My code doesn't work and just expands the expander. I tried associating the click on the header with something different than expanding, but that doesn't seem to work. The xaml and C# code is below. Help?
<Expander HorizontalAlignment="Left" Margin="89,372,0,0" VerticalAlignment="Top" Height="453" Width="909" IsExpanded="True" x:Name="grid_expander">
<Expander.Header>
<TextBlock HorizontalAlignment="Left" Height="22" Margin="5,0,0,0" TextWrapping="Wrap" Width="154" Foreground="White" FontSize="18" MouseDown="grid_expander_MouseDown" MouseUp="grid_expander_MouseUp" MouseMove="grid_expander_MouseMove">
Advanced Options
</TextBlock>
</Expander.Header>
<Expander.RenderTransform>
<TranslateTransform x:Name="expander_xy"/>
</Expander.RenderTransform>
</Expander>
private void grid_expander_MouseDown(object sender, MouseButtonEventArgs e)
{
//e.Handled = true;
m_start = e.GetPosition(window);
m_startOffset = new Vector(expander_xy.X, expander_xy.Y);
grid_expander.CaptureMouse();
}
private void grid_expander_MouseMove(object sender, MouseEventArgs e)
{
if (grid_expander.IsMouseCaptured)
{
Vector offset = Point.Subtract(e.GetPosition(window), m_start);
expander_xy.X = m_startOffset.X + offset.X;
expander_xy.Y = m_startOffset.Y + offset.Y;
}
}
private void grid_expander_MouseUp(object sender, MouseButtonEventArgs e)
{
grid_expander.ReleaseMouseCapture();
}
I have did the same using an Image Control instead of Expander Control, I believe you can replace the Image Control and place your Expander Control I will share my code. Try doing with Expander Control Let me know if it helps.
XAML
<Grid
Background="Black"
mousebehav:MouseBehaviour.MouseLeftButtonUpCommand="{Binding MLBUCommand}">
<Canvas >
<Image RenderTransformOrigin="0.5,0.5"
Source="{Binding ImageSource}"
Stretch="None" SnapsToDevicePixels="False"
mousebehav:MouseBehaviour.MouseLeftButtonDownCommand="{Binding MDCommand}"
mousebehav:MouseBehaviour.MouseMoveCommand="{Binding MMCommand}">
<Image.RenderTransform>
<TranslateTransform
X="{Binding MouseX}"
Y="{Binding MouseY}" />
</Image.RenderTransform>
<Image.LayoutTransform>
<TransformGroup>
<ScaleTransform
ScaleX="{Binding ScaleX}"
ScaleY="{Binding ScaleY}"/>
<RotateTransform
Angle="{Binding RotateAngle}"/>
</TransformGroup>
</Image.LayoutTransform>
</Image>
</Canvas>
</Grid>
View Model
private double _ScaleX;
public double ScaleX
{
get { return _ScaleX; }
set { _ScaleX = value; NotifyPropertyChanged(); }
}
private double _ScaleY;
public double ScaleY
{
get { return _ScaleY; }
set { _ScaleY = value; NotifyPropertyChanged(); }
}
private double _RotateAngle;
public double RotateAngle {
get { return _RotateAngle; }
set { _RotateAngle = value; NotifyPropertyChanged(); }
}
private double _MouseX;
public double MouseX
{
get { return _MouseX; }
set { _MouseX = value; NotifyPropertyChanged(); }
}
private double _MouseY;
public double MouseY
{
get { return _MouseY; }
set { _MouseY = value; NotifyPropertyChanged(); }
}
public bool IsMouseCaptured { get; set; }
private RelayCommand _mouseDownCommand;
public RelayCommand MDCommand
{
get
{
if (_mouseDownCommand == null) return _mouseDownCommand = new RelayCommand(param => ExecuteMouseDown((MouseEventArgs)param));
return _mouseDownCommand;
}
set { _mouseDownCommand = value; }
}
System.Windows.Point start;
System.Windows.Point origin;
private void ExecuteMouseDown(MouseEventArgs e)
{
var image = (System.Windows.Controls.Image)e.OriginalSource;
var border = (IInputElement)image.Parent;
start = e.GetPosition(border);
origin = new System.Windows.Point(MouseX, MouseY);
IsMouseCaptured = true;
}
private RelayCommand _mouseMoveCommand;
public RelayCommand MMCommand
{
get
{
if (_mouseMoveCommand == null) return _mouseMoveCommand = new RelayCommand(param => ExecuteMouseMove((MouseEventArgs)param));
return _mouseMoveCommand;
}
set { _mouseMoveCommand = value; }
}
private void ExecuteMouseMove(MouseEventArgs e)
{
if(IsMouseCaptured)
{
var image = (System.Windows.Controls.Image)e.OriginalSource;
var border = (IInputElement)image.Parent;
Vector v = start - e.GetPosition(border);
MouseX = origin.X - v.X;
MouseY = origin.Y - v.Y;
}
}
private RelayCommand _MLBUCommand;
public RelayCommand MLBUCommand
{
get
{
if (_MLBUCommand == null) return _MLBUCommand = new RelayCommand(param => Execute_MLBU((MouseEventArgs)param));
return _MLBUCommand;
}
set { _MLBUCommand = value; }
}
private void Execute_MLBU(MouseEventArgs param)
{
IsMouseCaptured = false;
}
Mouse Behaviors as Attached properties
public class MouseBehaviour
{
#region MouseUp
public static readonly DependencyProperty MouseUpCommandProperty =
DependencyProperty.RegisterAttached("MouseUpCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseUpCommandChanged)));
private static void MouseUpCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseUp += element_MouseUp;
}
static void element_MouseUp(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseUpCommand(element);
command.Execute(e);
}
public static void SetMouseUpCommand(UIElement element, ICommand value)
{
element.SetValue(MouseUpCommandProperty, value);
}
public static ICommand GetMouseUpCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseUpCommandProperty);
}
#endregion
#region MouseDown
public static readonly DependencyProperty MouseDownCommandProperty =
DependencyProperty.RegisterAttached("MouseDownCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseDownCommandChanged)));
private static void MouseDownCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseDown += element_MouseDown;
}
static void element_MouseDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseDownCommand(element);
command.Execute(e);
}
public static void SetMouseDownCommand(UIElement element, ICommand value)
{
element.SetValue(MouseDownCommandProperty, value);
}
public static ICommand GetMouseDownCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseDownCommandProperty);
}
#endregion
#region MouseMove
public static readonly DependencyProperty MouseMoveCommandProperty =
DependencyProperty.RegisterAttached("MouseMoveCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseMoveCommandChanged)));
private static void MouseMoveCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseMove += new MouseEventHandler(element_MouseMove);
}
static void element_MouseMove(object sender, MouseEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseMoveCommand(element);
command.Execute(e);
}
public static void SetMouseMoveCommand(UIElement element, ICommand value)
{
element.SetValue(MouseMoveCommandProperty, value);
}
public static ICommand GetMouseMoveCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseMoveCommandProperty);
}
#endregion
}
I am trying to apply drag drop to a dynamically created collection of buttons on a ItemsControl. I get an error:
Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel.
I am not sure how I can fix this.
My xaml:
<ItemsControl Grid.Row="2" Grid.Column="0" IsTabStop="False" Name="icsp"
ItemsSource="{Binding Path=ContentButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel x:Name="sp" AllowDrop="True" Background="SkyBlue"
PreviewMouseLeftButtonDown="sp_PreviewMouseLeftButtonDown"
PreviewMouseLeftButtonUp="sp_PreviewMouseLeftButtonUp"
PreviewMouseMove="sp_PreviewMouseMove"
DragEnter="sp_DragEnter" Drop="sp_Drop"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
My code behind:
public partial class Admin : UserControl
{
public Admin()
{
InitializeComponent();
}
private bool _isDown;
private bool _isDragging;
private Point _startPoint;
private UIElement _realDragSource;
private UIElement _dummyDragSource = new UIElement();
private void sp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var sp = FindChild<StackPanel>(Application.Current.MainWindow, "sp");
if (e.Source == sp)
{
}
else
{
_isDown = true;
_startPoint = e.GetPosition(sp);
}
}
private void sp_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isDown = false;
_isDragging = false;
_realDragSource.ReleaseMouseCapture();
}
private void sp_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_isDown)
{
var sp = FindChild<StackPanel>(Application.Current.MainWindow, "sp");
if ((_isDragging == false) &&
((Math.Abs(e.GetPosition(sp).X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance) ||
(Math.Abs(e.GetPosition(sp).Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)))
{
_isDragging = true;
_realDragSource = e.Source as UIElement;
_realDragSource.CaptureMouse();
DragDrop.DoDragDrop(_dummyDragSource, new DataObject("UIElement", e.Source, true),
DragDropEffects.Move);
}
}
}
private void sp_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("UIElement"))
{
e.Effects = DragDropEffects.Move;
}
}
private void sp_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("UIElement"))
{
var sp = FindChild<StackPanel>(Application.Current.MainWindow, "sp");
UIElement droptarget = e.Source as UIElement;
int droptargetIndex =-1, i =0;
foreach (UIElement element in sp.Children)
{
if (element.Equals(droptarget))
{
droptargetIndex = i;
break;
}
i++;
}
if (droptargetIndex != -1)
{
sp.Children.Remove(_realDragSource);
sp.Children.Insert(droptargetIndex, _realDragSource);
}
_isDown = false;
_isDragging = false;
_realDragSource.ReleaseMouseCapture();
}
}
Find child is a method used to search the visual tree and grab the panel named sp. It works and returns the control. When I apply this code to a stack panel with buttons created in xaml everything works. It is just when I try to use it in an ItemsControl that it blows up.
I need to replace the following code usercontrol instead of main window (of course the main window is just calling to the userControl),while doing that I have the following problem.
Currently I tried to add the following code like in the main window in the
constructor of the usercontrol after the
public partial class UserControl: UserControl
{
private static MappingViewModelView _modelViewInstance;
public UserControl()
{
InitializeComponent();
_modelViewInstance = new MappingViewModelView();
DataContext = _modelViewInstance;
var source = Resources["source"] as CollectionViewSource;
if (source != null)
source.Source = _modelViewInstance.UserList;
ListBox.SelectionChanged += listbox_SelectionChanged;
But now in the user control there is no event SelectionChanged for list box (using the intlisense) just ListBox.SelectionChangedEvent which is not fit the original solution from the main window which is
ListBox.SelectionChanged += listbox_SelectionChanged;
error which is given if i put the exact code is: Cannot access non-static event 'SelectionChanged' in static context
Any idea why the list box behave different in the user control?
public partial class MainWindow : Window
{
public ObservableCollection<User> _UsersList = new ObservableCollection<User>();
private readonly Dictionary<string, string> _mapping = new Dictionary<string, string>();
private const string DRAG_SOURCE = "DragSource";
public MainWindow()
{
InitializeComponent();
_UsersList.Add(new User { Name = "Jhon" });
_UsersList.Add(new User { Name = "Mike" });
_UsersList.Add(new User { Name = "Alex" });
_UsersList.Add(new User { Name = "Darl" });
CollectionViewSource source = this.Resources["source"] as CollectionViewSource;
source.Source = _UsersList;
ListBox.SelectionChanged += listbox_SelectionChanged;
DataObject.AddCopyingHandler(text1, DragCopy);
DataObject.AddCopyingHandler(text2, DragCopy);
}
public ObservableCollection<User> UserList
{
get { return _UsersList; }
}
private void listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 1)
{
if (ListBox.SelectedItems.Count > 0)
{
var mySelectedItem = ListBox.SelectedItem as User;
if (mySelectedItem != null)
{
DragDrop.DoDragDrop(ListBox, mySelectedItem,
DragDropEffects.Copy | DragDropEffects.Move);
}
}
}
}
private void DropText_PreviewDragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.None;
}
private void DropText_PreviewDrop(object sender, DragEventArgs e)
{
var textbox = (TextBox)sender;
if (!(textbox.Text.Length > 0))
{
DataObject data = e.Data as DataObject;
User user = data.GetData(typeof(User)) as User;
textbox.Tag = user;
var name = user.Name;
textbox.Text += name;
textbox.Focus();
textbox.CaretIndex = textbox.Text.Length;
e.Handled = true;
var remove = _UsersList.Remove((User)ListBox.SelectedItem);
if (!_mapping.ContainsKey(textbox.Name))
_mapping.Add(textbox.Name, name);
}
e.Handled = true;
}
private void DropText_PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void ListBox_Drop(object sender, DragEventArgs e)
{
DataObject data = e.Data as DataObject;
if (data != null)
{
User user = data.GetData(typeof(User)) as User;
if (user != null && !_UsersList.Contains(user))
_UsersList.Add(user);
}
TextBox txtBox = e.Data.GetData(DRAG_SOURCE) as TextBox;
if (txtBox != null)
{
if (_mapping.ContainsKey(txtBox.Name))
_mapping.Remove(txtBox.Name);
txtBox.Dispatcher.BeginInvoke((Action)(() => { txtBox.Text = string.Empty; }), System.Windows.Threading.DispatcherPriority.Normal);
}
e.Handled = true;
}
private void DragCopy(object sender, DataObjectCopyingEventArgs e)
{
if (e.IsDragDrop)
{
e.CancelCommand();
TextBox txtBox = sender as TextBox;
if (txtBox != null && txtBox.Tag != null)
{
DataObject dataObject = new DataObject(txtBox.Tag);
dataObject.SetData(DRAG_SOURCE, txtBox);
DragDrop.DoDragDrop(sender as DependencyObject, dataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
e.Handled = true;
}
}
private void DropText_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox txtBox = sender as TextBox;
if (txtBox.Text == string.Empty)
{
User user = txtBox.Tag as User;
if(user != null && !_UsersList.Contains(user))
_UsersList.Add(user);
if (_mapping.ContainsKey(txtBox.Name))
_mapping.Remove(txtBox.Name);
}
}
}
ListBox x:Name="ListBox" HorizontalAlignment="Left" Height="115"
VerticalAlignment="Top" Width="150" ItemsSource="{Binding Source={StaticResource source}}"
DisplayMemberPath="Name"
AllowDrop="True" Drop="ListBox_Drop" />
<Window.Resources>
<CollectionViewSource x:Key="source">
</CollectionViewSource>
</Window.Resources>
<TextBox x:Name="text1"
AcceptsReturn="True"
AllowDrop="True"
PreviewDragEnter="DropText_PreviewDragEnter"
PreviewDrop="DropText_PreviewDrop"
PreviewDragOver="DropText_PreviewDragOver"
TextChanged="DropText_TextChanged"
Grid.Column="1"
HorizontalAlignment="Left" Height="23" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="text2"
AcceptsReturn="True"
AllowDrop="True"
PreviewDragEnter="DropText_PreviewDragEnter"
PreviewDrop="DropText_PreviewDrop"
PreviewDragOver="DropText_PreviewDragOver"
TextChanged="DropText_TextChanged"
Grid.Column="1"
HorizontalAlignment="Left" Height="23" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
If you moved the ListBox to the UserControl, the most likely reasen is that - maybe by accident - the name was changed in the process.
The error message points in this direction as it recognizes ListBox as the type and not as the name of a concrete instance of a ListBox on the UserControl.