Click here to Skip to main content
15,885,546 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am trying to rewrite my CheckBoxList from Code Behind to MVVM pattern in WPF C#. The thing is that now I have a problem to get all selected checkboxes. I have implemented INotifyPropertyChanged interface. Project is building correctly, when I set the break point I can notice that I received always false value for my checkboxes, even if they are selected. I assume that maybe I did something wrong with data binding. Please, does anybody can help? I am totally newbie in MVVM.

What I have tried:

 public class ObservableObject : INotifyPropertyChanged
    {

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string strPropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(strPropertyName));
        }

        #endregion

    }
}


public class SharedModel : ObservableObject
   {
               private bool _fIsSelected;

               public bool IsSelected
               {
                   get => _fIsSelected;
                   set
                   {
                       _fIsSelected = value;
                       OnPropertyChanged("IsSelected");
                   }
               }

       public bool IsSelected { get; set; }

       public string Name { get; set; }

       public string Method { get; set; }
   }



class TestViewModel : ObservableObject
    {

        public ObservableCollection<SharedModel> List { get; set; } = new ObservableCollection<SharedModel>
        {
            new SharedModel
            {
                Name = "A1",
                Method = Test(),
            },
            new SharedModel
            {
                Name = "A2",
                Method = TestOne()
            }
};

        public string GetSelectedCheckboxes()
        {
             var command =
                    from item in List
                    where item.IsSelected
                    select item.Method;
          return string.Join("\r&", new NewList<string>(command));
        }


<StackPanel Margin="0,0,769,510">
            <ListBox Name="ListBox"
                     ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                     ItemsSource="{Binding List}"
                     SelectionMode="Multiple" Background="{x:Null}" Margin="0,133,590,470" Foreground="White" BorderBrush="{x:Null}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"
                                    MinWidth="170" MaxWidth="170"
                                    Margin="0,0, 0, 0" >
                            <CheckBox x:Name="TestCheckbox"
                                      Tag="{Binding Method}" IsChecked="{Binding IsSelected}" />
                            <ContentPresenter
                                Content="{Binding Name}"
                                Margin="5,0, 15, 0" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
Posted
Updated 8-Feb-18 8:57am
Comments
George Swan 10-Feb-18 2:39am    
The IsSelected property needs to call OnPropertyChanged() in its setter

1 solution

First, you don't implement OnChanged event in your model, only the view model.
Next, one thing you have to be careful with in databinding is the "new" keyword.

The practice that I have found to work the best for me is declare the variable

privateObservableCollection<SharedModel> _List;
public ObservableCollection<SharedModel> List 
{ 
    get { return _List; } 
    set { _List = value; OnPropertyChanged("List");} 
}


Then I initialize it in the constructor so that it is only using the "new" keyword one time. The reason for this is that the "new" keyword will often "break" the binding. I am not sure if you want to modify the list from the viewmodel, so I usually specify Two-Way for the mode so that I can change items from both the view and viewmodel. I also change the update source trigger, but IIRC lostfocus is the default.

XML
<Listview Itemssource="{Binding List, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> 

Then in your item template, your IsSelected should update.
<ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"

                                    MinWidth="170" MaxWidth="170"

                                    Margin="0,0, 0, 0" >
                            <CheckBox x:Name="TestCheckbox"

                                      Tag="{Binding Method}" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                            <ContentPresenter

                                Content="{Binding Name}"

                                Margin="5,0, 15, 0" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>



I don't know if you use a command, or event or whatever you do to get to GetSelectedCheckboxes(), but there are a million different ways to return all of the true selections. one way might be:



C#
public string GetSelectedCheckboxes()
        {
             List<string> yourlist = List.where(l => l.IsSelected == true).Select(x => x.Method).ToList();
             return yourlist;   
        }


Hope this helps, if you need me to clarify further I will just let me know.


Edit: To show you how I set it up.

public class SharedModel
   {
       public bool IsSelected { get; set; }

       public string Name { get; set; }

       public string Method { get; set; }
   }


C#
public class TestViewModel : ObservableObject
{
    privateObservableCollection<SharedModel> _List;
    public ObservableCollection<SharedModel> List 
    { 
        get { return _List; } 
        set { _List = value; OnPropertyChanged("List");} 
    }

    public TestViewModel()
    {
        List = new ObservableCollection<SharedModel>()
        {
            new SharedModel()
            {
                Name = "A1",
                IsSelected = false
            },
            new SharedModel()
            {
                Name = "A2",
                IsSelected = false
            }
        };
    }

    public List<string> GetSelectedCheckboxes()
    {
        List<string> selectedCheckboxes = List.Where(x => x.IsSelected == true).Select(x => x.Name).ToList();
        return selectedCheckboxes;
}


This along with you setting the DataContext should work. All you have to do is wire up a button to call GetSelectedCheckboxes() and that list will be only checkboxes that are selected.
 
Share this answer
 
v3
Comments
Member 13668663 9-Feb-18 12:31pm    
Thank you for your answer :)
I am changing my code now. I would like to ask about this part:

privateObservableCollection<sharedmodel> _List;
public ObservableCollection<sharedmodel> List
{
get { return _List; }
set { _List = value; OnPropertyChanged("List");}
}

What I need is exactly notify that checkbox is slected, so I have tried follow with your suggestions and I have now something like that:


class TestViewModel : ObservableObject
{
public ObservableCollection<sharedmodel> List { get; set; }
public void CreateCheckBoxList()
{
List = new ObservableCollection<sharedmodel>
{
new SharedModel
{
Name = "A1",
Method = Test(),
},
new SharedModel
{
Name = "A2",
Method = TestOne()
}
};
}
public string GetSelectedCheckboxes()
{
var command =
from item in List
where item.IsSelected
select item.Method;
return string.Join("\r&", new NewList<string>(command));
}
....

I did also changes in xaml as you suggested. I am not sure how I can call this list in UI now. This collection should be invoke in constructor e.g. from TestList.xaml.cs?
J. Calhoun 9-Feb-18 13:06pm    
I assumed you had set the datacontext if you are seeing that your listview is being populated by your itemssource? If you are setting your datacontext then the property should update, if you need a way to call GetSelectedCheckboxes() you could always set up a button, or some sort of command control to call it.

I typically never use the xaml.cs file as I just reference the viewmodel from the xaml.

xmlns:vm="clr-namespace:yourtestviewmodelproject"

Then put it in the datacontext

<Window.DataContext>
<vm:testviewmodel>
**there should be a closing datacontext tag here but when I type it it doesnt show for whatever reason**



or if you use the xaml.cs, then in the constructor you would just set
this.DataContext = new TestViewModel();

you can do it either way I just am kind of a purist that never uses a code behind.

If you do these things and it still doesn't work, let me know and I can make a quick sample project and show you.
Member 13668663 9-Feb-18 13:18pm    
The problem is that I can't see the list in the view now

Here is how I setup the DataContext

public TestListView()
{
InitializeComponent();
viewModel = new TestViewModel();
this.DataContext = viewModel;
}
J. Calhoun 9-Feb-18 13:50pm    
In TestViewModel do you do

public TestViewModel()
{
CreateCheckBoxList()
}
J. Calhoun 9-Feb-18 14:14pm    
In updating my solution, I noticed you don't need to extend ObservableObject from your model. And I put the loading of the list in the constructor, but you can use your CreateCheckboxList() instead. Either way shouldn't matter. With the datacontext set, then all of this should work. I threw together a test project real quick, and this all works fine.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900