How to set ItemsSource for a databinding from the code-behind - c#

I cannot figure out how to set ItemsSource to my Pivot programatically. I am using MVVM Light ViewModelLocator where my ViewModel is registered. Then I set the DataSource of my Page and in the xaml of the Pivot I set its ItemsSource. But in the ViewModel I have other collection that I want to change at runtime to be an ItemsSource for my Pivot:
Here is my ViewModelLocator:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<TripTypeViewModel>();
}
public TripTypeViewModel TripTypeVM
{
get
{
return ServiceLocator.Current.GetInstance<TripTypeViewModel>();
}
}
public static void Cleanup() {}
}
XAML of the page:
<Page
...
DataContext = "{Binding Source={StaticResource Locator}, Path=TripTypeVM }">
...
<Pivot x:Name="TripsSegmentsPivot" Title="Locator" Foreground="#FF888888" Style="{StaticResource PivotStyle1}" SelectionChanged="Pivot_SelectionChanged" Margin="0" Grid.Row="1" ItemTemplate="{StaticResource TripTypeTemplate1}" ItemsSource="{Binding TripTypeViewModelDataSource}">
<Pivot.HeaderTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding id}"/>
</Grid>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
So in the ViewModel except TripTypeViewModelDataSource i have also TripTypeViewModelDataSource2.
In the xaml.cs of the View I would usually do this:
TripsSegmentsPivot.ItemsSource = ViewModelLocator.TripTypeVM.TripTypeViewModelDataSource;
TripsSegmentsPivot.ItemsSource = ViewModelLocator.TripTypeVM.TripTypeViewModelDataSource2;
but its not working..

There are many ways to bind but let's discuss a few...
Directly to the Pivot object itself: http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemssource(v=vs.105).aspx
MyPivot.Itemsource=mycollection;
You could use a collection view source: http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource(v=vs.110).aspx Visual Stuidio will create these if you drag a container control onto the surface, it also put code in the code behind so you can "wire-it-up" It works very nicely with any collection type and fully integrates with LINQ.
You can set up observable collection in the View Model and Bind to them from the view. The easiest way to do this is to create a Static Instance of the View Model in the View XAML itself because visual studio property page will "See" the view model and allow you to pick the property as an item source. All you need to do is implement INPC or create a Dependency Property (where needed) and you are all set to go. You need this to notify the view when a property changes. https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=using%20inotifypropertychanged
If you need to change collections at run time, the CollectionViewSource is dead-simple to use.. like this...
MyCollectionViewSource.Source = MyCollection.Where(p=>p.Name==SelectedName).ToList();
MyCollectionViewSource.Source = MyCollection.Where(p=>p.ID > 500).ToList();
MyCollectionViewSource.Source = MyOtherCollection.ToList();
MyCollectionViewSource.Source = JustSayNoToObamaCare();
In the current code it shows:
ItemsSource="{Binding TripTypeViewModelDataSource}"
If you use a CollectionViewSource like this:
ItemsSource="{Binding MyCVS}"
You can then change the collection at will in the viewmodel like this.
MyCVS.Source = Collection1;
MyCVS.Source = Collection2;

Related

Trouble binding ViewModel to View, as part of a ListViewItem DataTemplate with ReactiveUI

I'm having some issues trying to display a view in a ListViewItem Data template in reactive. I have a ListView in, for example ReceiptView.xaml whose source is bound like such
this.WhenActivated(d =>
{
this.OneWayBind(ViewModel, vm => vm.Items, view =>
view.ReceiptListView.ItemsSource).DisposeWith(d);
});
where in ReceiptViewModel - Items is an IObservableCollection<ItemsViewModel>
The ItemTemplate for the ListView is as such
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type viewModel:ItemViewModel}">
<DockPanel
Width="{Binding Path=ViewportWidth, RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}}}"
Margin="0,5"
HorizontalAlignment="Left"
Background="Transparent">
<views:ItemView />
</DockPanel>
</DataTemplate>
It is here where I'm unsure whether to use this mechanism for displaying the view or the ViewModelViewHost ViewModel="{Binding ItemViewModel}"..
My ItemView.xaml.cs is as such
public partial class ItemView : ReactiveUserControl<ItemViewModel>, ISupportsActivation
{
public ItemView()
{
Activator = new ViewModelActivator();
InitializeComponent();
this.WhenActivated(d =>
{
this.Bind(ViewModel, vm => vm.Name, v => v._Name.Text).DisposeWith(d);
});
}
}
Currently with the usage above, it never goes into the WhenActivated function, if I remove the ISupportsActivation functionality, it just crashes out with the message ViewModel is null. I was wondering what the correct way is to get something like this working?
I have tried using ViewModelViewHost and setting the ViewModel property inside the DataTemplate instead of above but it doesn't even reach the ItemView.xaml.cs backing code. The dependencies are setup initially like:
dependencyResolver.Register(() => new ItemView(), typeof(IViewFor<ItemViewModel>));
The solution, in respect of actually getting it to work, was a matter of binding issue. I needed to use
<ViewModelViewHost ViewModel="{Binding .}" />
within the DataTemplate.

Wpf call function of ContentControl view

I am pretty new to C# so I am just starting to learn the basics. Right now I have a ContentControl inside a Window like this:
<ContentControl Content="{Binding}" x:Name="SubView"/>
And I configured my resources of the Windows like this:
<Window.Resources>
<DataTemplate x:Name="StammdatenViewTemplate" DataType="{x:Type viewmodels:StammdatenViewModel}">
<views:StammdatenView DataContext="{Binding}" />
</DataTemplate>
<DataTemplate x:Name="AdministrationViewTemplate" DataType="{x:Type viewmodels:AdministrationViewModel}">
<views:AdministrationView DataContext="{Binding}" />
</DataTemplate>
</Window.Resources>
In my Window class I am setting the DataContext like this:
DataContext = new StammdatenViewModel();
Here is the thing I would like to do. I want to disable all the TextBoxes inside the ContentControl. I thought about adding a function to my StammdatenView.xaml.cs class (which is the class of my subview), then firing the event from the Window somehow. Though I would need access to the function inside the subview. Is that somehow possible and if yes how? Or would anyone suggest a different approach?
Thanks in advance.
Or would anyone suggest a different approach?
Yes. You should bind the IsEnabled property of each TextBox in the StammdatenView to a boolean property of the StammdatenViewModel.
You can then disable the TextBoxes by setting the source property in the view model class. This is one of the key aspects of the MVVM design pattern, i.e. that you handle your application logic in the view model.
Make sure that the view model class implements the INotifyPropertyChanged interface and provide change notifications as explained on MSDN.
You can create a INotifyPropertyChanged event on your view model, then bind it to the 'IsEnabled={Binding IsTextBoxEnabled}' attribute in your view template for the textbox.
public class ViewModel : BaseViewModel
{
private bool _isTextBoxEnabled;
public bool IsTextBoxEnabled
{
get { return _isTextBoxEnabled; }
set
{
if (value != _isTextBoxEnabled)
_isTextBoxEnabled = value;
this.RaisePropertyChanged("IsTextBoxEnabled");
}
}
}
XAML
<DataTemplate x:Key="template">
<StackPanel Orientation="Horizontal" DataContext="{Binding}">
<TextBox IsEnabled="{Binding IsTextBoxEnabled}" />
</StackPanel>
</DataTemplate>

Navigation with Frame and Combobox MVVM

I'm trying to get my head around MVVM, i'm currently stuck on how to handle navigation.
Currently I have a page and within that page is a frame, that frame is responsible for framing in various other pages. Navigation was previously handled with a drop down box and on selection changed it would navigate that way.
I'm not sure how I could do this without touching the frame from the model view which would end up breaking mvvm.
In the end what I am trying to accomplish is, clicking on the combobox, selecting an item and then having then frame below navigate to the correct view.
I'm not using Prism or any other framework with MVVM, just trying to do it all manually.
The ComboBox would display an ObservableCollection of frame items exposed by your main viewmodel, and the viewmodel will have another property for the selected item.
Your main viewmodel and the frame item viewmodels all inherit from a ViewModelBase class which implements INotifyPropertyChanged, and maybe some other stuff.
So, C#:
public ObservableCollection<ViewModelBase> FrameItems { get; protected set; }
private ViewModelBase _selectedFrameItem;
public ViewModelBase SelectedFrameItem {
get { return _selectedFrameItem; }
set {
value = _selectedFrameItem;
// Defined in ViewModelBase
OnPropertyChanged();
}
}
Your main viewmodel will populate FrameItems in its constructor:
public MainViewModel()
{
FrameItems = new ObservableCollection<ViewModelbase> {
new IceCreamMenu(),
new SmurfOptions(),
new MagicSparklePonyFourierTransformConfiguration()
};
}
Every frame item is a subclass of ViewModelBase. It exposes properties with notifications, including ObservableCollections of any set of child things it may have. And we'll display it by writing a datatemplate for it in just a bit.
Let's assume that you've given your ViewModelBase class a String Title { get; set; } property. Or maybe you'll want to write a subclass of ViewModelBase that introduces Title; your call. For now let's put it in ViewModelBase for simplicity.
XAML -- this leaves out all the layout, but you don't need that here.
<ComboBox
ItemsSource="{Binding FrameItems}"
SelectedItem="{Binding SelectedFrameItem}"
DisplayMemberPath="Title"
/>
<Frame Content={Binding SelectedFrameItem}" />
OK, but how on earth does it know what to do with SelectedFrameItem?!
Easy! Write a resource dictionary called, say, ViewModelDataTemplates.xaml, and merge it into App.xaml so its contents are "visible" in any XAML in your application.
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Source is a relative path from project root directory -->
<ResourceDictionary Source="ViewModelDataTemplates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
...plus whatever theme stuff or whatever.
In ViewModelDataTemplates.xaml, define data templates for your frame item classes.
Say you've got an IceCreamMenu viewmodel, with a collection of Flavors
public ObservableCollection<IceCreamFlavor> Flavors { get; protected set; }
...and a SelectedFlavor. You'd define the namespace vm appropriately with an xmlns:vm attribute on the resource dictionary.
ViewModelDataTemplates.xaml
<DataTemplate DataType="{x:Type vm:IceCreamMenu}">
<Grid>
<ListBox
ItemsSource="{Binding Flavors}"
SelectedItem="{Binding SelectedFlavor}"
/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:IceCreamFlavor}">
<StackPanel Orientation="Horizontal">
<Border
Height="20"
Width="20"
Margin="4"
Background={Binding Color, Converter={StaticResource ColorToBrushConverter}}"
/>
<Label Content="Name" />
</StackPanel>
</DataTemplate>
If you've got existing UserControls that you want to use via datatemplates, that's easy: Say you've got a NotesTabView UserControl that's a view for your NotesTabViewModel, you could define a DataTemplate like this:
<DataTemplate DataType="{x:Type vm:NotesTabViewModel}">
<vw:NotesTabView />
</DataTemplate>
#EdPlunkett: As an alternative to DataTemplate for each view, you can bind your frame to selected page viewmodel using ViewModelToViewConverter like I did here: https://stackoverflow.com/a/31721236/475727
Implicit DataTemplates and DataTemplateSelectors are unique to WPF and XAML, so people think it's recommended solution, but I think it's not suitable for navigation. It feels hackish and it smells with violation of DRY principle.

c# mvvm bind views to tabcontrol with header

I have a wpf programme with a main View (Window)which contains a TabControl to show several different UserControl Views (the sub-views, one in each tab). Every View has an associated ViewModel.
I wish to bind the TabControl so that I just need to load a new sub-view into the ApplicationViewModel and it will appear on the TabControl.
I have successfully bound the sub-views to the content, but cannot seem to get anything in the header. I wish to bind the header to a property in the sub-view's ViewModel, specifically TabTitle.
Application View (DataTemplate binding not working):
<Window ...>
<DockPanel>
<TabControl ItemsSource="{Binding PageViews}" SelectedIndex="0"> <!--Working-->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.TabTitle}, Path=DataContext.TabTitle}" /> <!--Not Working-->
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DockPanel>
</Window>
Application ViewModel (ObservableObject basically implements INotifyPropertyChanged`):
class ApplicationViewModel : ObservableObject
{
private DataManager Data;
private ObservableCollection<UserControl> _pageViews;
internal ApplicationViewModel()
{
Data = new DataManager();
PageViews.Add(new Views.MembersView(new MembersViewModel(Data.DataSet)));
}
public ObservableCollection<UserControl> PageViews
{
get
{
if (_pageViews == null)
{
_pageViews = new ObservableCollection<UserControl>();
}
return _pageViews;
}
}
The MembersView Code behind:
public partial class MembersView : UserControl
{
public MembersView(MembersViewModel ViewModel)
{
InitializeComponent();
DataContext = ViewModel;
}
}
MembersViewModel (truncated):
public class MembersViewModel : INotifyPropertyChanged
{
public TabTitle { get; protected set; }
public MembersViewModel(DataSet BBDataSet)
{
TabTitle = "Members";
}
//All view properties
}
I'm sure that it is something simple...
You are binding the TabControl to a collection of type UserControl. That means the data context for each item will be of type UserControl. There is no property named "TabTitle" in UserControl, so the binding will not work.
I think what you are trying to do can be accomplished with the following changes:
Have ApplicationViewModel expose a collection of type MembersViewModel, instead of UserControl, and populate it appropriately.
Setup a ContentTemplate to create views for your items in the TabControl:
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type namespace:MembersViewModel}">
<namespace:MembersView />
</DataTemplate>
</TabControl.ContentTemplate>
(Replace "namespace:" with your xaml imported namespace containing your controls.)
Update the ItemTemplate in your TabControl so it binds properly to the view model:
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabTitle}}" />
</DataTemplate>
</TabControl.ItemTemplate>
Update MembersView to have a parameterless constructor. The DataContext on the view will be set for you by the TabControl. If you need to access the view model from your code-behind, it should be available through the DataContext property after the InitializeComponent() call.
Anytime you are working with ItemsControl (and its extensions such as ListBox, TreeView, TabControl, etc.), you should never be instantiating your own item views. You always want to setup a template that instantiates the view based on the data (or view model) and bind directly to the data (or view model) in the ItemsSource property. This allows all of the item's data contexts to be setup for you so you can bind to them.
Edit: Since you have multiple view / viewmodel pairings, you will want to define your templates slightly differently:
<TabControl ItemsSource="{Binding PageViews}" SelectedIndex="0">
<TabControl.Resources>
<DataTemplate DataType="{x:Type namespace:MembersViewModel}">
<namespace:MembersView />
</DataTemplate>
<DataTemplate DataType="{x:Type namespace:ClassesViewModel}">
<namespace:ClassesView />
</DataTemplate>
<DataTemplate DataType="{x:Type namespace:SessionsViewModel}">
<namespace:SessionsView />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabTitle}}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
The difference is that you want to define multiple data templates, one for each type, in your resources. That means it will use those templates each time it encounters those types. You still want to set ItemTemplate to force the tab headers to use a specific template. However, do not set ContentTemplate, allowing the content to use the data templates defined in resources.
I hope that makes sense.
P.S. You can also define these data templates in a higher level resource dictionary, such as in your main window or your application, if you want them to apply to content presenters every place you use those view models, rather than only in this one TabControl.

Looking for guidance for where to put some code in a WPF MVVM application

First time attempting MVVM, looking for clarity on where to put some code.
My main view will need to bind to a list that will be holding 1 to many UserControls.
Would the List exist in the ViewModel or the Model? From what I'm reading, the model contains properties typically that the View binds to via the ViewModel. I don't see how that would work for this, the Model would not need to know about the List of UserControls which is a list of a View(UserControl), I may be making this harder than needed, but I'm still wrapping my mind around where code should be placed and I want to understand it. Thanks for any guidance, or if I did not explain myself well please let me know.
Your UserControls should have a ViewModel (Let's call it ItemViewModel by now).
Your MainViewModel should have an ObservableCollection<ItemViewModel>.
Then your view should have an ItemsControl (or one of its derivatives such as ListBox) for which the ItemsSource property will be bound to the ObservableCollection.
And then the ItemsControl should define an ItemTemplate that contains your UserControl.
This is the right way to do what you're describing with WPF / MVVM.
Example (pseudocode):
MainViewModel:
public class MainViewModel
{
public ObservableCollection<ItemViewModel> Items {get;set;}
}
MainView:
<Window>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<my:UserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Keep in mind that each instance of UserControl will have it's DataContext set to the corresponding item in the source collection.
Therefore, if your ItemsViewModel looks something like this:
public class ItemsViewModel
{
public string LastName {get;set;}
public string FirstName {get;set;}
//INotifyPropertyChanged, etc.
}
your UserControl can be defined like this:
<UserControl>
<StackPanel>
<TextBox Text="{Binding LastName}"/>
<TextBox Text="{Binding FirstName}"/>
</StackPanel>
</UserControl>
You shouldn't need a list of UserControls. What you would likely have is your View binding to a List of items in your ViewModel. For example, create a ListBox and set it's ItemsSource to your ViewModel's list.
To create a your user control for each item, you would need to create a DataTemplate for the type in your list and specify your UserControl and you can give any bindings inside that usercontrol to the item.
The ListBox will then use the DataTemplate to create a UserControl for each item in the list.

Categories

Resources