CheckBox as datatemplate in a ListBox reset IsChecked value when scrolling - wpf

I want to start using MVVM in my project so I have started to investigate it.
While I was playing a bit with WPF I've encountered a bug that I couldn't find a solution to him by myself and while exploring internet.
I have something like that(I can't paste my full code because its not in the same network):
MainView.Xaml
<ListBox ItemsSource="{Binding Persons}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<my:AddToInvitation />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<my:RemoveFromInvitation />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
MainViewModel.cs
public ObservableCollection<PersonViewModel> Persons { get; set; }
public MainViewModel()
{
this.Persons = new ObservableCollection<PersonViewModel>();
for(int i=0;i<1000;i++)
{
PersonViewModel personVM = new PersonViewModel (string.Format("Person - {0}",i));
this.Persons.add(personVM);
}
}
PersonViewModel.cs
private Person PersonObject { get; set; }
public string Name
{
get
{
return this.PersonObject.Name;
}
}
public PersonViewModel(string personName)
{
this.PersonObject = new Person(personName);
}
Person.cs
public string Name { get; set; }
public Person(string name)
{
this.Name = name;
}
Now if you try to paste it and run it, it will look just fine.
The problem is when you try the following instructions:
1) Check the first 10 persons in the ListBox.
2) Scroll down the ListBox to the bottom of it.
3) Leave the mouse when the list box is scrolled down.
4) Scroll back up to the top of the ListBox.
5) Poof! you'r checking disappeared.
Now the solution i have found to this is to add IsChecked property(Though I don't really need it) to the PersonViewModel and bind it to the CheckBox IsChecked DependencyProperty, but then I have added a functionality that lets the user to press a button and it will iterate over all the persons in the ListBox and change it IsChecked property to true(Button -> Select all).
Following to the disappear Checks bug I have crossed another bug which I believe somehow is connected to the disappearing Checks - the actions that I have put to trigger when Check and Uncheck occurs would trigger only for some of the CheckBoxes when you select all.
I tried to count how many times the actions would happen when I used the select all function and I found a connection between the height of the ListBox(Current Visible CheckBoxes) and the amount of the triggers that fired, furthermore I scrolled to the middle of the ListBox and used the SelectAll functionality and the triggers didn't fire until the loop encountered the first visible ChekBox that I can see in my ListBox.
Its a bit hard to understand this bug if you don't try it, so please comment here only if you tried this.
Thanks in advance!

The simple answer is: You are going against the current.
The binding is all about changing value in your ViewModel and allowing you to write your code against simple view model classes so that your presentation logic is free of business logic. In your example the decision to execute AddToInvitation RemoveFromInvitation is in your view and it should not be there.
You will be good with bool IsInvited{get;set;} property that is easily bound to checkbox (no dependency property required). And this will allow user changes to be persisted in your view model. If you need some other more complicated logic you should attach to PropertyChagned event form INotifyPropertyChanged interface that your ViewModel must implement. Then you can change property in your simple class at will and ui will update accordingly.

Related

How can I realize SelectionChanged in MVVM ListBox Silverlight

The ListBox control does not implement a Command property. I have to attach some functionality to the SelectionChanged event. Somebody knows how can I do it? Please help me
I prefer using a binding to the SelectedItem and implementing any functionality in the setting of the binding property.
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" />
...
public class ViewModel
{
public IEnumerable<Item> Items { get; set; }
private Item selectedItem;
public Item SelectedItem
{
get { return selectedItem; }
set
{
if (selectedItem == value)
return;
selectedItem = value;
// Do logic on selection change.
}
}
}
This is the way where You can Reach the Selection changed events in Your MVVM Application
First Of all i tell you that Command Property only work in Button now we have to Explicitly
binding that property in our Selection Changed event like List box or combo box
in Your XMAL file
<ListBox Name="MyListBox" ItemsSource="{Binding ListItems}" Height="150" Width="150" Margin="281,32,-31,118">
<Local:Interaction.Triggers>
<Local:EventTrigger EventName="SelectionChanged">
<Local:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=MyListBox,Path=SelectedItem}"/>
</Local:EventTrigger>
</Local:Interaction.Triggers>
</ListBox>
for this you have to add dll Syatem.Windows.Interactivity
now u have to add references in your xaml file namespace like
xmlns:Local="clr-namespace:System.Windows.Interactivityassembly=System.Windows.Interactivity"
in your ViewModel Class you have to define your Command in Con structure
public ViewModel123()
{
MyCommand = new RelayCommand<string>(TestMethod);
}
now create the TestMethod method which can handle the selection changed event
private void TestMethod(string parameter)
{
MessageBox.Show(parameter);
}
i hope this may help u.
Basically you have a few options:
Use the property SelectedItem of ListBox to bind to a property in the backend (ie in view model) and perform logic in the setter as described by Cameron MacFarland.
Use a third party library that has a generic event to command behavior like in the link posted by Pedro Lamas.
If you don't want to use third party libraries or writing logic inside property setter is somehow unacceptable you can create your own behavior for ListBox control. It would subscribe to control's SelectionChanged event and execute a command (the command could be a dependency property exposed by the behavior).
Think this post from Laurent Bugnion will help you solve the problem:
http://geekswithblogs.net/lbugnion/archive/2010/05/19/handling-datagrid.selecteditems-in-an-mvvm-friendly-manner.aspx
The post above mentions the DataGrid but I do think it will work with the ListBox too!
Best regards and Happy New Year!! :)
I would suggest using RelayCommand. Either use the MVVM Light Toolkit or just use the RelayCommand and CommandManager classes from Josh Smith's implementations. I personally use just the two classes, so I don't need the entire toolkit.
While this will definitely work, there might be an easier way depending on what you are doing. It might just be easier to bind an object to the SelectedValue of the ListBox and listen for that value to change.

How to bind multiple selection of listview to viewmodel?

I am implementing a listview, and a button next to it. I have to be able that when i select multiple items in a listview, and then click on a button, then the selected items are put into a list. But my question is , how do i bind the selected items towards the viewmodel?
I changed my selectionmode to multiple. But then, do i just have to do:
SelectedItem={Binding path= selectedItems}
and then make in my viewmodel a property selectedItems, and it will set these items i have selected? Or what is the right solution to do this?
Like Doctor has already pointed out, you can bind SelectedItems to XAML CommandParameter
After a lot of digging and googling, I have finally found a simple solution to this common issue.
To make it work you must follow ALL the following rules:
Following Ed Ball's suggestion', on you XAML command databinding, define CommandParameter property BEFORE Command property. This a very time-consuming bug.
Make sure your ICommand's CanExecute and Execute methods have a parameter of object type. This way you can prevent silenced cast exceptions that occurs whenever databinding CommandParameter type does not match your command method's parameter type.
private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)
{
// Your goes here
}
private bool OnDeleteSelectedItemsExecute(object SelectedItems)
{
// Your goes here
}
For example, you can either send a listview/listbox's SelectedItems property to you ICommand methods or the listview/listbox it self. Great, isn't it?
Hope it prevents someone spending the huge amount of time I did to figure out how to receive SelectedItems as CanExecute parameter.
It's kind of tricky to do this Mutliple Selection in MVVM, because the SelectedItems property isn't a Dependency Property. However, there are some tricks you can use. I found this triology of blog posts that describe the matter in some details and provide some useful solutions.
Part I
Part II
Part III
Hope this helps
If you are using System.Windows.Interactivity and Microsoft.Expression.Interactions already, here is a workaround without any other code/behaviour to mess around. If you need these, it can be download from here
This workaround make use of interactivity event trigger and interactions set property mechanism in above assemblies.
Additional namespace declaration in XAML
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
XAML:
<ListView Name="MyListView" ItemsSource="{Binding ModelList}" DisplayMemberPath="Name" Grid.Column="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<ei:ChangePropertyAction TargetObject="{Binding Mode=OneWay}" PropertyName="SelectedItems" Value="{Binding Path=SelectedItems, ElementName=MyListView}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
View Model:
public class ModelListViewModel
{
public ObservableCollection<Model> ModelList { get; set; }
public ObservableCollection<Model> SelectedModels { get; set; }
public ModelListViewModel() {
ModelList = new ObservableCollection<Model>();
SelectedModels = new ObservableCollection<Model>();
}
public System.Collections.IList SelectedItems {
get {
return SelectedModels;
}
set {
SelectedModels.Clear();
foreach (Model model in value) {
SelectedModels.Add(model);
}
}
}
}
In example above, your ViewModel will pick up the selected items whenever the selection on ListView changed.
What you can do is you can handle the Button_Click(...) in your code-behind. Then in that code-behind method you can create a List of selected items by iterating over the selected items of the listView.
Since it is allowed to access the ViewModel from the View you can now call a method on your ViewModel and pass the list of selected items as a parameter.
I'm not sure if this would also work with Bindings only, however it is not bad practice to use code-behind as well.
Example Code:
public void Button_Click(object sender, EventArguments arg)
{
List<ListViewItem> mySelectedItems = new List<ListViewItem>();
foreach(ListViewItem item in myListView.SelectedItems)
{
mySelectedItems.Add(item);
}
ViewModel.SomeMethod(mySelectedItems);
}
EDIT
Here is a minimalist example, XAML:
<DataTemplate
x:Key="CarTemplate"
DataType="{x:Type Car}">
</DataTemplate>
<ListView x:Name="myListView"
ItemsSource="{Binding Path=Cars}"
ItemTemplate="{StaticResource CarTemplate}">
</ListView>
CODE-BEHIND:
public void Button_Click(object sender, EventArguments arg)
{
List<Car> mySelectedItems = new List<Car>();
foreach(Car item in myListView.SelectedItems)
{
mySelectedItems.Add(item);
}
ViewModel.SomeMethod(mySelectedItems);
}
Unfortunately the SelectedItems is a read only not bindable property.
I found a lot of help from this article How to Databind to a SelectedItems property in WPF
If you are using Metro/WinRT you may want to look at the WinRTXXAMLToolkit as it offers a bindable SelectedItems dependency property as one of its extensions.
You can't bind, but you can send to Command as an CommandParameter.
As a slight variation on Christian's post, I implemented similar code using the ListView.SelectionChanged event. Instead of calling a method on the ViewModel, I set a property called SelectedItems:
public void ListView_SelectionChanged( object s, SelectionChangedEventArgs e ) {
List<Car> mySelectedItems = new List<Car>();
foreach( Car item in myListView.SelectedItems )
mySelectedItems.Add(item);
ViewModel.SelectedItems = mySelectedItems;
}
This way, ViewModel.SelectedItems is available for any command you might have in your ViewModel and it can be used for data binding (if you turn it into an ObservableCollection).
I did a solution for this, to me this was simple enough.
<ListBox ItemsSource="{Binding ListOfModel}" x:Name="ModelList"
SelectedItem="{Binding SelectedModel, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ExecuteListBoxSelectionChange}" CommandParameter="{Binding ElementName=ModelList}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
Then in the viewmodel:
public ICommand ExecuteListBoxSelectionChange { get; private set; }
ExecuteListBoxSelectionChange = DelegatingCommand<ListBox>.For(ListBoxSelectionChnageEvent).AlwaysEnabled();
SelectedModels is the list where I wanted the selection to be filled.
private void ListBoxSelectionChnageEvent(ListBox modelListBox)
{
List<ModelInfo> tempModelInfo = new List<ModelInfo>();
foreach(ModelInfo a in modelListBox.SelectedItems)
tempModelInfo.Add(a);
SelectedModels = tempModelInfo;
}

MVVM Listbox Update Content Maintain Selected Item Silverlight

I've been reading a lot about MVVM (using Laurent Bugnion's library in specific) and I'm constantly struggling to determine how to do things in MVVM that were otherwise easy with code behind.
Here's just one example where I suspect I'm doing things the hard way. If anyone has the time to read all this, perhaps they can comment on the sanity of my approach. :)
I have a list box bound to a ViewModel like so:
<ListBox x:Name="lstFruitBasketLeft" ItemsSource="{Binding FruitBasket}"
SelectedItem="{Binding SelectedFruit, Mode=TwoWay}" Width="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"
HorizontalAlignment="Left" Margin="2">
<TextBlock Text="{Binding Name}" />
<TextBlock Text=":" />
<TextBlock Text="{Binding Quantity}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
The ItemSource is an ObservableCollection of Fruit objects:
public class Fruit
{
public string Name { get; set; }
public int Quantity { get; set; }
public Fruit() { }
public Fruit(string name, int quantity)
{
this.Name = name;
this.Quantity = quantity;
}
}
It is defined in the ViewModel as:
// Property FruitBasket
public const string FruitBasketPropertyName = "FruitBasket";
private ObservableCollection<Fruit> _fruitBasket = null;
public ObservableCollection<Fruit> FruitBasket
{
get { return _fruitBasket; }
set
{
if (_fruitBasket == value)
return;
_fruitBasket = value;
// Update bindings, no broadcast
RaisePropertyChanged(FruitBasketPropertyName);
}
}
The bound SelectedItem property is as such:
//Property SelectedFruit
public const string SelectedFruitPropertyName = "SelectedFruit";
private Fruit _selectedFruit = null;
public Fruit SelectedFruit
{
get { return _selectedFruit; }
set
{
if (_selectedFruit == value)
return;
var oldValue = _selectedFruit;
_selectedFruit = value;
// Update bindings, no broadcast
RaisePropertyChanged(SelectedFruitPropertyName);
}
}
Then, the list is populated on the construction of the ViewModel.
Now, I add a RelayCommand to a button on the presentation page that executes a method which increments the quantity of the selected item. Note that I am not using the parameter yet, but "Bob" is a placeholder for some changes for later.
<Button x:Name="butMore" Content="More!" HorizontalAlignment="Right" Height="25" Width="75" Margin="4">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand
Command="{Binding addMoreCommand}"
CommandParameter="Bob" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Here's the code for the command:
// Property addMoreCommand
public RelayCommand addMoreCommand
{
get;
private set;
}
...
//Init relays (this is in the constructor)
addMoreCommand = new RelayCommand(AddFruit, CanExecute);
...
public void AddFruit()
{
//Increment the fruit
SelectedFruit.Quantity++;
//Save the previous selected item
Fruit oldSelectedItem = SelectedFruit;
//We have to have a new list in order to get the list box to refresh
FruitBasket = new ObservableCollection<Fruit>(FruitBasket);
//Reselect
SelectedFruit = oldSelectedItem;
}
public bool CanExecute()
{
return true; //for now
}
Now this does work, but I have some problems with it:
First, I feel like there are a lot of conditions that have to come together for this to work and I wonder if I'll get so lucky trying to move some Telerik Drag and Drop code into MVVM.
Second, it seems like a pretty poor performance approach to recreate the list like that.
Lastly, it seems like this would be easier in code behind (though I'm not 100% certain I still won't have to rebuild that list).
Does anyone have any thoughts on my approach or perhaps even... suggestions to make things easier? Am I just missing something obvious here?
Thanks
-Driodilate :]
maulkye,
There is something going wrong if you have to refresh your ObservableCollection. Usually, you should not need it because the ObservableCollection will notify about item changes.
Never do this:
FruitBasket = new ObservableCollection<Fruit>(FruitBasket);
Your public ObservableCollection<Fruit> FruitBasket should have no public setter, it should be read only. Just Add or Remove Items to/from the list.
If you want to handle multiple selections, you will probably need an extended CollectionView which can handle this, get more hints here.
I hope this helps a little bit, even if I probably didn't answer all questions : )
EDIT:
Ok, I guess i got some things wrong. Now i guess i fully understand what you're trying to accomplish. You are not getting notified when your property is changed, right? Well, for this reason, we've adapted "BindableLinq" in one of our projects, which you can compile in Silverlight without problems. (there are similar solutions available, called Continuous Linq or Obtics, make your choice).
Using BindableLinq, you can transform your ObservableCollection to a BindableCollection using one single extension method. The BindableCollection will then reflect all changes properly. Give it a try.
EDIT2:
To implement a proper ViewModel, Please consider the following Changes.
1) Fruit is your Model. Since it doesn't implement INotifyPropertyChanged, it won't propagate any changes. Create a FruitViewModel, embedding your Fruit Model and invoke RaisePropertyChanged for each property setter.
2) Change your FruitBasket to be an ObservableCollection of FruitViewModel. Slowly it starts to make sense :)
3) SelectedFruit has to be a FruitViewModel as well. Now it makes even more sense.
4) Now it already works for me, even without BindableLinq. Did you have any success?
HTH
best regards,
thomas

In C# (or in C# with WPF), how would I build a checkbox at run time?

In C# (or in C# with WPF), how would I build a checkbox at run time?
I would I be able to query the check box to see if the user clicked on it?
In other words, suppose I have a "grid" on which I want to have displayed some checkboxes. But I do not know how many checkboxes to display. I suppose I could (in WPF) fill the grid with checkboxes at design time and mark them as hidden (or visibly == false) and then show them at run time. But I was hoping there was a more elegant way to do this.
There are several ways to do this in WPF. A quick and dirty approach would be to do something like this:
<StackPanel x:Name="CheckBoxes" />
Then in your code behind do:
for (int i=0; i < 10; i++) {
this.CheckBoxes.Children.Add(new CheckBox());
}
But while at first glance it looks simple, this makes it somewhat of a pain to work with in the long run. Instead, a better solution would be to have a class that has a boolean property such as:
// this should really implement INotifyPropertyChanged but
// we'll ignore that for now...
public class SelectableThing {
public bool IsSelected {
get;
set;
}
public string Description {
get;
set;
}
}
Then in your XAML, you would have a bindable control such as ItemsControl:
<ItemsControl x:Name="CheckBoxes">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
Content="{Binding Description}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then in your code behind you could create a collection of these SelectableThing's and set them as the ItemsSource.
private SelectableThing[] things;
// where you do this is up to you really
private void Window_Load(object sender, RoutedEventArgs e) {
things = new SelectableThing[] {
new SelectableThing("First Thing"),
new SelectableThing("Second Thing"),
new SelectableThing("Third Thing")
};
CheckBoxes.ItemsSource = things;
}
In an event handler or something like that, eventually a method that gets called, you could do this. Let's say your Canvas is called myCanvas.
var cb = new CheckBox { //... set the properties, e.g.:
Checked = true, Content = "Check me" };
// do whatever you like to do with your newly created CheckBox
myCanvas.Children.Add(cb);
Hope this helps; of course you can do this inside a loop. If you need to hold a specific set of references to the created CheckBoxes be aware of that or use the Tag Property to identify these special CheckBoxes. Also, you could check myCanvas.Children for CheckBoxes.

Sending data from view to viewmodel with command binding

Question:
How do send data to a view model when using command binding? So that, for example, when i click a button, it sends the "currently selected index" of a list so that it can perform an operation on that item of the list
Further Information:
I'm working on a program where i have a list of shipments, and each shipment has a list of pallets. I want to make a button that will allow me to add a new pallet to the currently selected shipment. >Edit> And to through another wrench into the works, each pallet has a list of products. so not only do i need to know what shipment i'm on, but i also need to know what pallet of what shipment I'm on.
When I do a command binding, I have no idea how to send the data to the ViewModel. I would like to keep this pure MVVM so i don't want to have the ViewModel checking the view for anything.
~N
Edits:
11/04/09 - I removed the section of the question about the instantiation of the ViewModel. I'll ask that again in a different question as this one is well on track for solving the other question. And I made a few other edits to the question to clearify in the direction i want. as well as changed some grammatical bits so that it wasn't talking about two questions when there is only one.
I usually expose a CollectionView from the view model and set the IsSynchronizedWithCurrentItem property on the ItemsControl displaying the list in the view. Then when the command is executed, I can inspect the CollectionView.CurrrentItem propety to see what is currently selected.
EDIT: This answer addresses the first question in your, um, question. Rather than your view sending the currently selected item to the ViewModel, the ViewModel keeps track of the currently selected item. So using this technique you don't need to work out how to send that information.
Something like this in your view model:
class ApplicationViewModel
{
// Exposes a list of ShipmentViewModels.
public CollectionView Shipments { get; private set; }
// A DelegateCommand or similar, that when executed calls AddPallet().
public ICommand AddPalletCommand { get; private set; }
void AddPallet()
{
ShipmentViewModel shipment = (ShipmentViewModel)Shipments.CurrentItem;
shipment.Pallets.Add(new PalletViewModel(...));
}
}
And then this in your xaml:
<ListBox ItemsSource="{Binding Shipments}" IsSynchronizedWithCurrentItem="True"/>
<Button Command="{Binding AddPalletCommand}>Add Pallet</Button>
This way you can also track the selection of the Shipments collection from your ViewModel and update the command's CanExecute state.
Does that help any?
For keeping track of the currently selected item I do something similar to Groky, maybe this example make a little more sense.
In your ViewModel that contains the collection that your list is bound to (I'm using a ListBox in this example) expose a property that relates to the selected item.
// Assuming your using the MVVM template from Microsoft
public class PalletListViewModel : ViewModelBase
{
// The collection our list is bound to
private ObservableCollection<Pallet> _palletList;
// The current selected item
private Pallet _selectedPallet;
// Our command bound to the button
private DelegateCommand _processCommand;
public ObservableCollection<Pallet> PalletList
{
get { return _palletList; }
}
public Pallet SelectedPallet
{
get { return _selectedPallet; }
set
{
if(value == _selectedPallet)
return;
_selectedPallet = value;
// INotifyPropertyChanged Method for updating the binding
OnPropertyChanged("SelectedPallet");
}
}
public ICommand ProcessCommand
{
get
{
if(_processCommand == null)
_processCommand = new DelegateCommand(Process);
return _processCommand;
}
}
private void Process()
{
// Process the SelectedPallet
}
}
<Window ...>
<Grid x:Name="LayoutRoot">
<Button Content="Process Pallet" Command="{Binding ProcessCommand}" />
<ListBox ItemsSource="{Binding PalletList}" SelectedItem="{Binding SelectedPallet}">
...
</ListBox>
</Grid>
</Window>
Hopefully this is what your looking for.

Resources