MVVM Indexed Binding - wpf

I am using WPF MVVM architecture and my ViewModel has an ObservableCollection of object. My Main ViewModel will look like below and it is binding to a list in main page, and it has a number of UserControls. These UserControls are binding based on user selection from this List. How can I bind indexed data to UserControl relative to my Main ViewModel?
main ViewModel :-
public ApplicationViewModel() {
}
private string selectedID;
public string SelectedID
{
get { return selectedID; }
set { selectedID = value;NotifyPropertyChange("SelectedID"); }
}
private ObservableCollection<RATA.Model.ApplicationModel> appViewModel;
public ObservableCollection<RATA.Model.ApplicationModel> AppViewModel
{
get { return appViewModel; }
set { appViewModel = value;NotifyPropertyChange("AppViewModel"); }
}
}

Related

WPF prevent fire combobox source selection in MVVM during his population

I need help. I am kind of new in MVVM
I've got event datagrid selecteditem event in MVVM. After creating a source for grid items(void search) I need populate combobox source as a filter values based on main collection. I used(before MVVM) to work with event subscribing, but can not achieve it here. If I put OnPropertChanged("FamilyFilter") at the end of main function combobox selectionchanged event start working immediately. Here is the code:
public IList<Object> somelist { get; set; }
public CollectionView Items { get; set; }
private string _selectedFamily;
public string SelectedFamily
{ get { return _selectedFamily; }
set { Items = <<filteredcollection>>; OnPropertyChanged("SelectedFamily"); }
}
private List<string> _familyEntries;
public List<string> FamilyEntries
{ get { return _familyEntries; }
set { _familyEntries = value; OnPropertyChanged("FamilyEntries");}
}
public void search()
{
Items = (CollectionView)CollectionViewSource.GetDefaultView(somelist);
_familyEntries = somelist.GroupBy(x => x.Family).Select(z => z.First().Family).ToList();
OnPropertyChanged("Items");
OnPropertyChanged("FamilyEntries");
}
<ComboBox ItemsSource="{Binding FamilyEntries}" SelectedValue="{Binding SelectedFamily}"/>

WPF binding a combobox to an array/list of business objects

I'm building a WPF application and my UI consists of combobox and about a dozen other UI controls. I have a single business object class that contains a dozen or so properties and implements INotifyPropertyChanged.
Here's a snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
private int _idNumber;
public int IdNumber
{
get { return _idNumber; }
set
{
if (_idNumber == value)
{
return;
}
_idNumber = value;
OnPropertyChanged(new PropertyChangedEventArgs("IdNumber"));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
private int _age;
public int Age
{
get { return _age; }
set
{
if (_age == value)
{
return;
}
_age = value;
OnPropertyChanged(new PropertyChangedEventArgs("Age"));
}
}
private string _address;
public string Address
{
get { return _address; }
set
{
if (_address == value)
{
return;
}
_address = value;
OnPropertyChanged(new PropertyChangedEventArgs("Address"));
}
}
private bool _employed;
public bool Employed
{
get { return _employed; }
set
{
if (_employed == value)
{
return;
}
_employed = value;
OnPropertyChanged(new PropertyChangedEventArgs("Employed"));
}
}
public MyBusinessObject(int idNumber)
{
this.IdNumber = idNumber;
// set default values here
}
}
As you might expect, the various UI controls will be bound to the properties of my business object. However, I need to create an array or list of business objects (10 of them to be specific) and bind my combobox to the IdNumber property. So my user will select the object that they want from the combobox and then the other UI controls should update to display the values for each of their bound properties for the selected object.
Right now, I just have one instance of my business object declared in my code behind like this:
public partial class MainWindow : Window
{
// this will be replaced with an array/list of business objects
MyBusinessObject myObject = new MyBusinessObject(1234); // 1234 = IdNumber
public MainWindow()
{
InitializeComponent();
this.DataContext = myObject;
}
}
And currently, my combobox is defined like this:
<ComboBox x:Name="selectedObjectComboBox" IsEditable="False"/>
Once I implement my array/list of business objects, can anyone tell me how I would bind the combobox to the array so that it will display the IdNumber for each object? Also, what, if anything, will I need to do to get the other bound controls to reflect the values of the selected object when the user changes their selection in the combobox?
You need to bind your ComboBox to your list, then use the DisplayMemberPath to specify which member you want displayed:
<ComboBox ItemsSource={Binding yourList} DisplayMemberPath="IdNumber"/>
If you want your other controls to update off of this value, you might want to consider making a SelectedItem property on your view model and bind the selected item to this. Then your other controls can bind to this.
EDIT
That can be achieved by doing something like:
<ComboBox ItemsSource={Binding yourList} SelectedItem={Binding SelectedBusinessObject} DisplayMemberPath="IdNumber"/>
If you don't want to make a backing field, you could do something like..
<ComboBox ItemsSource={Binding yourList} x:Name="BusinessComboBox" DisplayMemberPath="IdNumber"/>
<MyControl Item={Binding SelectedItem, ElementName=BusinessComboBox />

How keep alive user control?

I have wizard project that works with ContentControl which contains user controls.
I do the instantiation through the XAML file at my main window:
<DataTemplate DataType="{x:Type ViewModel:OpeningViewModel}">
<view:OpeningView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:SecondUCViewModel}">
<view:SecondUCView/>
</DataTemplate>
But when I navigate between the UC's it seems that the UC's aren't works like "keep alive", Every UC switching creates new instance. How can I avoid it?
I want create for every UC just one instance and navigate between those instances only without creating new instances.
I know how write singleton but my project based on MVVM and I'm quite new at WPF so I'm not sure what is the best way to do this.
Thanks
Update:
Here the code of the viewModel:
In the viewModel I have :
private ObservableCollection _pages = null;
private NavigationBaseViewModel _currentPage;
#endregion
#region Properties
public int CurrentPageIndex
{
get
{
if (this.CurrentPage == null)
{
return 0;
}
return _pages.IndexOf(this.CurrentPage);
}
}
public NavigationBaseViewModel CurrentPage
{
get { return _currentPage; }
private set
{
if (value == _currentPage)
return;
_currentPage = value;
OnPropertyChanged("CurrentPage");
}
}
private ICommand _NavigateNextCommand;
public ICommand NavigateNextCommand
{
get
{
if (_NavigateNextCommand == null)
{
_NavigateNextCommand = new RelayCommand(param => this.MoveToNextPage(), param => CanMoveToNextPage);
}
return _NavigateNextCommand;
}
}
private ICommand _NavigateBackCommand;
public ICommand NavigateBackCommand
{
get
{
if (_NavigateBackCommand == null)
{
_NavigateBackCommand = new RelayCommand(param => this.MoveToPreviousPage(), param => CanMoveToPreviousPage);
}
return _NavigateBackCommand;
}
}
private bool CanMoveToNextPage
{
get
{
return this.CurrentPage != null && this.CurrentPage.CanMoveNext;
}
}
bool CanMoveToPreviousPage
{
get { return 0 < this.CurrentPageIndex && CurrentPage.CanMoveBack; }
}
private void MoveToNextPage()
{
if (this.CanMoveToNextPage)
{
if (CurrentPageIndex >= _pages.Count - 1)
Cancel();
if (this.CurrentPageIndex < _pages.Count - 1)
{
this.CurrentPage = _pages[this.CurrentPageIndex + 1];
}
}
}
void MoveToPreviousPage()
{
if (this.CanMoveToPreviousPage)
{
this.CurrentPage = _pages[this.CurrentPageIndex - 1];
}
}
And the ContentControl which contains the UC`s binded to CurrentPage
You can do that by hardcoding the UserControls in XAML, instead of using DataTemplates. DataTemplates will create new Controls every time they are instantiated. However, since you use MVVM, you could also move all data you want persisted between the changes to the ViewModels, and make sure that the ViewModel objects are always the same. Then, the DataTemplates would still create new controls, but they would contain the same information as before.
I have recently come up against the same issue with my views in MVVM. Basically, I wanted to cache views that took a while to render. If you are familiar with the ViewModelLocator, this approach should be straight forward.
In the client (e.g. WPF) project I created a ViewLocator class that looked like this:
public class ViewLocator : ObservableObject
{
#region Properties
private View _myView;
public View MyView
{
get { return _myView; }
set { Set(() => MyView, ref _myView, value); }
}
#endregion Properties
#region Constructors
public ViewLocator()
{
RegisterViews();
}
#endregion Constructors
#region Private Methods
private void RegisterViews()
{
MyView = new View();
}
#endregion Private Methods
}
And to use this in a data template I specified the ViewLocator as an static application resource so only one instance is ever instantiated - in my case I put it in in App.xaml. To use the ViewLocator and it's "View" properties, I did the following:
<vl:ViewLocator x:Key="ViewLocator" />
<DataTemplate DataType="{x:Type vm:ViewModel}">
<ContentControl Content="{Binding Source={StaticResource ViewLocator}, Path=MyView}" />
</DataTemplate>
By doing it this way, each view is only instantiated once and can be reused.

WPF MVVM Bind list on custom control to ViewModel

Is it possible to bind data in the "wrong" direction? I want a value in a custom control to be bound to my ViewModel. I've tried binding with mode "OneWayToSource" but I can't get it to work.
Scenario (simplified):
I have a custom control (MyCustomControl) that has a dependency property that is a list of strings:
public class MyCustomControl : Control
{
static MyCustomControl()
{
//Make sure the template in Themes/Generic.xaml is used.
DefaultStyleKeyProperty.OverrideMetadata(typeof (MyCustomControl), new FrameworkPropertyMetadata(typeof (MyCustomControl)));
//Create/Register the dependency properties.
CheckedItemsProperty = DependencyProperty.Register("MyStringList", typeof (List<string>), typeof (MyCustomControl), new FrameworkPropertyMetadata(new List<string>()));
}
public List<string> MyStringList
{
get
{
return (List<string>)GetValue(MyCustomControl.MyStringListProperty);
}
set
{
var oldValue = (List<string>)GetValue(MyCustomControl.MyStringListProperty);
var newValue = value;
SetValue(MyCustomControl.MyStringListProperty, newValue);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(MyCustomControl.MyStringListProperty, oldValue, newValue));
}
}
public static readonly DependencyProperty MyStringListProperty;
}
The control also contains code to manipulate this list.
I use this custom control in a UserControl that has a ViewModel. The ViewModel has a property that is also a list of strings:
public List<string> MyStringsInTheViewModel
{
get
{
return _myStringsInTheViewModel;
}
set
{
if (value != _myStringsInTheViewModel)
{
_myStringsInTheViewModel = value;
OnPropertyChanged("MyStringsInTheViewModel");
}
}
}
private List<string> _myStringsInTheViewModel;
Now I want to bind the list in my custom control (MyStringList) to the list in my ViewModel (MyStringsInTheViewModel) so that when the list is changed in the custom control it is also changed in the ViewModel. I've tried this but can't get it to work...
<myns:MyCustomControl MyStringList="{Binding Path=MyStringsInTheViewModel, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}">
How can I make such a binding?
Use ObservableCollection<T> instead of List<T>. It implements INotifyCollectionChanged Interface.

How to save data from a DetailView bound to a ViewModel if the repository is a no-go in a viewmodel?

we mvvm lovers all know Josh Smith mvvm sample and how he has saved the customer in the detail customer view by injecting the repository object into the customerViewModel`s constructor.
But a viewmodel should not know about repositories. Its just a model of a view nothing must being aware of persistence etc...
How can I register my Action delegate SaveDocumentDelegate on the DocumentViewModel if its set in the code-behind? Actually I should subscribe the delegate in my DocumentController but how can I instantiate the DocumentView in my DocumentController and set it as Datacontext not doing that in code-behind. Only thing that came to my mind is using a contentcontrol in the window and bind it to the type of the viewModel and datatemplate it with the Document UserControl like this:
<UserControl.Resources>
<DataTemplate DataType="{x:Type ViewModel:DocumentViewModel}">
<View:DocumentDetailView/>
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{Binding MyDocumentViewModel}" />
But I do not want to use a control to solve my architectural problems...
xaml:(view first approach)
public partial class DocumentDetailView : UserControl
{
public DocumentDetailView()
{
InitializeComponent();
this.DataContext = new DocumentViewModel(new Document());
}
}
DocumentViewModel:
public class DocumentViewModel : ViewModelBase
{
private Document _document;
private RelayCommand _saveDocumentCommand;
private Action<Document> SaveDocumentDelegate;
public DocumentViewModel(Document document)
{
_document = document;
}
public RelayCommand SaveDocumentCommand
{
get { return _saveDocumentCommand ?? (_saveDocumentCommand = new RelayCommand(() => SaveDocument())); }
}
private void SaveDocument()
{
SaveDocumentDelegate(_document);
}
public int Id
{
get { return _document.Id; }
set
{
if (_document.Id == value)
return;
_document.Id = value;
this.RaisePropertyChanged("Id");
}
}
public string Name
{
get { return _document.Name; }
set
{
if (_document.Name == value)
return;
_document.Name = value;
this.RaisePropertyChanged("Name");
}
}
public string Tags
{
get { return _document.Tags; }
set
{
if (_document.Tags == value)
return;
_document.Tags = value;
this.RaisePropertyChanged("Tags");
}
}
}
UPDATE:
public class DocumentController
{
public DocumentController()
{
var win2 = new Window2();
var doc = new DocumentViewModel(new DocumentPage());
doc.AddDocumentDelegate += new Action<Document>(OnAddDocument);
win2.DataContext = doc;
wind2.ShowDialog();
}
private void OnAddDocument(Document doc)
{
_repository.AddDocument(doc);
}
}
What do you think about that idea?
But a viewmodel should not know about
repositories. Its just a model of a
view nothing must being aware of
persistence etc...
The viewmodel connects the model and view together; it is exactly what controls persistence, though it does not handle persistence.
We decouple this from other concern by using services.
One way to avoid adding persistence concerns to the viewmodel is by abstracting those concerns into repository interfaces, so that we can inject it as a dependency. In this way we can delegate persistence work in the viewmodel, usually in response to the user's interaction with the view.

Resources