I have ICollectionView looks like
public ICollectionView UsersCollectionView
{
get
{
var view = CollectionViewSource.GetDefaultView(this);
view.GroupDescriptions.Add(new PropertyGroupDescription("SeriesName"));
view.SortDescriptions.Add(new SortDescription("CreationDate", ListSortDirection.Ascending));
view.SortDescriptions.Add(new SortDescription("DocumentTypeId", ListSortDirection.Ascending));
return view;
}
}
I want to use drag & drop to change the item Series Name , and location on the list view any idea how to do that for example
--- ScienceFiction
------------> Book1
------------> Book2
--- History
------------> Book3
------------> Book4
if Idraged and droped book3 in ScienceFiction the output should be
--- ScienceFiction
------------> Book1
------------> Book2
------------> Book3
--- History
------------> Book4
I use xaml code like this :
<UserControl.Resources>
<Style x:Key="ContainerStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander Header="{Binding Name}" IsExpanded="True">
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<ListBox x:Name="lbPersonList" Margin="19,17,162,25" AlternationCount="2" ItemsSource="{Binding}">
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ContainerStyle}"/>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
AMH,
First modify the ListviewItem Style. It is the container that contains every line (datatemplate instanciation) of the listbox. It is a good place to manage Drag and Drop at the line level ( not a control of a line, there could be many in the DataTemplate).
In Visual Studio, select the listbox, right click, Edit additional templates/Edit Generated Item Container(ItemContainerStyle)/Edit a copy
In the ListBoxItemStyle created, add those three declarations amongst the setters :
<EventSetter Event="ListBoxItem.DragOver" Handler="ListBoxItemDragOver"/>
<EventSetter Event="ListBoxItem.Drop" Handler="ListBoxItemDrop"/>
<EventSetter Event="ListBoxItem.PreviewMouseMove" Handler="ListBoxItemPreviewMouseMove"/>
Set the AllowDrop property to true on the ListBox :
<ListBox x:Name="listboxBooks" AllowDrop="True">
Then implement the handlers in the .xaml.cs code :
#region DnD management
private Book sourceBook;
private void ListBoxItemPreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed)
return;
var listboxItem = sender as ListBoxItem;
if (listboxItem == null)
return;
sourceBook = listboxItem.DataContext as Book;
if (sourceBook == null)
return;
var data = new DataObject();
data.SetData(sourceBook);
// provide some data for DnD in other applications (Word, ...)
data.SetData(DataFormats.StringFormat, sourceBook.ToString());
DragDropEffects effect = DragDrop.DoDragDrop(listboxItem, data, DragDropEffects.Move | DragDropEffects.Copy);
}
private void ListBoxItemDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(Book)))
return;
var listBoxItem = sender as ListBoxItem;
if (listBoxItem == null)
return;
var targetBook = listBoxItem.DataContext as Book;
if (targetBook != null)
{
viewModel.RecategorizeBook(sourceBook, targetBook.Category);
}
e.Handled = true;
}
private void ListBoxItemDragOver(object sender, DragEventArgs e)
{
Debug.WriteLine(e.Effects);
if (!e.Data.GetDataPresent(typeof(Book)))
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
}
private void GroupItemDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(Book)))
return;
var groupItem = sender as GroupItem;
if (groupItem == null)
return;
dynamic targetGroup = groupItem.DataContext;
if (targetGroup != null)
{
// here I change the category of the book
// and refresh the view of the collectionViewSource ( see link to project zipped further)
viewModel.RecategorizeBook(sourceBook, targetGroup.Name as String);
}
e.Handled = true;
}
#endregion
Note that I also implemented Drop management on the group header in the handlers.
So the handler needs to be declared in the XAML groupstyle :
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<EventSetter Event="GroupItem.Drop" Handler="GroupItemDrop"/>
<EventSetter Event="GroupItem.PreviewMouseMove" Handler="ListBoxItemPreviewMouseMove"/>
It works, here is full working code : http://1drv.ms/1FhBZwr
Wish you the best possible code
Unfortunately .net does not yet provide an "easy to use" implementation of drag'n'drop. You have to build a bunch of things on your own. The starting point will be a Behavior starting the drag and a ContentControl being the area onto which the user can drop things. Having those defined you can easily reuse this concept. In the following example the StackPanel can be "dragged" onto an invisible "area" surrounding the TextBlock. This way you are able to implement manually sorting your books (drop before/behind the book under the mouse pointer).
If you want to drop books onto your headers surround them with a DropArea. You could also implement both ways.
Your xaml will look like:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
[...]
<ListBox x:Name="lbPersonList" Margin="19,17,162,25" AlternationCount="2" ItemsSource="{Binding}">
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ContainerStyle}"/>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<myOwn:DropArea>
<TextBlock Text="{Binding Name}"/>
</myOwn:DropArea>
<i:Interaction.Behaviors>
<myOwn:DragBehavior/>
</i:Interaction.Behaviors>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The DragBehavior will look like:
public class DragBehavior : Behavior<FrameworkElement>
[...]
protected override void OnAttached()
{
AssociatedObject.MouseMove += AssociatedObject_MouseMove;
AssociatedObject.MouseDown += AssociatedObject_MouseLeftButtonDown;
AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
AssociatedObject.MouseDown -= AssociatedObject_MouseLeftButtonDown;
AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
base.OnDetaching();
}
protected virtual void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
{
if (some condition of mouse button states or mouse moves)
{
DataObject data = new DataObject();
data.SetData(typeof(anyKeyType), anyData);
data.SetData(typeof(anyOtherKeyType), anyOtherData);
DragDrop.DoDragDrop(fe, data, DragDropEffects.Move);
}
}
The DropArea will look like:
public class DropArea : ContentControl
[...]
public DropArea()
{
DragEnter += AssociatedObjectDragEnter;
DragLeave += AssociatedObjectDragLeave;
DragOver += AssociatedObjectDragOver;
IsTabStop = false;
AllowDrop = true;
}
protected override void AssociatedObjectDrop(object sender, DragEventArgs e)
{
object o = e.Data.GetData(typeof(anyKeyType));
//handle dropped data
}
Hope that may help you on your way. There may be any frameworks or libraries to adress that issue, but this way you can adress your own needs.
Related
In my app I have one main ScrollViewer and many DataGrids with embedded scrollviewers in them. Thanks to this article https://serialseb.com/blog/2007/09/03/wpf-tips-6-preventing-scrollviewer-from/ and comments below I made parent and children scrollviwers work fine for me. Every time I start scrolling with mouse my parent scroll starts work when it reaches to any DataGrid then DataGrid's scrollviewer does it work and when whole content is scrolled it returns scrolling to the parent (main ScrollViewer). But that logic is not enough for me. I need this: if I start scrolling DataGrid (using child scrollviewer) it will scroll ONLY this DataGrid (when child ScrollViwer can't scroll any more it doesn't return scrolling to parent), but only if there is something to scroll on it. If not, the main window will scroll again. And if I start scrolling the main content than any of child ScrolViewers shouldn't react.
My questions is: how can I detect what UIElement start scrolling it's content and continue to scroll only it's content?
Simply put I want the same scroll parent/child logic as we have here on stackoverflow (try to scroll elements with code and whole page).
I think the similar idea is in this question WPF detect scrolling parent Control but it's not suitable for me because I have DataGrid instead of Popup.
I think about IsFocusable property but honestly I don't know where in my code I can check it.
Here is my code:
public class ScrollViewerCorrector
{
public static void SetApplyScrolling(DependencyObject obj, bool value)
{
obj.SetValue(ApplyScrollingProperty, value);
}
public static readonly DependencyProperty ApplyScrollingProperty =
DependencyProperty.RegisterAttached("ApplyScrolling", typeof(bool), typeof(ScrollViewerCorrector),
new FrameworkPropertyMetadata(false, OnApplyScrollingPropertyChanged));
public static void OnApplyScrollingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var viewer = sender as ScrollViewer;
if (viewer == null) return;
if ((bool)e.NewValue) viewer.PreviewMouseWheel += OnPreviewMouseWheel;
else viewer.PreviewMouseWheel -= OnPreviewMouseWheel;
}
private static List<MouseWheelEventArgs> _wheelEventArgsList = new List<MouseWheelEventArgs>();
private static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var scrollControl = sender as ScrollViewer;
if (!e.Handled && sender != null && !_wheelEventArgsList.Select(args => (args.Source)).Contains(sender))
{
var previewEventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{
RoutedEvent = UIElement.PreviewMouseWheelEvent, Source = sender
};
var originalSource = e.OriginalSource as UIElement;
_wheelEventArgsList.Add(previewEventArg);
originalSource.RaiseEvent(previewEventArg);
_wheelEventArgsList.Remove(previewEventArg);
//check if any of children handled the event
if (!previewEventArg.Handled && ((e.Delta > 0 && scrollControl.VerticalOffset == 0) ||
(e.Delta <= 0 && scrollControl.VerticalOffset >=
scrollControl.ExtentHeight - scrollControl.ViewportHeight)))
{
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
eventArg.RoutedEvent = UIElement.MouseWheelEvent;
eventArg.Source = sender;
var parent = (UIElement)((FrameworkElement)sender).Parent;
parent.RaiseEvent(eventArg);
}
}
}
MainView.xaml
<rui:ReactiveUserControl x:TypeArguments="viewModels:MainViewModel"
x:Class="AnalyticalReporting.UI.Views.MainView"
xmlns:viewModels="clr-namespace:AnalyticalReporting.UI.ViewModels"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:rui="http://reactiveui.net"
xmlns:controls="clr-namespace:MNS.Common.UI.Controls;assembly=MNS.Common.UI"
mc:Ignorable="d"
d:DesignHeight="1024" d:DesignWidth="1280"
Background="{DynamicResource WhiteBrush}">
<UserControl.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type ScrollViewer}">
<Style.Setters>
<Setter Property="controls:ScrollViewerCorrector.ApplyScrolling" Value="True" />
</Style.Setters>
</Style>
<DataTemplate x:Key="ListTemplate">
<StackPanel>
<rui:ViewModelViewHost x:Name="DisplayingViewModelViewHost" HorizontalContentAlignment="Stretch" ViewModel="{Binding .}"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<ScrollViewer x:Name="MainScrollViewer" VerticalScrollBarVisibility="Auto" VerticalAlignment="Top">
<ItemsControl x:Name="InfoCardList" ItemTemplate="{StaticResource ListTemplate}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</rui:ReactiveUserControl>
The new text will never be present in the items inside ComboBox. Below is the full XAML + code-behind that does not work as I wish. What I am trying to achieve is to have the user select an actual group of items from the combo box, or a No Group (italic, grayed out) item that should be applied as an empty string in the text box inside the ComboBox.
I also tried:
with StaysOpenOnEdit="True" (with the same result) and
by handling the Selected event of the "click me" ComboBoxItem (the event handler is called before the Text or SelectedItem properties of the ComboBox change).
XAML
<Window x:Class="cs_wpf_test_12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:cs_wpf_test_12"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Orientation="Vertical">
<ComboBox SelectionChanged="ComboBox_SelectionChanged"
IsEditable="True">
<ComboBoxItem>test</ComboBoxItem>
<ComboBoxItem Foreground="Gray">click me</ComboBoxItem>
</ComboBox>
</StackPanel>
</Window>
Code-behind
internal bool HandlingSelectionChange = false;
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (HandlingSelectionChange)
{
return;
}
HandlingSelectionChange = true;
var cb = sender as ComboBox;
if (cb.Text == "click me")
{
cb.Text = "";
e.Handled = true;
}
HandlingSelectionChange = false;
}
Expected: when the user clicks on the "click me" item in the drop-down the text of the ComboBox becomes an empty string. The rest of the items, when clicked, should copy their text normally into the text box of the ComboBox.
Actual:
Start the program.
Select the "click me" item.
The Text changes to "click me" (not grayed out) instead of "".
Click on the "test" item.
The Text changes to "" (empty string) instead of "test".
Click again on the "test" item.
The Text changes to "test".
Update
I wish to use MVVM but I am still a beginner. I have several ComboBoxes like shown above inside a DataGridTemplateColumn, and for each of the ComboBoxes (which should have the same drop-down contents) I think I should have a ViewModel for each of its ComboBoxItems. If possible, I would like to learn how I could I use MVVM correctly in this situation.
The big XAML
<DataGridTemplateColumn Header="Group Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding GroupName, Mode=OneWay}">
</Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsEditable="True" StaysOpenOnEdit="True"
ItemsSource="{Binding Path=Clocks.GroupsVM,
RelativeSource={RelativeSource AncestorType=local:ClockDataGrid}}"
PreviewKeyDown="ComboBox_PreviewKeyDown"
SelectionChanged="ComboBox_SelectionChanged"
Text="{Binding GroupName}">
<ComboBox.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="FontStyle" Value="{Binding FontStyle}"/>
<Setter Property="Foreground" Value="{Binding Foreground}"/>
</Style>
</ComboBox.Resources>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The big code-behind
private void ComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
var cb = sender as ComboBox;
if ((e.Key == Key.Return ||
e.Key == Key.Enter) &&
cb.Text != "")
{
bool duplicate = false;
foreach (ClockGroupVM vm in Clocks.GroupsVM)
{
if (vm.Name == cb.Text)
{
cb.SelectedItem = vm;
duplicate = true;
break;
}
}
if (duplicate)
{
return;
}
// create a ClockGroupM and corresponding ClockGroupVM
// (ClockGroupVM inherits from ClockGroupM)
var cvm = new ClockGroupVM()
{
Name = cb.Text
};
Clocks.Groups.Insert(0, cvm);
cb.SelectedItem = cvm;
}
}
internal bool HandlingSelectionChange = false;
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (HandlingSelectionChange)
{
return;
}
HandlingSelectionChange = true;
var cb = sender as ComboBox;
//if (cb.SelectedItem is the VM with Style != Normal)
ClockGroupVM foundVM = null;
foreach (ClockGroupVM vm in Clocks.GroupsVM)
{
if (vm.FontStyle != FontStyles.Normal &&
ReferenceEquals(cb.SelectedItem, vm))
{
foundVM = vm;
break;
}
}
if (foundVM != null)
{
cb.Text = "";
e.Handled = true;
}
HandlingSelectionChange = false;
}
ComboBox.Text wont update immediately in the SelectionChanged event. You can use SelectedValue's content instead. And set SelectedValue to null. Change your if condition like this.
if ((cb.SelectedValue as ComboBoxItem).Content.ToString() == "click me")
{
cb.Text = "";
cb.SelectedValue = null;
e.Handled = true;
}
I found one bug, and I don't understand, why it's happening.
I have DataGrid. On left mouse button click I want to open ContextMenu.
I made it. It works fine until I start clicking on DataGrid's Cells on random (every time ContextMenu is closing and reappearing at new place). But at one moment something is happening and ContextMenu newer showing (and Window don't response to any Mouse events like clicking on buttons and so on)... until I move cursor out of window and return it (or clicking Alt or F10).
Here some code:
ContextMenu (inside <DataGrid.Resources>)
<ContextMenu Style="{StaticResource DefaultContextMenuStyle}" x:Key="cm"
DataContext="{Binding Data, Source={StaticResource WindowViewModel}}">
</ContextMenu>
DataGrid column:
<DataGridTemplateColumn Header="TestHeader" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel ContextMenu="{StaticResource cm}" VerticalAlignment="Stretch" Background="Transparent">
<i:Interaction.Behaviors>
<local:OpenContextMenuLeftBehavior />
</i:Interaction.Behaviors>
<TextBlock Style="{StaticResource TextVCenteredCellStyle}" Text="{Binding LPU}" />
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
And behavior to open ContextMenu:
public class OpenContextMenuLeftBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.PreviewMouseUp += OpenContextMenu;
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseUp -= OpenContextMenu;
}
void OpenContextMenu(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left) {
FrameworkElement Sender = sender as FrameworkElement;
Sender.ContextMenu.PlacementTarget = Sender;
Sender.ContextMenu.IsOpen = true;
}
}
}
When I found that bug, I tried to find some info with Snoop WPF.
And here some info from it:
Before bad thing happened one click on cell was like:
before bad thing
After bad thing:
after bad thing
First event happened at Popup (ContextMenu part?), it doesn't belong to window VisualTree. This Popup seems to stretch all over the main Window and closing, when i move mouse out of it.
So, I lost 2 days on that bug and this is all, that I can find.
Please help me.
EDIT:
I created minimal example:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="dg1" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<i:Interaction.Behaviors>
<local:OpenContextMenuLeftBehavior />
</i:Interaction.Behaviors>
<DockPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="123456"></MenuItem>
</ContextMenu>
</DockPanel.ContextMenu>
<TextBlock Text="123" />
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
dg1.ItemsSource = new List<int>()
{
1,2,3,4,5,6,7,8,9,0
};
}
}
public class OpenContextMenuLeftBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
AssociatedObject.PreviewMouseUp += OpenContextMenu;
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseUp -= OpenContextMenu;
}
void OpenContextMenu(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left) {
FrameworkElement Sender = sender as FrameworkElement;
if (Sender != null) {
Sender.ContextMenu.PlacementTarget = Sender;
Sender.ContextMenu.IsOpen = true;
Sender.ContextMenu.UpdateLayout();
}
}
}
}
To reproduce this bug just click rapidly on different cells
Revised Answer
After you published a full, simple example, I'd suggest the following:
after you create the ContextMenu
Sender.ContextMenu.IsOpen = true;
Sender.ContextMenu.PreviewMouseUp += ContextMenu_PreviewMouseUp;
Having defined
private void ContextMenu_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
var Sender = (sender as ContextMenu);
if (Sender != null)
{
Sender.IsOpen = true;
e.Handled = true;
}
}
(you still have to manage the grid line selection, but it's like off topic here)
I want to delete my listbox item on right click. But, right click event not worked in my case.
Below is the code which I tried.
In constructor:
listBox1.MouseDown += new MouseButtonEventHandler(listBox1_MouseRightClick);
Right Click:
private void listBox1_MouseRightClick(object sender, MouseButtonEventArgs e)
{
if (sender is ListBoxItem)
{
ListBoxItem item = (ListBoxItem)sender;
Harvest_TimeSheetEntry entryToDelete = (Harvest_TimeSheetEntry)item.DataContext;
MessageBoxResult Result = System.Windows.MessageBox.Show("Are you sure?", "Delete Confirmation", System.Windows.MessageBoxButton.YesNo);
if (Result == MessageBoxResult.Yes)
{
Globals._globalController.harvestManager.deleteHarvestEntry(entryToDelete);
}
else
{
System.Windows.MessageBox.Show("Delete operation Terminated");
}
}
}
In xaml:
<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="0,131,0,59" ItemTemplateSelector="{StaticResource templateSelector}" SelectionMode="Single" MouseRightButtonDown="listBox1_MouseRightClick">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="MouseDown" Handler="listBox1_MouseRightClick"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Please suggest, how should I use right click event so it could work?
Your original code seems redundant and verbose. MouseRightButtonDown isn't working because there is already an event handling listbox item selection and the ListBoxItem datacontext is simply the SelectedItem of listBox1.
Get rid of overriding the style and just declare the listbox with the preview event. This will tunnel MouseRightButtonDown instead of bubble it.
<ListBox x:Name="listBox1"
ItemsSource="{Binding}"
ItemTemplateSelector="{StaticResource templateSelector}"
Margin="0,131,0,59"
SelectionMode="Single"
PreviewMouseRightButtonDown="listBox1_MouseRightClick" />
In the constructor, get rid of this
listBox1.MouseDown += new MouseButtonEventHandler(listBox1_MouseRightClick);
Now in the event handler, sender is your listbox1 but if you're not tying this event to other listboxes, simply get the selectedItem from listbox1 and cast it to the appropriate object. Otherwise if you decide you want the functionality on multiple listboxes cast sender to ListBox
private void listBox1_MouseRightClick(object sender, MouseButtonEventArgs e)
{
Harvest_TimeSheetEntry entryToDelete = (Harvest_TimeSheetEntry)listBox1.SelectedItem;
if(entryToDelete != null)
{
//Do work
}
}
Deleting records on right click is not a good design and it leads users make more confuse the functionality. Still if you want to do something, then you can go for the PreviewMouseRightButtonDown event. Please see the below snippet
ListBox1.PreviewMouseRightButtonDown += new MouseButtonEventHandler(ListBox1_MouseRightButtonDown);
Change your XAML as follows
<ListBox x:Name="listBox1"
ItemsSource="{Binding}"
Margin="0,131,0,59"
ItemTemplateSelector="{StaticResource templateSelector}"
SelectionMode="Single">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="ListBox1_PreviewMouseRightButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I have a ListBox with binding, when i add Items it works perfect but if i try to remove the items with contextMenu it doesnt work.
Here is what i try so far ListBox Xaml Code
<ListBox Name="lstPersons"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" Margin="126,-228,2,-242">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Name="PersonContext">
<toolkit:MenuItem Name="PersonDelete" Header="Delete" Click="DeletePerson_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="btnKellnerName"
Text="{Binding _PersonName}"
FontSize="35"
FontFamily="Portable User Interface"/>
<TextBlock Name="btnPosition"
Text="{Binding _PersonPosition}"
FontSize="22"/>
<TextBlock Name="lblUhrzeit"
Text="{Binding _CreationDate}"
FontSize="18"/>
<TextBlock Name="Space" Text=" "/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the Binding Class Code
public class Person
{
public string _PersonName { get; set; }
public string _PersonPosition { get; set; }
public string _CreationDate { get; set; }
}
When i add items like this
ObservableCollection<Person> personList = new ObservableCollection<Person>();
personList.Add(new Person {
_PersonName = "Tom",
_PersonPosition = "Bla",
_CreationDate = "33"
});
this.lstPerson.ItemSource = personList;
it works pefect! Now i Want to remove a selected Item with the ContextMenu like this
private void DeletePerson_Click(object sender, RoutedEventArgs e)
{
int indexPerson = lstPerson.SelectedIndex;
personList.RemoveAt(indexPerson);
}
but it doesnt work. Does Anybody have an Idea what im making wrong? Thanks
Ok Guys i have now the Solution the Problem was the value of SelectedIndex now ive got the right Value. First ive put the ContextMenu inside ListBoxItemTemplate/StackPanel
Code Behind:
private void DeletePerson_Click(object sender, RoutedEventArgs e)
{
try {
var selectedListBoxItem = listBox.ItemContainerGenerator.ContainerFromItem(((MenuItem) sender).DataContext) as ListBoxItem;
var selectedIndex = listBox.ItemContainerGenerator.IndexFromContainer(selectedListBoxItem);
_personList.RemoveAt(selectedIndex);
}
catch( Exception ex ) { MessageBox.Show(ex.Message); };
}
In addition to what Joan said, you can also do this in Xaml like this:
<ListBox ItemsSource={Binding personList}
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" Margin="126,-228,2,-242">
<toolkit:ContextMenuService.ContextMenu>
</ListBox>
You should read how to use Bindings and the MVVM-Model. It's pretty convenient for stuff like this.
Here are some links:
http://channel9.msdn.com/Events/MIX/MIX10/EX14
http://channel9.msdn.com/Events/MIX/MIX11/OPN03
Don't get discouraged. It's a bit of learning at the beginning, but it's totally worth it. I also had some problems with displaying lists, before I started doing everything with MVVM using Laurent Bugnions' MvvmLight package.
try this:
private void DeletePerson_Click(object sender, RoutedEventArgs e)
{
System.Collections.IList pathRemove;
pathRemove = lstPerson.SelectedItems;
if(pathRemove.Count != 0)
{
for (int i = pathRemove.Count - 1; i >= 0; i--)
{
lstPerson.Remove((Person)pathRemove[i]);//multiple deletes
}
}
}
Try to add this when the form is initialized:
lstPersons.ItemsSource = personList ;
How to: Create and Bind to an ObservableCollection
It appears that the problem is due to selected item
The key is setting the PreviewMouseRightButtonDown event in the
correct place. As you'll notice, even without a ContextMenu right
clicking on a ListViewItem will select that item, and so we need to
set the event on each item, not on the ListView.
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="OnListViewItemPreviewMouseRightButtonDown" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu Item">Item 1</MenuItem>
<MenuItem Header="Menu Item">Item 2</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
</ListView>
.
private void OnListViewItemPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("Preview MouseRightButtonDown");
e.Handled = true;
}
wpf listview right-click problem
Why don't use binding for all.
Bind item source as ObservableCollection blabla
Bind Selecteditem as XXXX
Use command for your button and when you want remove an item do :
blabla.remove(SelectedItem);