How do I bind multiple viewmodels to a single view - mvvm

I have two listviews on a single Content page therefore l would like for each listview to have its own viewmodel as itemsoruce.

Adding separate ItemsSource for ListView is easier. If this is what you are looking for.
It can be done with two properties in your MainViewModel.
XAMl
<ListView
ItemsSource="{Binding ListSource1}">
</ListView>
<ListView
Grid.Column="1"
ItemsSource="{Binding ListSource2}">
</ListView>
ViewModel class
public class MainViewModel
{
public ObservableCollection<string> ListSource1 { get; set; }
public ObservableCollection<string> ListSource2 { get; set; }
public MainViewModel()
{
ListSource1 = new ObservableCollection<string>()
{
"I'm from first viewmodel",
"I'm from first viewmodel",
"I'm from first viewmodel",
"I'm from first viewmodel",
"I'm from first viewmodel",
"I'm from first viewmodel"
};
ListSource2 = new ObservableCollection<string>()
{
"I'm from second ViewModel",
"I'm from second ViewModel",
"I'm from second ViewModel",
"I'm from second ViewModel",
"I'm from second ViewModel",
"I'm from second ViewModel"
};
}
}
If you are looking to add source from different ViewModels.
ListSource1 = new ViewModel1().ListSource;
ListSource2 = new ViewModel2().ListSource;
If you wish to set BindingContext of ListView this can be done by binding the Binding Context itself as Marco suggests.
But you need to add the ViewModels as well to you main ViewModel as properties.
public ViewModel1 FirstViewModel { get; set; }
public ViewModel2 SecondViewModel { get; set; }
Xaml:
<ListView
BindingContext="{Binding FirstViewModel}"

Just set your BindingContext for your ListView.
In Xaml:
<ListView x:Name="list1">
</ListView>
<ListView x:Name="list2">
</ListView>
And in Code behind just bind it:
list1.BindingContext = ViewModel1;
list2.BindingContext = ViewModel2;
If you whish to bind it in code behind, you could use the BindingContext-Property in XAML too:
<ListView ItemSource="{Binding itemList1}" BindingContext="{Binding ViewModel1}" />
<ListView ItemSource="{Binding itemList2}" BindingContext="{Binding ViewModel2}" />
But keep in mind, that you need a "global" binding Model (or MasterViewModel) which keeps the nested View Models as child properties.

Related

Bound items display as name of item type, instead of contents of each item

//My Model
public class BookInfo
{
public string BookName { get; set; }
public string BookDescription { get; set; }
}
//my View Model
public ObservableCollection<BookInfo> Bookmodel { get; set; }
public BookRepoInfo()
{
Bookmodel = new ObservableCollection<BookInfo> { //**is this correct way.**
new BookInfo { BookName = "Object-Oriented Programming in C#", BookDescription = "Object-oriented programming is a programming paradigm based on the concept of objects" },
......
};
}
XAML page:
<ContentPage.BindingContext>
<local:BookRepoInfo />
</ContentPage.BindingContext>
<X:YList ItemsSource="{Binding Bookmodel}"></X:YList>
Load the list item using MVVM pattern
Assuming that YList is either a inheritance from ListView or CollectionView, you'll need to provide some sort of template which you want to apply to each cell of that list.
Right now what is happening is that it will just call the ToString() on the object that you put in.
Change your code to be something like:
<X:YList ItemsSource="{Binding Bookmodel}">
<X:YList.ItemTemplate>
<DataTemplate>
<VerticalStackLayout>
<Label Text="{Binding BookName}"/>
<Label Text="{Binding BookDescription}"/>
</VerticalStackLayout>
</DataTemplate>
</X:YList.ItemTemplate>
</X:YList>
More information is here: https://learn.microsoft.com/dotnet/maui/user-interface/controls/collectionview/populate-data?view=net-maui-7.0#define-item-appearance

UWP ObservableCollection notifies View on change

I want to create an UWP App with a Page, which shows a list of items, which are listed in an ObservableCollection. When I add or remove an item from the list, it should be also be added or removed from the view on the GUI. Or when I change a property of an item, it should also be changed in the view on the GUI. I tried the code from another page on this platform but it did not work.
What is necessary to notify the view (ListView or TreeView), which uses the ObservableCollection as ItemsSource, on changes (new item, removed item, value within item changed) within the ObservableCollection? NotifiyPropertyChanged(<name of ObservableCollection>) does not work in this case, but works on a simple string member.
Can anyone give me a small working example code from Model, View and ViewModel?
Kind regards,
Wolfgang
Can anyone give me a small working example code from Model, View and ViewModel?
Sure, Please check the following code. And ObservableCollection could notify UI Change without NotifiyPropertyChanged when add or delete item.
public sealed partial class MainPage : Page
{
private ObservableCollection<Book> Books { get; set; } = new ObservableCollection<Book>();
public MainPage()
{
this.InitializeComponent();
GetData();
}
private void GetData()
{
for (int i = 0; i < 25; i++)
{
var book = new Book { Name = $" Book{i}", Author = $"Author{i}" };
Books.Add(book);
}
MyListView.ItemsSource = Books;
}
private int count;
private void AddClick(object sender, RoutedEventArgs e)
{
count++;
Books.Insert(0, new Book { Name = $"NewBook{count}", Author = $"NewAuthor{count}" });
}
private void DelClick(object sender, RoutedEventArgs e)
{
if (Books.Count != 0)
{
Books.RemoveAt(0);
}
}
}
public class Book
{
public string Name { get; set; }
public string Author { get; set; }
}
Xaml
<Page.BottomAppBar>
<CommandBar>
<AppBarButton
Click="AddClick"
Icon="Add"
Label="Add"
/>
<AppBarButton
Click="DelClick"
Icon="Delete"
Label="Delete"
/>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<ListView x:Name="MyListView">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Margin="23,0,0,0" Text="{Binding Author}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

how to bind an autocompletebox with a model in mvvm?

i exposed a collection and binded it to itemsource of autocompletebox which works but selecting or changing the text on the autocompletebox doesn't update the model like a textbox or a label!
viewmodel:
public ObservableCollection<String> SymptomsDb { get; private set; }
private String symptom;
public String Symptom
{
get { return symptom; }
set
{
symptom = value;
RaisePropertyChanged(() => this.Symptom);
}
}
public AnalysisViewModel()
{
List<String> s = new List<String>();
s.Add("test");
SymptomsDb = new ObservableCollection<String>(s);
}
view:
<controls:AutoCompleteBox
ItemsSource="{Binding SymptomsDb}"
SelectedItem="{Binding Symptom}"
Text="{Binding Symptom}"
IsTextCompletionEnabled="True"
FilterMode="Contains"/>
To get a change from the user interface back to the viewmodel, you will always need to bind the property TwoWay (except some properties like TextBox.TextProperty that are TwoWay by default):
<controls:AutoCompleteBox
ItemsSource="{Binding SymptomsDb}"
SelectedItem="{Binding Symptom, Mode=TwoWay}"
Text="{Binding Symptom}"
IsTextCompletionEnabled="True"
FilterMode="Contains"/>

Binding Silverlight Control to View Model element?

Say I have:
<TextBlock Text="{Binding MyString}"/>
public class AlanViewModel {
public string MyString {get; set;}
public TextBlock Control { get; set; } //How to bind this to the TextBlock control?
}
Can I bind the instance of the control to the ViewModel, or must I continue to jump through hoops in the code-behind to couple them together?
I know this couples the ViewModel to the View, but it's for a good reason.
<ContentControl Content="{Binding TextBlock}" />

Value of a textbox in silverlight + MVVM

I have a xaml names Customer.xaml like this:
<Grid x:Name="customview" >
<StackPanel x:Name="CustomPanel" >
<TextBox x:Name="CustomText" />
</StackPanel>
</Grid
Using MVVM I have created ICustomerviewmodel and Customerviewmodel like this:
public interface ICustomerviewmodel
{
ICommand SaveClientCommand { get; }
}
public class Customerviewmodel : ICustomerviewmodel , INotifyPropertyChanged
{
......
private void ExecuteSaveClient()
{
//
}
My question is how I could get the value of
inside the function ExecuteSaveClient() to save this?
You should declare a string property in your view model say:
public string CustomText { get; set; }
Assign datacontext of customview to be your viewmodel int the constructor, hope this grid is in a UserControl or Window:
this.customview.DataContext = new CustomerViewModel();
Bind to that property:
<TextBox x:Name="CustomText" Text="{Binding CustomText}"/>
Implement INotifyPropertyChanged, if TwoWay binding and notification are required.
Read more into silverlight databinding here.
Use a binding expression:
<TextBox x:Name="CustomText" Text="{Binding TestProperty}" />
Where TestProperty is a public property on your view model which is the current DataContext.
If you wish to update the value in your view model, and have this reflected in the view, then the setter of the TestProperty property should invoke the PropertyChanged event on the INotifyPropertyChanged interface implemented by your view model.