I want when a user selects one or multiple items that my source property gets updated. I have tried with the binding mode OneWayToSource but this is not helping. Below is the XAML and ViewModel code:
<ListBox x:Name="ItemsListBox" SelectionMode="Multiple" Height="300"
ItemsSource="{Binding ResultSet}"
SelectedItem="{Binding SelectedItems,Mode=OneWayToSource}">
private List<string> _selectedItems;
public List<string> SelectedItems
{
get
{
return _selectedItems;
}
set
{
_selectedModeItems = value;
NotifyPropertyChanged("SelectedItems");
}
}
I have taken the approach by using Attached behaviours and it works , but is there any simpler way?
Your question should be like this.
How to get multiple selected items from the ListBox in WPF with MVVM?
Well, you have the answer from following stackoverflow threads.
link 1
link 2
Simply you can define IsSelected property in your ResultSet view model. Then if you want to get selected items at any point, just get the items which the "IsSelected" property is set to true from the ResultSet.
you could also create an Attached Behavior
here is an Example how to do it
WPF ListBox has two properties related to the currently selected item:
SelectedItem available for binding, bound to the first selected item.
SelectedItems (with an 's' at the end) is not available to binding.
When multi selection is enabled, you want to have access to SelectedItems but unfortunately you can't bind to it
You can workaround this limitation using code behind.
Create a property named SelectedItems that will contain the selection, then subscribe the SelectionChanged event:
<ListBox x:Name="ItemsListBox" SelectionMode="Multiple" Height="300"
ItemsSource="{Binding ResultSet}"
SelectionChanged="ListBox_SelectionChanged">
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (string item in e.RemovedItems)
{
SelectedItems.Remove(item);
}
foreach (string item in e.AddedItems)
{
SelectedItems.Add(item);
}
}
Related
I am attempting to bind a list to a combobox. I want to display this list of options within the Combobox itself. (Later to allow the user to select an item 'SelectedItem', I'll cross that bridge when I get there)
MyCode.cs
// List of values for 'Type' dropdown
private static readonly List<string> MarkerTypeList = new List<string>(new string[]
{
"Analog",
"Digital"
});
// Binding for viewing list in window
public List<string> TypeOptions
{
get { return MarkerTypeList; }
}
MyCode.xaml
<ComboBox x:Name="myCombobox" HorizontalAlignment="Left" Margin="125,26,0,0" VerticalAlignment="Top" Width="70" Height="23" SelectedItem="" ItemsSource="{Binding TypeOptions}" />
The solution boiled down to changing this:
ItemsSource="{Binding TypeOptions}"
to this:
ItemsSource="{Binding Marker.TypeOptions}"
Thanks for the input, sorry you didnt have a whole lot to go on.
If you bind you should have your INotifyPropertyChanged interface added to your class which is the actual ViewModel for your ComboBox.
So - 1. Add the Interface to the class. 2. Create RaisePropertyChanged function. 3. Call the function through the setter of the property. This will push the updated value of the property through the binding and you will see the combobox populated.
Question summary: Is there a way in XAML to ensure that my DataGrid component is fully loaded before it initiates a binding on the SelectedIndex property?
My ViewModel is set up like this. I'm using MVVM-light to notify the view of changes. I pass the new model to SetData() whenever the it gets updated from the server.
public class MyViewModel : ViewModelBase
{
public void SetData(DataModel model)
{
Data = model.Data; //Array of 75 DataObjects
DataIndex = model.Index; //int between 0 and 74
}
// Array to Bind to Datagrid ItemsSource
public DataObject[] Data
{
get { return _data; }
private set
{
if (_data!= value)
{
_data= value;
RaisePropertyChanged("Data");
}
}
}
private DataObject[] _data;
// Int to Bind to Datagrid SelectedIndex
public int DataIndex
{
get { return _index; }
private set
{
if (_index != value)
{
_index = value;
RaisePropertyChanged("DataIndex");
}
}
}
private int _index;
}
The view looks like this:
<Application.Resources>
<ResourceDictionary>
<core:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
</Application.Resources>
<DataGrid ItemsSource="{Binding MyViewModel.Data, Source={StaticResource Locator}}"
SelectedIndex="{Binding MyViewModel.DataIndex, Source={StaticResource Locator}, Mode=OneWay}"
AutoGenerateColumns="True"/>
My issue is that none of the rows are selected on my DataGrid. All the data shows up in the grid correctly but the row does not get selected. I've checked the properties and confirmed that the Array length is 75 and DataIndex is an int between 0 and 74.
It seems the reason is because the DataGrid hasn't finished loading when the binding is set. I can prove this by initializing the binding after the component is loaded. In this case, everything works as expected and my selected item is displayed correctly:
<DataGrid x:Name="MyDataGrid" Loaded="OnDataGridLoaded"
ItemsSource="{Binding MyViewModel.Data, Source={StaticResource Locator}}"
AutoGenerateColumns="True"/>
private void OnDataGridLoaded(object sender, RoutedEventArgs e)
{
Binding b = new Binding("DataIndex");
b.Source = Locator.MyViewModel.Data;
MyDataGrid.SetBinding(DataGrid.SelectedIndexProperty, b);
}
I'd prefer not to have to do it like this because, you know, code-behind. So is there a way to fix this using only XAML? Here is what I've tried so far (none of which worked for me):
Binding SelectedIndex to an int property on my ViewModel (as shown above)
Binding SelectedItem to a DataObject property on my ViewModel (same result)
Binding SelectedValue and SelectedPath to a property of my DataObject (This actually worked, for the first instance only. The problem is I have multiple instances of this Datagrid component, and for some reason this only works on the first instance)
Binding to an ObservableCollection instead of an Array (tried all 3 of the above methods with an ObservableCollection and got the same results for each)
Delaying the change notification by wrapping it in a call to Dispatcher.Invoke. This doesn't help because the component is not immediately in view.
Creating the binding in XAML and then updating the target in the Loaded function. MyDataGrid.GetBindingExpression(DataGrid.SelectedIndexProperty).UpdateTarget();
My original question was missing a piece of info that was causing the issue.
It seems there's a WPF bug when the binding source is a static link. If I move the DataIndex property into the component's DataContext, then it will work correctly.
I'm not going to do this however, because the data is shared between multiple instances. I don't need to have multiple instances of the data, only the component. Therefor, I will just right it off as a Microsoft bug, and use the code-behind work around.
I will leave the question open tho, in case anyone has a solution for this bug.
I'm using Caliburn Micro on a Windows Store App.
I have a ListView which has a SelectedItem that works the first time I use it. However, when I clear the ListView and re-bind it to another Collection, the selected item no longer appears selected.
The selectedItem property is being set correctly, since I can hit the breakpoint, and everything works has expected, just the View is not being updated with the selected item, after I clear the collection.
What could be wrong?
Thanks.
Edit:
View Code:
<ListView x:Name="DetailNotes"
ItemsSource="{Binding DetailNotes}"
SelectedItem="{Binding SelectedDetailNote}"
ItemTemplate="{StaticResource Notes600ItemTemplate}"
IsItemClickEnabled="True"
caliburn:Message.Attach="[Event ItemClick] = [DetailNoteSelected($eventArgs)]"/>
ViewModel Code:
(...)
private Note selectedDetailNote;
public Note SelectedDetailNote
{
get { return this.selectedDetailNote; }
set
{
this.selectedDetailNote = value;
this.NotifyOfPropertyChange(() => this.SelectedDetailNote);
}
}
(...)
public void DetailNoteSelected(ItemClickEventArgs eventArgs)
{
Note n = (Note)eventArgs.ClickedItem;
this.SelectedDetailNote = n;
}
Sorry! The problem was my explicit binding. I just left Caliburn do his work, and now it works!
Solution below:
View Code:
<ListView x:Name="DetailNotes"
ItemTemplate="{StaticResource Notes600ItemTemplate}"/>
ViewModel Code:
private Note selectedDetailNote;
public Note SelectedDetailNote
{
get { return this.selectedDetailNote; }
set
{
this.selectedDetailNote = value;
this.NotifyOfPropertyChange(() => this.SelectedDetailNote);
}
}
I know it's late, but your problem was binding mode. You should set it to TwoWay:
SelectedItem="{Binding SelectedDetailNote, Mode=TwoWay}"
In WinRT XAML default is OneWay.
I need to bind a Label to two ListBoxes. In order to do this I have set the SelectionChanged property of both ListBoxes to the same function:
<ListBox Name="ListBox1" SelectionChanged="UpdateSelectedItem" />
<ListBox Name="ListBox2" SelectionChanged="UpdateSelectedItem" />
<Label Name="DetailsLabel" DataContent="DefinedElsewhere" />
However I am having trouble finding what the selected item actually is. I have gone through all the properties of the sending object and the SelectionChangedEventArgs but I cannot find it. The ListBox is bound to an ObservableCollection of objects, and I would like the Label to display the properties of the last selected item, no matter from which ListBox it was selected. How do I find that?
private void UpdateSelectedItem(object sender, SelectionChangedEventArgs e)
{
DetailsLabel.Content = ???;
}
You can read the selected item text doing something like:
ListBoxItem item = ((ListBox)sender).SelectedItem as ListBoxItem;
String itemText = (item != null) ? item.Content.ToString() : String.Empty;
You have to cast the SelectedItem property to the type of object you have in the list.
In this example I've used ListBoxItem.
I'm having a problem with getting a string from a bound textblock within a listbox, when I use the code below, I can bind the listbox and the listbox has items showing up, but when the item in the list is clicked I don't get the proper string, I print a message box a message with objects names like
"MyApp.Item"
shows up instead. myApp is the name of the app and Item is the name of my model that I am binding to the listbox. The proper text from the selected item showed up when the listbox was not binded.
private void listBoxtrend_Tap(object sender, GestureEventArgs e)
{
selectedText = "";
selectedText = listBox.SelectedValue.ToString();
MessageBox.Show(selectedText);
}
xml
<ListBox ItemsSource="{Binding Item}" Foreground="RoyalBlue"
Height="395" HorizontalAlignment="Center"
Margin="12,111,0,0" Name="listBox"
VerticalAlignment="Top" Width="438"
TabIndex="10" Tap="listBox_Tap" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" FontSize="26" HorizontalAlignment="Left"
Name="tblItem" Text="{Binding ItemString}"
VerticalAlignment="Top" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd really appreciate if you could help me thanks
You're binding to the ItemString in the DataTemplate's TextBlock and the Item Collection in the ListView. As such the SelectedValue will be of the Item type. You should actually be doing something like this in your Tap handler to get at the ItemString's value...
private void listBoxtrend_Tap(object sender, GestureEventArgs e)
{
selectedText = "";
var selected = listBox.SelectedValue as Item;
selectedText = selected.ItemString;
MessageBox.Show(selectedText);
}
In your example, the ToString is printing the name of the class. You could also override ToString in your Item model to be whatever you want the string to be.
Note: the types and such may be a bit off, I guessed a bit based off of what you wrote in your question. Also, there is no need to set selectedText to an empty string that will just be overwritten in the third line above. I wanted to keep it so you could get some idea of what I changed in your code.
It's very simple, try following:
string selectedText = ListBox.GetItemText(ListBox.SelectedItem);
You need to also set the SelectedItem of the Listbox to something.
SelectedItem = {Binding SelectedItem}
and rename your ItemsSource to "Items" as that makes more sense.
Your SelectedItem in your codebehind or your ViewModel should then contain a property:
public class Item
{
public string ItemString { get;set; }
}
Try This...
string ListBoxConent = ((ListBoxItem)listbox.SelectedItem).Content.ToString();
Try
listBox.SelectedItem.ToString()
If a property isn't specified in ValueMember then SelectedValue returns the results of the ToString method of the object.