WPF: Binding listview itemsource from viewmodel in MVVM [duplicate] - wpf

This question already has an answer here:
Creating and binding buttons dynamically in a WrapPanel
(1 answer)
Closed 5 years ago.
I am trying to set ItemSource property for listview without success.
View (xaml):
<ListView Margin="10" Name="MyLv" ItemsSource="{Binding}">
....
</ListView>
Code-Behind Constructor (xaml.cs):
public MyView()
{
InitializeComponent();
}
ViewModel:
private List<DataModel> lstData = null;
public MyViewModel()
{
this.lstData = this.LoadData(); // this connects to a database an extract info to be loaded in listview
}
Data Model:
public class DataModel
{
public bool IsSelected { get; set; }
public string ID { get; set; }
public string Desc { get; set; }
}
Before this, I was loading the listview from code-behind and it was working, but now I want to load it from my viewmodel and I do not know how can I make it work.

Based on the code you posted, there are a couple of problems:
You haven't set the DataContext of the view. You typically want to set this to an instance of your view model class.
The ViewModel doesn't expose the list as a public property. WPF bindings only work on public properties. The ItemsSource should be bound to this property, and not to the DataContext itself.
Finally, you probably want the collection in the ViewModel to be an ObservableCollection. This way, when changes are made to the collection, the list in the UI will be automatically updated.

Related

How to use IObservable<T> as a source for a WPF source binding to a ListBox?

So this is what I am currently doing:
I have a WPF ListBox that is currently data bounded to and populated by a
public ObservableCollection<string> SourceBinding
{
get;
set;
}
This WPF bounded source has an OnCollectionChanged event handler that does the following whenever a new item is added;
ObservableCollection<string> source = new ObservableCollection<String>();
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("NotifyPropertyChanged")
}
I am also subscribed to an IObservable<string> stream that is handling each tick via the TickHHandler event handler;
Stream.Subscribe(TickHandler);
TickHandler is currently doigng this:
private void TestSubscription( string item)
{
sourceBinding.Add(item)
}
Here, for each output tick event from the Rx stream, the ObservableCollection is updated and the WPF GUI is notified of the changes that need to be made.
What I would like to do however, is bind the ListBox directly to my IObservable<string> stream preferably inside xaml.
I am assuming that I'd somehow have to use Behaviors to expose a custom IObservableItemsSource property to bind the IObservable<string> source for consumption. I imagine it would look something like this in the end:
IObservableItemsSource ="{Binding IObservableSource}"
I know how to implement Behavior, but I have no idea how to start creating an IObservable<string> property for use in xaml, or if this is even possible!
Am I way off the mark? Can someone explain what the best way to implement this should be?
Thanks.
The Observable Collection implement INotifyCollectionChanged which will alert the xaml that it needs to update the view. So if you just add to the collection as you are doing it should update the UI automatically as long as you are binding it correctly to the ViewModel.
This is the view
<ListBox ItemsSource="{Binding Collection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the code behind
public class ViewModel
{
public ObservableCollection<string> Collection { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.Collection = new ObservableCollection<string>();
this.DataContext = vm;
vm.Collection.Add("Item");
vm.Collection.Add("Item");
vm.Collection.Add("Item");
vm.Collection.Add("Item");
vm.Collection.Add("Item");
}
Note that as long as you are doing your processing in the UI thread you this will work. If you are doing it in a background thread you will need to dispatch and add to the observable collection in the UI thread.

WPF Listbox Items

I have a ListBox control that contains the names of files inside a directory.
How would I iterate though the controls and get those names? I've tried:
for (int i = 0; i < listboxFileGroups.Items.Count; i++)
{
// I don't want to use properties that start with Selected
// Here is what I was looking for
string textItem = listboxFileGroups.Items[i].ToString();
}
Any suggestions?
You may want to explore an MVVM approach.
In one of your view model classes, you can have an ObservableCollection<string>:
public ObservableCollection<string> StuffForListBox { get; set; }
Populate that ObservableCollection with what you want to get displayed in the ListBox.
In the code-behind of the UserControl or Window in which you have the ListBox, set the DataContext to an instance of the class containing StuffForListBox seen above.
this.DataContext = new MyClass();
Alternatively you could also create a datatemplate for the usercontrol / window which will automagically wire up the datacontext with your view model.
Since you only mentioned that you want to display the files in a directory (not including sub-directories), you just need to bind the ItemsSource to StuffForListBox.
<ListBox ItemsSource="{Binding StuffForListBox}" ... >
To iterate through the strings displayed in the ListBox you just need to iterate through the ObservableCollection.
If you don't want to bother with MVVM or if that is some third party listbox, you can try grabbing the ItemsSource in the codebehind and loop through that but I'd certainly recommend MVVM. It'll make your life easier.
Now, if you wanted to get a little crazier and display things like subfolders then an ObservableCollection<string> won't cut it. You would need to create a class that contains children to model how a folder has files and subfolders.
public class DemoItem
{
public string Name { get; set; }
public DemoItem Parent { get; set; }
public ObservableCollection<DemoItem> Children { get; set; }
public bool IsSelected { get; set; }
}
...and then base your observable collection thats bound to the listbox on the above class.
If and when you do that, your listbox won't display the items properly until you create a DataTemplate But I suppose that't outside of the scope of the question :p

Adding databing to a datagrid?

I have a simple datastructure following:
In the model I have
public class Receipt
{
public int Id { get; set; }
public double Price { get; set; }
public string Store { get; set; }
public DateTime Date { get; set; }
}
I've made two of these objects and I am trying to bind them to a datagrid. I've filled in the properties of the two receipts and added them to the dataGridRows but they don't show up in my DataGrid.
public MainWindow()
{
InitializeComponent();
makeReceipts()
}
public ObservableCollection<Receipt> dataGridRows = new ObservableCollection<Receipt>();
public Receipt receipt1 = new Receipt();
public Receipt receipt2 = new Receipt();
public void makeReceipts()
{
receipt1.Id = 1;
receipt1.Price = 10;
receipt1.Store = "Brugsen";
receipt1.Date = DateTime.Today;
receipt2.Id = 2;
receipt2.Price = 15;
receipt2.Store = "Netto";
receipt2.Date = DateTime.Today;
dataGridRows.Add(receipt1);
dataGridRows.Add(receipt2);
}
And in the xaml of the MainWindow where I want my datagrid to display the receipts I have:
<DataGrid Name="ReceiptGrid" CanUserResizeColumns="True" IsReadOnly="True" AutoGenerateColumns="True" ItemsSource="{Binding Source=dataGridRows}" />
What am I doing wrong?
first you can just bind to public properties.
so if you want to use binding you have at least do:
public ObservableCollection<Receipt> dataGridRows {get;set;}
second you have to do two steps:
set the right datacontext
set the right binding expression(Path)
assume that the datacontext for yyour grid is an object with the property dataGridRows, your binding should look like this
<DataGrid ItemsSource="{Binding Path=dataGridRows}" .../>
Think your problem is you have to write
ItemsSource="{Binding Path=dataGridRows}"
and not
ItemsSource="{Binding Source=dataGridRows}"
source is to specify another control in xaml file
First of all you can bind only public properites so you need to change definition of dataGridRows to something like this:
public ObservableCollection<Receipt> dataGridRows { get; set; }
then you don't bind it as a Source but as a Path, however since your dataGridRows is defined in MainWindow you need to specify Source as your MainWindow otherwise it will look in default DataContext which is not set in your case
<DataGrid ... ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=dataGridRows}" />
this tells Binding to find Window and look for a dataGridRows property there.EDIT:Normally you don't put data into view. I suggest you read more about MVVM design pattern but basically the idea is that you have your ViewModel where you put in you whole application logic, unaware of interface, and then on top you have your view to interact with user but in ViewModel you don't operate on controls.What you should do is create your view-model class with dataGridRows property and assign it through DataContext of Window for example. Each FrameworkElement has it and when you don't specify Binging source (Source, RelativeSource, ElementName) it will try to resolve Binding.Path in current DataContext. If current control does not have it specified then if will go to parent in visual tree and so on.

How to set datacontext for each combobox?

I'm having a problem about binding in combobox ( WPF, MVVM).
I have a combobox, which binds to AViewModel ( for example).
To do that, I did have:
- AModel
- AViewModel
- Xaml file :
<Window.DataContext>
<ViewModel:AViewModel/>
</Window.DataContext>
It works fine.
But, now, I add one more combobox to the same form with combobox above. This combobox binds to diffirent ViewModel (BViewMoel for example, note that, this BViewModel located in diffirent file with AViewModel above).
And this is combobox xaml:
<ComboBox
DataContext="BViewModel"
ItemsSource="{Binding Path=MyList}" DisplayMemberPath="BName"/>
My problem is: the second combobox is not populated because it does not have datacontext.
But I cannot set datacontext for it because it is set above for AViewModel.
I did a lots of searching but I still stuck in this.
Should I merge all ViewModels into a ViewModel and set this to Datacontext of Window or any ideal?
Thank you.
Really, I wouldn't use a ViewModel for each combobox. Combobox is a simple control, you should bind the ItemsSource property to a public property (of type ObservableCollection<T> for instance) of the ViewModel of the owner view.
Sometimes it's useful to use a ViewModel for a specific and complex usercontrol. In this case, you can expose the viewModel as a public property of the ViewModel of the owner view:
public class UCViewModel : ViewModelBase
{
}
public class MyViewViewModel : ViewModelBase
{
public MyViewViewModel()
{
this.UCViewModel = new UCViewModel();
}
public UCViewModel UCViewModel { get; set; }
}
<Window x:Class="MyView">
<MyComplexUsercontrol DataContext="{Binding UCViewModel}" />
</Window>
public partial class MyView : Window
{
public MyView()
{
InitializeComponent();
this.DataContext = new MyViewViewModel();
}
}
But again, for a simple combobox, just bind it to a property of the ViewModel associated with the owner view.
combobox1.DataContext = new AViewModel();
combobox2.DataContext = new BViewModel();
But I suggest using a ViewModel contains two properties.
public class ViewModel
{
public AViewModel AViewModel{get;set;}
public BViewModel BViewModel{get;set;}
}

Binding a save command WPF

I have a window with 3 textboxes in a grid -this is my view- and I have Save button to add a new user to my user list with the datas from the textboxes.
I want to use a relay command to do this on my viewmodel class but I am quite confused with how to make the bindings. I hope it's clear enough. Any ideas, or examples will be helpful.
thanks in advance.
You should have a ViewModel something like the following :
class UserViewModel
{
public String Name { get; set; }
public String Password { get; set; }
public String Email { get; set; }
public RelayCommand AddUserCommand { get; set; }
public UserViewModel()
{
AddUserCommand = new RelayCommand(AddUser);
}
void AddUser(object parameter)
{
// Code to add user here.
}
}
And you can use it like following :
<StackPanel>
<TextBox Text="{Binding Name}"></TextBox>
<TextBox Text="{Binding Password}"></TextBox>
<TextBox Text="{Binding Email}"></TextBox>
<Button Command="{Binding AddUserCommand}">Add</Button>
</StackPanel>
To make this work, put following code in your UserControl/Control/Window's constructor :
DataContext = new UserViewModel();
I presume that you read Josh Smith article: WPF Apps With The Model-View-ViewModel Design Pattern. If you didn't, then read it first, and then download code, because example is very similar to your problem.
Did you created an instance of the ViewModel and putted this instance in the DataContext of your view or stackpanel?
example:
UserViewModel viewModel = new UserViewModel();
UserWindow view = new UserWindow();
view.DataContext = viewModel;
view.Show();
There are several options on coupling the View and the Viewmodel:
Create the View and ViewModel and set the ViewModel to the DataContext property (code above)
Create the ViewModel in the constructor of the View and fill the DataContext property with it
Create a Resource in your view of the type of your ViewModel and fill the DataContext property in XAML
I prefer the first option because you can combine the Views and Viewmodels as you like at runtime.
Hopefully this is a helpfull answer.

Resources