I have a listbox in my WPF app. The definition is given below:
<ListBox Margin="17.493,33.32,22.491,26.656" Name="lstData"
PreviewMouseLeftButtonDown="ListBox_MouseDown"
IsTextSearchEnabled="False" />
In the code behind, I bind the ListBox to a List. When the value is selected from the List Box, in my code behind, I want to be able to retrieve that value. How do I do it? Sample C# code will be helpful.
Thanks.
You can just bind to an item in your code behind
Example:
<ListBox Margin="17.493,33.32,22.491,26.656" Name="lstData"
SelectionChanged="ListBox_selectionChanged"
IsTextSearchEnabled="False"
ItemsSource="{Binding MyItems}"
SelectedItem="{Binding MySelectedItem}"/>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private ObservableCollection<MyItemType> _myItems = new ObservableCollection<MyItemType>();
public ObservableCollection<MyItemType> MyItems
{
get { return _myItems; }
set { _myItems = value; }
}
private MyItemType _mySelectedItem;
public MyItemType MySelectedItem
{
get { return _mySelectedItem; }
set { _mySelectedItem = value; NotifyPropertyChanged("MySelectedItem"); }
}
private void ListBox_selectionChanged(object sender, SelectionChangedEventArgs e)
{
MessageBox.Show(_mySelectedItem);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
Related
I'm trying to bind checkbox ischecked value. but whenever I click multiple checkbox, only last item is get selected.
This is my xaml.
<ListBox x:Name="EntryBox" ItemsSource="{Binding SourceCollection}"
SelectedItem="{Binding SelectedCheckbox}" SelectionMode="Multiple"
Grid.Column="0" Grid.Row="0" >
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsSelected="{Binding Selected}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Selected}"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My viewmodel
public class MytViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Item> _socketEntryCollection;
public MyViewModel()
{
_socketEntryCollection = new ObservableCollection<Item>();
}
private ListCollectionView _sourceCollection;
public ListCollectionView SourceCollection
{
get { return _sourceCollection; }
set
{
_sourceCollection = value;
NotifyPropertyChanged(nameof(SourceCollection));
}
}
private Item _selectedCheckbox;
public Item SelectedCheckbox
{
get
{
return _selectedCheckbox;
}
set
{
_selectedCheckbox = value;
NotifyPropertyChanged(nameof(SelectedCheckbox));
}
}
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _selected;
public Item()
{
_selected = false;
}
public bool Selected
{
get { return _selected; }
set
{
if(_selected != value)
{
_selected = value;
NotifyPropertyChanged(nameof(Selected));
}
}
}
private void NotifyPropertyChanged(string Obj)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(Obj));
}
}
In my code behind,
private MyViewModel _BVM;
public BulkTest()
{
InitializeComponent();
_BVM = new MyViewModel();
}
foreach (MyViewModel.Item i in _BVM.SourceCollection)
{
if (i.Selected)
{
}
}
When I try to retrieve selected items, it only gives me only the last item I checked even though I checked multiple.
I would like to get every item that I checked.
How can I do that?
I have a listview in my WPF app. Each item in the listview is a path of an image file. I'd like to show a thumbnail or preview of selected image on the listview.
MainWindow.xaml
<Image Source="{Binding SelectedImage}" />
<ListView x:Name="MyListView" AllowDrop="True" Drop="MyListView_Drop" />
MainWindow.xaml.cs
namespace MyWPFApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyDataContext();
}
}
private void MyListView_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
List<string> fileList = new List<string>(files);
foreach (string file in fileList)
{
if ((file.ToLower().EndsWith(".jpg") || file.ToLower().EndsWith(".jpeg") && !MyListView.Items.Contains(file))
{
MyListView.Items.Add(file);
}
}
}
public class MyDataContext : INotifyPropertyChanged
{
public string SelectedImage
{
get { return ((KeyValuePair<string, object>?)((MainWindow)Application.Current.MainWindow).MyListView.SelectedItem)?.Value?.ToString(); }
set
{
((MainWindow)Application.Current.MainWindow).MyListView.SelectedItem = value;
OnPropertyChanged(nameof(SelectedImage));
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Currently, selecting an image on the listview does not do anything. Any help will be greatly appreciated.
Your ListView does not bind to the SelectedImage property.
Besides that, you should consider having an actual view model with an ObservableCollection property for the image files list in addition to the SelectedImage property.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> Images { get; }
= new ObservableCollection<string>();
private string selectedImage;
public string SelectedImage
{
get { return selectedImage; }
set
{
selectedImage = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedImage)));
}
}
}
You would bind to it in XAML like shown below (perhaps with a ListBox instead of a ListView, because it is simpler):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Images}"
SelectedItem="{Binding SelectedImage}"
AllowDrop="True" Drop="ListView_Drop"/>
<Image Grid.Column="1" Source="{Binding SelectedImage}" />
</Grid>
The code behind of the window:
public partial class MainWindow : Window
{
private readonly ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private void ListView_Drop(object sender, DragEventArgs e)
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (var file in files.Where(
f => (f.ToLower().EndsWith(".jpg") || f.ToLower().EndsWith(".jpeg"))
&& !viewModel.Images.Contains(f)))
{
viewModel.Images.Add(file);
}
}
}
I have a Listbox where it's items are objects. In these objects I store two colors.
I want to bind these colors with an other object's property, but how can I achieve this?
The listbox looks like this:
Listbox1.Items.Add(new ColorAndMoreClass(Color.Red, Color.Blue));
Far away, in an other class there is a property which I'd like to bind to.
How can I do that?
Your rootclass could look like this.
In the class you have a object representing a different Class.
public class ColorAndMoreClass: INotifyPropertyChanged
{
private Color _c;
private Color _c2;
private OtherClass _example;
public ColorAndMoreClass(Color c, Color c2)
{
_c= c;
_c2 = c2;
}
public OtherClass example
{
get { return _example }
set
{
_example = value;
OnPropertyChanged("example");
}
}
public Color c
{
get { return _c; }
set
{
_c= value;
OnPropertyChanged("c");
}
}
public Color c2
{
get { return _c2; }
set
{
_c2 = value;
OnPropertyChanged("c2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Your other class could look like this. I just took a simple string.
public class OtherClass : INotifyPropertyChanged
{
private String _someOtherProperty;
public OtherClass(){}
public String someOtherProperty
{
get { return _someOtherProperty; }
set
{
_someOtherProperty= value;
OnPropertyChanged("someOtherProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
In your Code behind make a property the Listbox can bind to
public List<ColorAndMoreClass>> ListOfColorAndMore{ get; set; }
public Window1()
{
ListOfColorAndMore = GetDataThatFillsUpTheProperty();
InitializeComponent();
DataContext = this;
}
Your XAML could then look like this. The Datatemplate is used to tell XAML how to display your object.
<Grid>
<ListBox ItemsSource={Binding ListOfColorAndMore}>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=c.R}" />
<TextBlock Text="{Binding Path=c2.R}"/>
<TextBlock Text="{Binding Path=example.someOtherProperty}"/>
</StackPanel>
</DataTemplate>
</ListBox>
</Grid>
I hope it is this that you mean. But your question is not that clear.
With some help, I recently made binding collections in my custom control work. However, to my surprise I was told that to make the custom control property more flexible (that is possible to be bound with other parametrized collections), I needed to make the custom control's property to be of type IEnumerable<object> because of covariance and contravariance. However, this seems not to work for me
This is the control's view
<UserControl x:Class="BadaniaOperacyjne.Controls.Matrix"
mc:Ignorable="d" Name="CustomMatrix"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- ... -->
<Grid Grid.Row="2" Grid.Column="1" Name="contentGrid">
<ListBox ItemsSource="{Binding ElementName=CustomMatrix, Path=ItemsList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</UserControl>
and its code-behind is here
#region ItemsList Property
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register("ItemsList", typeof(IEnumerable<object>), typeof(Matrix), new PropertyMetadata(new PropertyChangedCallback(ItemsListChanged)));
public IEnumerable<object> ItemsList
{
get { return GetValue(ItemsListProperty) as IEnumerable<object>; }
set { SetValue(ItemsListProperty, value); }
}
private void ItemsListChanged(object value)
{
System.Diagnostics.Debug.WriteLine("matrix: items list changed " + value);
if (ItemsList != null)
{
//ItemsList.CollectionChanged += ItemsList_CollectionChanged;
System.Diagnostics.Debug.WriteLine("got " + string.Join(",", ItemsList.ToList()));
}
else
{
System.Diagnostics.Debug.WriteLine("got null");
}
}
void ItemsList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("matrix: current items list collection changed");
}
private static void ItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Matrix)d).ItemsListChanged(e.NewValue);
}
#endregion
and the Window that consumes the control is the following
<custom:Matrix x:Name="customMatrix" DockPanel.Dock="Top" Title="{Binding Title}" ItemsList="{Binding Items}"/>
with the code-behind like
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
Items = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6};
Items.CollectionChanged += Items_CollectionChanged;
}
void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("problem manager: items list changed " + e.NewItems.Count);
}
public ObservableCollection<int> Items { get; private set; }
protected string title;
public string Title
{
get { return title; }
set
{
if (title != value)
{
title = value;
NotifyPropertyChanged("Title");
}
}
}
protected void NotifyPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public ViewModel VM { get; private set; }
// the window's constructor
private ProblemManager()
{
VM = new ViewModel();
DataContext = VM;
InitializeComponent();
VM.Title = "title";
}
private int i = 0;
private void btnAddRow_Click(object sender, RoutedEventArgs e)
{
//VM.Items.Add(++i);
VM.Items[2] = 112;
//customMatrix.ItemsList = new ObservableCollection<object> { 1, 2, 3 };
//customMatrix.ItemsList.Add(66);
//////
VM.Title = (++i).ToString();
}
When I change the DependencyProperty of the ItemsList control to ObservableCollection<int> or at least ObservableCollection<object>, it works fine.
Is it really possible? If so, then is the mistake I made?
Co-variance is allowed for IEnumerable but i just checked its only allowed for reference types and not for value types (e.g. int).
Your version will work if you bind with ObservableCollection<string> since string is reference type.
So what you can do is use IEnumerable (non-generic version) as return type of your DP like this so that it will work for value types as well:
public static readonly DependencyProperty ItemsListProperty =
DependencyProperty.Register("ItemsList", typeof(IEnumerable), typeof(Matrix),
new PropertyMetadata(new PropertyChangedCallback(ItemsListChanged)));
public IEnumerable ItemsList
{
get { return (IEnumerable)GetValue(ItemsListProperty); }
set { SetValue(ItemsListProperty, value); }
}
I'm building application using the MVVM pattern. After clicking on one of the elements I want to see this element's details. I wrote this:
XAML
<phone:LongListSelector ItemsSource="{Binding Data}"
Margin="0,0,0,158"
SelectedItem="{Binding SelectedItem}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button>
<!-- Command="{Binding ShowDetailsAction}"-->
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding Text}"></TextBlock>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
ViewModel:
public IEnumerable SelectedItem
{
get { return _itemsControl; }
set
{
if (_itemsControl == value)
return;
_itemsControl = value;
// Test
_mss.ErrorNotification("fd");
}
}
I tried also using a command, which didn't work, too.
This was the command part:
public ICommand ShowDetailsCommand { get; private set; }
public ViewModel()
{
_loadDataCommand = new DelegateCommand(LoadDataAction);
SaveChangesCommand = new DelegateCommand(SaveChangesAction);
ShowDetailsCommand = new DelegateCommand(ShowDetailsAction);
}
private void ShowDetailsAction(object p)
{
_mss.ErrorNotification("bla bla");
}
EDIT
ViewModel
private IEnumerable _itemsControl;
public IEnumerable Data
{
get
{
return _itemsControl;
}
set
{
_itemsControl = value;
RaisePropertyChanged("Data");
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Model
public string Text { get; set; }
public DateTimeOffset Data { get; set; }
EDIT2
private MobileServiceCollection<ModelAzure, ModelAzure> _items;
private readonly IMobileServiceTable<ModelAzure> _todoTable = App.MobileService.GetTable<ModelAzure>();
private async void RefreshTodoItems()
{
try
{
_items = await _todoTable.ToCollectionAsync();
}
catch (MobileServiceInvalidOperationException e)
{
_mss.ErrorNotification(e.ToString());
}
Data = _items;
}
Your Data property looks like
private MobileServiceCollection<ModelAzure, ModelAzure> _itemsControl;
public MobileServiceCollection<ModelAzure, ModelAzure> Data
{
get
{
return _itemsControl;
}
set
{
_itemsControl = value;
RaisePropertyChanged("Data");
}
}
Edited
It seems the SelectedItem property from LongListSelector cannot be bound in WP8.
What you can do is either :
Use the derived and fixed custom LongListSelector provided in the link above instead of the default one, which looks like :
public class LongListSelector : Microsoft.Phone.Controls.LongListSelector
{
public LongListSelector()
{
SelectionChanged += LongListSelector_SelectionChanged;
}
void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedItem = base.SelectedItem;
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem",
typeof(object),
typeof(LongListSelector),
new PropertyMetadata(null, OnSelectedItemChanged)
);
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = (LongListSelector)d;
selector.SelectedItem = e.NewValue;
}
public new object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
}
Register the SelectionChanged event from LongListSelector and call your ViewModel by yourself inside the associated handler/callback :
in your view :
<phone:LongListSelector x:Name="YourLongListSelectorName"
ItemsSource="{Binding Data}"
Margin="0,0,0,158"
SelectionChanged="OnSelectedItemChanged">
in your code behind :
private void OnSelectedItemChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs e)
{
((YourViewModel)this.DataContext).NewSelectedItemMethodOrWhateverYouWant((ModelAzure)this.YourLongListSelectorName.SelectedItem);
//or
((YourViewModel)this.DataContext).SelectedItem = (ModelAzure)this.YourLongListSelectorName.SelectedItem;
}
Finally your Button command wasn't properly working, because when you use a DataTemplate, the ambiant DataContext is the item itself. Which means that it was looking for your Command into your Model instance, not into your ViewModel instance.
Hope this helps
In your ViewModel, you have:
public IEnumerable SelectedItem
{
get { return _itemsControl; }
set
{
if (_itemsControl == value)
return;
_itemsControl = value;
// Test
_mss.ErrorNotification("fd");
}
}
Why is your SelectItem an IEnumerable? Should it not be of type "Model"? Your list is bound to "Data" which should be ObservableList, not IEnumerable. It will provide it's own change notification, so you don't need to.
The list will set the SelectedItem when it gets selected, but if the type is wrong, it won't get set.
Greg