I'm trying to get the SelectedItem of a ContextMenu.
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<StackPanel>
<ListBox x:Name="MyListBox" ItemsSource="{Binding MyList}" SelectedItem="{Binding MySelectedItem}">
<ListBox.ContextMenu>
<ContextMenu ItemsSource="{Binding OCContext}" PreviewMouseDown="ContextMenu_PreviewMouseDown"/>
</ListBox.ContextMenu>
</ListBox>
<Button Content="Delete Item" Click="Button_Click"/>
</StackPanel>
</Grid>
</Window>
Code Behind
public partial class MainWindow : Window
{
public MainWindow()
{
OCContext = new ObservableCollection<string>();
MyList = new ObservableCollection<string>();
MyList.Add("Item 1");
MyList.Add("Item 2");
InitializeComponent();
}
public ObservableCollection<string> MyList { get; set; }
public ObservableCollection<string> OCContext { get; set; }
public string MySelectedItem { get; set; }
private void ContextMenu_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
MenuBase s = sender as MenuBase;
ItemCollection ic = s.Items;
string MyItem = "";
MyItem = (string)ic.CurrentItem;
MyList.Add(MyItem);
OCContext.Remove(MyItem);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (MySelectedItem != null)
{
OCContext.Add(MySelectedItem);
MyList.Remove(MySelectedItem);
}
}
}
You can Copy/Paste the code and the program should work.
The program is doing the following:
You can select an item in the ListBox. If you click on "Delete Item", the item will be deleted and added to the ContextMenu. If you click on the ContextMenu-Item, the item should be added again to the ListBox and removed from the ContextMenu. You should be able to do this over and over again...
So the ContextMenu is being binded to a collection. I get the Item with ic.CurrentItem.
The problem is that when I delete the item in the ListBox and add it again (by clicking on the item on the ContextMenu), ic.CurrentItem will be null.
Why?
Edit: Solution of Cyphryx is working, but now I'm trying to do the same by using MVVM/Binding:
XAML:
<ContextMenu x:Name="MyContext" ContextMenu="{Binding MyContextMenu}" ItemsSource="{Binding OCContext}"/>
ViewModel:
private ObservableCollection<string> _occontext;
public ObservableCollection<string> OCContext
{
get
{
if (_occontext == null)
_occontext = new ObservableCollection<string>();
MyContextMenu.Items.Clear();
foreach (var str in _occontext)
{
var item = new System.Windows.Controls.MenuItem();
item.Header = str;
item.Click += Content_MouseLeftButtonUp;
MyContextMenu.Items.Add(item);
}
return _occontext;
}
set
{
_occontext = value;
RaisePropertyChanged(() => OCContext);
}
}
private void Content_MouseLeftButtonUp(object sender, RoutedEventArgs e)
{
var s = sender as System.Windows.Controls.MenuItem;
if (s == null) return;
string ic = s.Header.ToString();
}
private System.Windows.Controls.ContextMenu _mycontextmenu;
public System.Windows.Controls.ContextMenu MyContextMenu
{
get
{
if (_mycontextmenu == null)
_mycontextmenu = new System.Windows.Controls.ContextMenu();
return _mycontextmenu;
}
set
{
_mycontextmenu = value;
RaisePropertyChanged(() => MyContextMenu);
}
}
Content_MouseLeftButtonUp is not being called?..
Rudi, from my knowledge, you cannot assign event handlers to individual objects in bound source. You can only use the WPF event handlers for the object it is tied to, hence, you'll have to fill the context menu manually, allowing you to add the event handlers at that time. In short, when you add
PreviewMouseDown="ContextMenu_PreviewMouseDown" to you WPF, the handler is assigned to the context menu, but when the binding adds the individual menu items, it does not add that handler to each item, leaving you event handless ;-) Below is code that will fix this:
WPF
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<StackPanel>
<ListBox x:Name="MyListBox" ItemsSource="{Binding MyList}" SelectedItem="{Binding MySelectedItem}" Height="Auto" MinHeight="20">
<ListBox.ContextMenu>
<ContextMenu Name="ContextMenu" Opened="ContextMenu_Opened" />
</ListBox.ContextMenu>
</ListBox>
<Button Content="Delete Item" Click="Button_Click"/>
</StackPanel>
</Grid>
</Window>
Code Behind:
public MainWindow()
{
OCContext = new ObservableCollection<string>();
MyList = new ObservableCollection<string>();
MyList.Add("Item 1");
MyList.Add("Item 2");
InitializeComponent();
}
public ObservableCollection<string> MyList { get; set; }
public ObservableCollection<string> OCContext { get; set; }
public string MySelectedItem { get; set; }
private void ContextMenu_Opened(object sender, EventArgs e)
{
ContextMenu.Items.Clear();
foreach (var str in OCContext)
{
var item = new MenuItem();
item.Header = str;
item.Click += Content_MouseLeftButtonUp;
ContextMenu.Items.Add(item);
}
}
private void Content_MouseLeftButtonUp(object sender, EventArgs e)
{
var s = sender as MenuItem;
if (s == null) return;
var ic = s.Header.ToString();
MyList.Add(ic);
OCContext.Remove(ic);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (MySelectedItem != null)
{
OCContext.Add(MySelectedItem);
MyList.Remove(MySelectedItem);
}
}
I hope this helps.
Cyphryx
Related
hello I have a list of data (sqlite) loaded to a listview and everything works marravilla if I select a listviewItem and right click on it, but I want to get the current listviewitem (under the pointer) without selecting any listviewitem
what I want is similar to the application of "Microsoft to DO"
and I have the following sample code:
MainPage.xaml
<Grid>
<ListView x:Name="myList">
<ListViewItem>Item 1</ListViewItem>
<ListViewItem>Item 2</ListViewItem>
<ListViewItem>Item 3</ListViewItem>
<ListViewItem>Item 4</ListViewItem>
<ListViewItem>Item 5</ListViewItem>
<ListView.ContextFlyout>
<MenuFlyout x:Name="itemActual">
<MenuFlyoutItem Text="see" Click="MenuFlyoutItem_Click"/>
</MenuFlyout>
</ListView.ContextFlyout>
</ListView>
</Grid>
MainPage.xaml.cs:
private void MenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
ContentDialog dialog = new ContentDialog()
{
//Content = myList.item ????
PrimaryButtonText = "ok"
};
dialog.ShowAsync();
}
Thanks in advance
My original answer wasn't correct so I decided to edit it.
First, create a field called _selectedValue with the type of your ListView's ItemsSource items' type, I'll call it "MyClass":
private MyClass _selectedItem;
Then, register the RightTapped event of your ListView:
<ListView x:Name="myList" RightTapped="myList_RightTapped">
From there, get the DataContext from the RightTappedRoutedEventArgs:
private void myList_RightTapped(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e) {
_selectedItem = (e.OriginalSource as FrameworkElement).DataContext as MyClass;
}
When your flyout's Click event is fired, use _selectedValue:
private void MenuFlyoutItem_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) {
// Do stuff with _selectedValue
}
Full example files:
MainPage.cs:
public sealed partial class MainPage : Page {
#region Fields
private List<MyClass> _items;
private MyClass _selectedItem;
#endregion
public MainPage() {
this.InitializeComponent();
_items = new List<MyClass>();
_items.Add(new MyClass() { Name = "O" });
_items.Add(new MyClass() { Name = "P" });
myList.ItemsSource = _items;
}
private void MenuFlyoutItem_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) {
// Do stuff with _selectedValue
}
private void myList_RightTapped(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e) {
_selectedItem = (e.OriginalSource as FrameworkElement).DataContext as MyClass;
}
public class MyClass {
public string Name { get; set; }
public override string ToString() => Name;
}
}
MainPage.xaml:
<Page
x:Class="UWP.Sandbox.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWP.Sandbox"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ListView x:Name="myList" RightTapped="myList_RightTapped">
<ListView.ContextFlyout>
<MenuFlyout x:Name="itemActual">
<MenuFlyoutItem Text="see" Click="MenuFlyoutItem_Click"/>
</MenuFlyout>
</ListView.ContextFlyout>
</ListView>
</Grid>
</Page>
Long story short; I have a grid inside a grid. Both grids have double click events that are supposed to fire different method calls (the main grid shows a window while the grid in the DataTemplate shows a window with parameters from the selected detail row).
The problem is that double clicking in the detail row also calls the double click on the main grid even though e.Handled is set to true.
The dumbed down XAML:
<Window x:Class="DoubleClickDataTemplate.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:DoubleClickDataTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate x:Key="LinesGrid">
<DataGrid x:Name="dgLines"
ItemsSource="{Binding Path=Lines}"
AutoGenerateColumns="True"
IsReadOnly="True"
MouseDoubleClick="dgLines_MouseDoubleClick">
</DataGrid>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid x:Name="dgFiles"
ItemsSource="{Binding}"
AutoGenerateColumns="True"
IsReadOnly="True"
RowDetailsVisibilityMode="VisibleWhenSelected"
RowDetailsTemplate="{StaticResource LinesGrid}"
MouseDoubleClick="dgFiles_MouseDoubleClick">
</DataGrid>
</Grid>
</Window>
The dumbed down source file:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace DoubleClickDataTemplate
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<FileClass> files = new List<FileClass>();
files.Add(new FileClass() { FileName = "File1", Lines = new List<LineClass>() { new LineClass() { LineNumber = 1, LineContents = "F1L1 contents" }, new LineClass() { LineNumber = 2, LineContents = "F1L2 contents" } } });
files.Add(new FileClass() { FileName = "File2", Lines = new List<LineClass>() { new LineClass() { LineNumber = 1, LineContents = "F2L1 contents" }, new LineClass() { LineNumber = 2, LineContents = "F2L2 contents" } } });
dgFiles.ItemsSource = files;
}
private void dgFiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
System.Diagnostics.Debug.WriteLine("dgFiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)");
}
private void dgLines_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
System.Diagnostics.Debug.WriteLine("dgLines_MouseDoubleClick(object sender, MouseButtonEventArgs e)");
e.Handled = true;
}
}
public class FileClass
{
public string FileName { get; set; }
public List<LineClass> Lines { get; set; }
}
public class LineClass
{
public int LineNumber { get; set; }
public string LineContents { get; set; }
}
}
The output shows that both events gets called when I double click in the DataTemplate/DetailRow:
00:05.616 (00:03:456) dgLines_MouseDoubleClick(object sender, MouseButtonEventArgs e)
dgFiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)
The closest got to a "solution" was using a lock flag (https://www.oipapio.com/question-3430969), but that can go wrong in too many ways.
Is there a way to make double clicking on the detail row only call the relevant event instead of both events?
You could handle the MouseLeftButtonDown and check the ClickCount for the outer DataGrid:
private void dgFiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
System.Diagnostics.Debug.WriteLine("dgFiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)");
}
}
private void dgLines_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
System.Diagnostics.Debug.WriteLine("dgLines_MouseDoubleClick(object sender, MouseButtonEventArgs e)");
e.Handled = true;
}
XAML:
<Window.Resources>
<DataTemplate x:Key="LinesGrid">
<DataGrid x:Name="dgLines"
ItemsSource="{Binding Path=Lines}"
AutoGenerateColumns="True"
IsReadOnly="True"
MouseDoubleClick="dgLines_MouseDoubleClick">
</DataGrid>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid x:Name="dgFiles"
ItemsSource="{Binding}"
AutoGenerateColumns="True"
IsReadOnly="True"
RowDetailsVisibilityMode="VisibleWhenSelected"
RowDetailsTemplate="{StaticResource LinesGrid}"
MouseLeftButtonDown="dgFiles_MouseDoubleClick">
</DataGrid>
</Grid>
If I press a button "Check All" all CheckBoxes in a ListBox should be selected and added to a list where all checked items are stored. The problem is that only the visible checkboxes are updated properly.
Here is my CheckBoxListItem class:
public class Cbli : INotifyPropertyChanged
{
private string _name;
private Boolean _isChecked;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; OnPropertyChanged("IsChecked"); }
}
public override string ToString()
{
return string.Format("Name: {0}, IsChecked: {1}", _name, _isChecked);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Window x:Class="ListBoxBuggy.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:listBoxBuggy="clr-namespace:ListBoxBuggy"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}" WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate x:Key="CheckBoxListItemTemplateNew" DataType="listBoxBuggy:Cbli">
<CheckBox Name="CheckBox"
IsChecked="{Binding IsChecked}"
Checked="Update"
Unchecked="Update"
FontSize="14">
<TextBlock Text="{Binding Name}" FontSize="14"/>
</CheckBox>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox HorizontalAlignment="Left" Height="300" VerticalAlignment="Top" Width="168"
ItemsSource="{Binding MyItemList}"
ItemTemplate="{StaticResource CheckBoxListItemTemplateNew}"
/>
<ListBox HorizontalAlignment="Left" Height="290" Margin="297,10,0,0" VerticalAlignment="Top" Width="195"
ItemsSource="{Binding CheckedItems}"
/>
<Button Content="Check All" HorizontalAlignment="Left" Margin="173,10,0,0" VerticalAlignment="Top" Width="75" Click="Check_All"/>
<Button Content="Uncheck All" HorizontalAlignment="Left" Margin="173,52,0,0" VerticalAlignment="Top" Width="75" Click="Uncheck_All"/>
</Grid>
</Window>
And the code behind:
public partial class MainWindow : Window
{
public ObservableCollection<Cbli> MyItemList { get; set; }
public ObservableCollection<Cbli> CheckedItems { get; set; }
public MainWindow()
{
// add dummy data
MyItemList = new ObservableCollection<Cbli>();
CheckedItems = new ObservableCollection<Cbli>();
for (int i = 0; i < 20; i++)
{
Cbli cbli = new Cbli
{
Name = "Test " + i,
IsChecked = i < 5 || i > 15
};
MyItemList.Add(cbli);
if (cbli.IsChecked)
CheckedItems.Add(cbli);
}
InitializeComponent();
}
private void Update(object sender, RoutedEventArgs e)
{
CheckBox selectedCheckbox = (CheckBox)sender;
Cbli cbli = (Cbli)selectedCheckbox.DataContext;
if (cbli.IsChecked)
CheckedItems.Add(cbli);
else
CheckedItems.Remove(cbli);
}
private void Check_All(object sender, RoutedEventArgs e)
{
foreach (Cbli cbli in MyItemList)
cbli.IsChecked = true;
}
private void Uncheck_All(object sender, RoutedEventArgs e)
{
foreach (Cbli cbli in MyItemList)
cbli.IsChecked = false;
}
}
After scrolling down, so all 20 items on the left list are visible and clicking then the "check all" button is working pretty well, but I don't know why.
Can someone please tell me what is wrong with that implementation? Checking/unchecking a single CheckBox is working, but the Check/Uncheck all buttons aren't working properly.
The comment from Blam (setting VirtualizingStackPanel.VirtualizationMode="Standard" was nearly the solution.
Add VirtualizingStackPanel.IsVirtualizing="False":
<ListBox HorizontalAlignment="Left" Height="300" VerticalAlignment="Top" Width="168"
ItemsSource="{Binding MyItemList}"
ItemTemplate="{StaticResource CheckBoxListItemTemplateNew}"
VirtualizingStackPanel.IsVirtualizing="False" />
This solved the problem (at least for me)
basically i'm trying to do THIS
but you can see it is not MVVM so i'm looking for a way to set SeletedItems = null or clear() depending on what's doable
because in my View i will got N ListBoxes and if he pressed a Button after selecting some Items i will change some properties of the SeletedItems but only for the last active Listbox
so i decided to use on SelectedItems Property for all the Listboxes but it doesn't work based on 2 problems i can't bind to to SelectedItems and based on this i can't test how to remove the selection from the other Listboxes
EDIT:
to give you an simple example:
XAML
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ListBox Width="432" Height="67"
HorizontalAlignment="Left" VerticalAlignment="Top"
SelectionMode="Extended"
<!-- SeletedItems="{Binding SelectedListItems}" ??? -->
ItemsSource="{Binding Collection1}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding MyText}"
Background="{Binding MyBackground}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Width="432" Height="67"
HorizontalAlignment="Left" VerticalAlignment="Top"
SelectionMode="Extended"
<!-- SeletedItems="{Binding SelectedListItems}" ??? -->
ItemsSource="{Binding Collection2}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding MyText}"
Background="{Binding MyBackground}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="unselect" Width="80" Height="150"
HorizontalAlignment="Right" VerticalAlignment="Top"
Command="{Binding MyCommand}"/>
</StackPanel>
</Window>
Code
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace Test
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VM();
}
}
public class VM : INotifyPropertyChanged
{
private ObservableCollection<DetailVM> _SelectedListItems = new ObservableCollection<DetailVM>();
public ObservableCollection<DetailVM> SelectedListItems
{
get { return _SelectedListItems; }
set
{
_SelectedListItems = value;
OnPropertyChanged("SelectedListItems");
}
}
public List<DetailVM> Collection1 { get; set; }
public List<DetailVM> Collection2 { get; set; }
private RelayCommand _myCommand;
public ICommand MyCommand
{
get { return _myCommand?? (_myCommand= new RelayCommand(param => OnMyCommand())); }
}
public void OnMyCommand()
{
foreach DetailVM item in SelectedListItems
{
item.MyBackground ="Red";
}
}
public VM()
{
Collection1 = new List<DetailVM>();
Collection2 = new List<DetailVM>();
for (int i = 0; i < 10; i++)
{
Collection1.Add(new DetailVM { MyText = "C1ITEM " + i });
Collection2.Add(new DetailVM { MyText = "C2ITEM " + i });
}
}
#region INotifyPropertyChanged Member
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class DetailVM
{
public string MyText { get; set; }
public string MyBackground { get; set; }
}
}
The code above should change the color of the Textbox background to Red
if the user selected some Items in a Listbox and he should only be able to seleted Items in one Listbox at the same time
so how to do this? (bear in mind this is a simple example but i need this for N Listboxes which will be generated over a template)
First of all, I would recommend you to extend ListView so that it includes a bindable SelectedValues property (you cannot use the name SelectedItems since it's already a non-bindable property of ListView). Here's an example of how this can be achieved.
public class MultiSelectListView : ListView
{
// Using a DependencyProperty as backing store
public static readonly DependencyProperty SelectedValuesProperty =
DependencyProperty.Register("SelectedValues", typeof(IList), typeof(MultiSelectListView), new PropertyMetadata(default(IList), OnSelectedItemsChanged));
public IList SelectedValues
{
get { return (IList)GetValue(SelectedValuesProperty); }
set { SetValue(SelectedValuesProperty, value); }
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// if selected items list implements INotifyCollectionChanged, we subscribe to its CollectionChanged event
var element = (MultiSelectListView)d;
if (e.OldValue != null && e.OldValue is INotifyCollectionChanged)
{
var list = e.OldValue as INotifyCollectionChanged;
list.CollectionChanged -= element.OnCollectionChanged;
}
if (e.NewValue is INotifyCollectionChanged)
{
var list = e.NewValue as INotifyCollectionChanged;
list.CollectionChanged += element.OnCollectionChanged;
}
}
// when selection changes in the view, elements are added or removed from the underlying list
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (SelectedValues != null)
{
foreach (var item in e.AddedItems)
{
if (!SelectedValues.Contains(item))
SelectedValues.Add(item);
}
foreach (var item in e.RemovedItems)
{
if (SelectedValues.Contains(item))
SelectedValues.Remove(item);
}
}
base.OnSelectionChanged(e);
}
// when underlying list changes, we set the control's selected items to the contents of the list
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (SelectedValues != null)
{
SetSelectedItems(SelectedValues);
}
}
}
Once you've done this you can control the behavior of a list's selected items through the viewmodel. Clearing the viewmodel list clears the selected items in the control.
Next you can subscribe to the collection changed event of your selected items lists (in the view model) and in the handler check whether you need to clear any of your lists.
I have a context menu in my XAML file. When I click on this menu item, I want to display a listbox to the user with a list of data populated from a backend call. How can I achieve this? I am a novice in XAML/WPF.
This would be your xaml:
<Window x:Class="MyWpfApp.MyWindow"
xmlns:cmd="clr-namespace:MyWpfApp.MyCommandsNamespace"
xmlns:vm="clr-namespace:MyWpfApp.MyViewModelsNamespace"
...>
<Window.Resources>
<DataTemplate x:Key="MyItemTemplate" DataType="{x:Type vm:MyItemClass}">
<TextBlock Text="{Binding MyItemText}"/>
</DataTemplate>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{x:Static cmd:MyCommandsClass.MyCommand1}" Executed="ExecuteMyCommand" CanExecute="CanExecuteMyCommand"/>
</Window.CommandBindings>
<Window.ContextMenu>
<ContextMenu>
<MenuItem Header="MyMenuItem1"
CommandTarget="{Binding}"
Command="{x:Static cmd:MyCommandsClass.MyCommand1}"/>
</ContextMenu>
</Window.ContextMenu>
<Grid>
<ItemsControl ItemsSource="{Binding MyList}"
ItemTemplate="{StaticResource MyItemTemplate}"/>
</Grid>
</Window>
and this would be your cs code:
public MyWindow()
{
VM = new MyViewModelsNamespace.MyViewModel();
this.DataContext = VM;
InitializeComponent();
}
public void ExecuteMyCommand(object sender, ExecutedRoutedEventArgs e)
{
VM.MyList.Add(new MyItemClass{MyItemText="some text"});
}
public void CanExecuteMyCommand(object sender, CanExecuteRoutedEventArgs e)
{
if (...) e.CanExecute = false;
else e.CanExecute = true;
}
where MyViewModel is something like:
public class MyViewModel : DependencyObject
{
//MyList Observable Collection
private ObservableCollection<MyItemClass> _myList = new ObservableCollection<MyItemClass>();
public ObservableCollection<MyItemClass> MyList { get { return _myList; } }
}
and MyItemClass is something like:
public class MyItemClass : DependencyObject
{
//MyItemText Dependency Property
public string MyItemText
{
get { return (string)GetValue(MyItemTextProperty); }
set { SetValue(MyItemTextProperty, value); }
}
public static readonly DependencyProperty MyItemTextProperty =
DependencyProperty.Register("MyItemText", typeof(string), typeof(MyItemClass), new UIPropertyMetadata("---"));
}
I forgot to mention command:
public static class MyCommandsClass
{
public static RoutedCommand MyCommand1 = new RoutedCommand();
}