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.
Related
<Window x:Class="WpfApp12.MainWindow"
xmlns=...usual namespaces...
Loaded="Window_Loaded"
>
<Window.Resources>
<DataTemplate x:Key="myHeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{TemplateBinding Content}"/>
<Button Margin="5,0,0,0" x:Name="MyButton">Press me</Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<!-- Just point the datacontext to the code behind -->
<Window.DataContext>
<Binding RelativeSource="{RelativeSource Mode=Self}"/>
</Window.DataContext>
<DataGrid Name="DG" ItemsSource="{Binding People}"/>
</Window>
This, togheter with the code behind below gives just what I want: a DataGrid with a column whose header has been dynamically assigned a DataTemplate with a Button "Press me":
The code behind:
public partial class MainWindow : Window
{
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
public MainWindow()
{
People.Add(new Person() { Name = "Isaac", Surname = "Newton" });
People.Add(new Person() { Name = "Galileo", Surname = "Galilei" });
InitializeComponent();
}
public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DG.Columns[0].HeaderTemplate = (DataTemplate)FindResource("myHeaderTemplate");
//how to access the button in the template in order to assign the click event?
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
//DO SOMETHING
}
}
}
Now i want to dynamically wire MyButton_Click event to the button in the template.
This kind of problems seem to have a lot of coverage, this one being one of the best:
WPF How to access control from DataTemplate
There there is something like:
ComboBox myCombo = _contentPresenter.ContentTemplate.FindName("myCombo", _contentPresenter) as ComboBox;
I'm not very familiar with the templating, and I cannot find the starting point, the "content presenter" on which to call the FindName.
You could use a recursive helper method that finds the Button in the visual tree:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DG.Columns[0].HeaderTemplate = (DataTemplate)FindResource("myHeaderTemplate");
DG.Dispatcher.BeginInvoke(new Action(() =>
{
DataGridColumnHeadersPresenter presenter = FindVisualChild<DataGridColumnHeadersPresenter>(DG);
DataGridCellsPanel dataGridCellsPanel = FindVisualChild<DataGridCellsPanel>(presenter);
DataGridColumnHeader header = dataGridCellsPanel.Children[0] as DataGridColumnHeader;
Button button = FindVisualChild<Button>(header);
if (button != null)
button.Click += MyButton_Click;
}));
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
//DO SOMETHING
}
private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
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'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();
}
}
}
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.
My aim is to be able to add/ edit/delete items to a listbox. I have created a listbox of textboxes in the following way. I am able to display data but I am not able to edit data. Can someone help me modify the code so that I can achieve this functionality.
<ListBox Name="lbDemoBox" ItemsSource="{Binding testList}" Grid.Column="0" Grid.Row="0" SelectionChanged="lbDemoBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Path=.}" KeyDown="TextBox_KeyDown" KeyUp="TextBox_KeyUp" GotFocus="TextBox_GotFocus"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the code behind
public partial class MainPage : UserControl
{
private string focusedString { get; set; }
public MainPage()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
LayoutRoot.DataContext = new ViewModel();
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && (Keyboard.Modifiers & (ModifierKeys.Shift)) == ModifierKeys.Shift)
{
(lbDemoBox.ItemsSource as ObservableCollection<string>).Add(string.Empty);
}
}
private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
int index = (lbDemoBox.ItemsSource as ObservableCollection<string>).IndexOf(focusedString);
(lbDemoBox.ItemsSource as ObservableCollection<string>).RemoveAt(index);
}
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
focusedString = (sender as TextBox).Text;
}
private void lbDemoBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
public class ViewModel
{
public ObservableCollection<string> testList
{
get { return new ObservableCollection<string> { "Item1", "Item2", "Item3" }; }
}
}
Try this:
<TextBox Text="{Binding Path=YourProperty, Mode=TwoWay}" />