A simpel WPS using MVVM and EF - wpf

I'm on the edge of going nuts.
I have a working WinForms/database application, that I try to rebuild with WPF. My main problem is that I haven't worked with MVVM before and I can't seem to get the grip of the databinding from Entity Framework to the View.
My WinForms is build exclusively using code-behind, which I know is bad praxis, but it works.
I have read about 100 articles, tutorials and examples, downloaded a couples of demo/samples using MVVM. Including "WPF Application Framework (WAF)"
But I haven't found a simple solution/sample how to use EF as the Model or as a dataprovider for the Model. And to pass the information on to to the ViewModel, and lastly bind it from the View.
All the tutorials I have read only describe fetching data from a static list, I need the usual CRUD operations on the database.
I know questions like this are being asked all the time here, but I haven't being able to find an answer to pushing data from EF to the View(Model) and update back through EF. I hope that some of you can help a (must be retarded) person like me with some guiding.

The MVVM pattern hinges, a lot, on the INotifyPropertyChanged interface which EF implements so some of the work here is done for you.
I am not too sure on your particular requirement but start off by creating a view mode for your window. Lets assume that you have a entity called Person with an attribute of FirstName as a string and an Id of Int32 (of course), and that all the work for the data layer is taken care of. I will show you how to list the Person entity and allow edit of the person entity as well.
ViewModel
public class Window1ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Person> _people;
private Person _selectedPerson;
public ObservableCollection<Person> People {
get { return _people; }
set { _people = value;
this.RaisePropertyChanged("People");
}
}
public Person SelectedPerson {
get { return _selectedPerson; }
set { _selectedPerson = value;
this.RaisePropertyChanged("SelectedPerson");
}
public Window1ViewModel() {
// Instead of setting to empty collection populate with data from EF.
this.People = new ObservableCollection<Person>();
}
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Now that you have your ViewModel taken care of it is time to create the UI and bind it.
Binding ListBox
<ListBox ItemsSource="{Binding Source={StaticResource viewModel}, Path=People}" SelectedItem="{Binding Source={StaticResource viewModel}, Path=SelectedPerson, Mode=TwoWay}"/>
Binding TextBox
<TextBox Text="{Binding Source={StaticResource viewModel}, Path=SelectedPerson.FirstName, Mode=TwoWay}"/>
Lastly you could create a button on your Window, bind it to an ICommand on your ViewModel to call the Save() method in the EF layer.
There is a lot you could write to meet your requirement i hope this goes some way to assist. Also recommend the following information. http://www.codeproject.com/Articles/81484/A-Practical-Quick-start-Tutorial-on-MVVM-in-WPF

Related

Why does one of MY WPF DataGrids give the "'EditItem' is not allowed for this view" exception?

I have read all the Q&A I could find here and on the MS forums about this exception, and tried most of the suggestions that I understood, and a few others. It seems that this exception can come up for a wide range of causes.
As with others, I have a WPF DataGrid bound to a collection, which throws this exception when one tries to edit one of the cells. They are set to be write-able, the collection is an ObservableCollection, I've implemented get and set handlers which send notification messages.
The suggestions I haven't tried are the ones involving implementing IList's non-generic interface, because I have no idea what I would do to do that. Also, I have many DataGrids bound to various lists and collections in my app which work, and this one used to work when it was bound to a LINQ collection.
Please help me figure out what I need to do here.
The Data Grid is:
<DataGrid Name="dgIngredients" Margin="567,32,0,44" Width="360" ItemsSource="{Binding}" IsReadOnly="False"
AutoGenerateColumns="False" HorizontalAlignment="Left" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Columns>
<DataGridTextColumn Width="63" Header="Percent" Binding="{Binding Preference}" IsReadOnly="False" />
<DataGridTextColumn SortDirection="Descending" Width="301" Header="Ingredient" Binding="{Binding Ingredient}" IsReadOnly="True" CanUserSort="True" CanUserReorder="False" />
</DataGrid.Columns>
</DataGrid>
The column being edited is the non-read-only one, Preference.
The collection is:
private ObservableCollection<RAM_Ingredient> MemberIngredientPrefs = new ObservableCollection<RAM_Ingredient>();
The binding is:
dgIngredients.DataContext = MemberIngredientPrefs.OrderBy("Ingredient",true);
RAM_Ingredient is:
public class RAM_Ingredient : INotifyPropertyChanged
etc.
Where RAM_Ingredient.Preference is:
private int _Preference;
public int Preference
{
get
{
return _Preference;
}
set
{
// This is needed to send notification of changes (and to not throw an exception on grid edit!):
if ((_Preference != value))
{
SendPropertyChanging();
_Preference = value;
SendPropertyChanged("Preference");
}
}
}
The exception is:
System.InvalidOperationException was unhandled
Message='EditItem' is not allowed for this view.
Source=PresentationFramework
StackTrace:
at System.Windows.Controls.ItemCollection.System.ComponentModel.IEditableCollectionView.EditItem(Object item)
at System.Windows.Controls.DataGrid.EditRowItem(Object rowItem)
at System.Windows.Controls.DataGrid.OnExecutedBeginEdit(ExecutedRoutedEventArgs e)
etc...
I have also this problem, And found that the point here is that we can not edit a IEnumerable in a DataGrid, only a list can be edited.
therefore we didn't need to create a new class, its works also on a LINQ query with anonymous return type. it's need only to be a list.
here is a sample of my code:
dtgPrdcts.ItemsSource= ProductLists.Select(Function(x) New With {.ListTitle = x.ListTitle, .ProductID = x.ProductID, .License = "", .ForRemove = True}).ToList
I still don't know what specifically caused the problem, but I managed to work around it, and I'm not sure how much of what I did was overkill, but it works.
I created a new class just for the purpose of holding the data in the DataGrid rows. I make a List of objects of this class and fill it in and bind it to the DataGrid as I was doing before. I also added the usual stuff and nonsense for getting Change Notification to work (probably overkill) and I had to re-define a comparison function in a different way to get it to sort because of that whole comedy situation.
i.e.
List<UsablePref> MemberIngredientPrefs = new List<UsablePref>();
...
foreach (RAM_Ingredient ingredient in App.Ingredients)
{
ingredient.GetPreferences(EditorMember);
UsablePref pref = new UsablePref();
pref.Ingredient = ingredient.Ingredient;
pref.IngredientID = ingredient.IngredientID;
pref.Preference = ingredient.Preference;
MemberIngredientPrefs.Add(pref);
}
// Sort alphabetically by ingredient name,
MemberIngredientPrefs.Sort(UsablePref.CompareByName);
// and bind the ingredient prefs DataGrid to its corresponding List
dgIngredients.DataContext = MemberIngredientPrefs;
I had this same problem trying to create a list of rows from a join; since the LINQ query returns an IEnumerable, I had the DataGrid bound to that IEnumerable; this worked fine for readonly and oddly worked with ComboBoxes and some other custom controls I used, but plain text editing threw the InvalidOperationException. The solution was an ObservableCollection in place of the IEnumerable; basically from:
BoundView = (/*LINQ QUERY*/); // is IEnumerable<CustomJoinObject>
to
BoundView = new ObservableCollection<CustomJoinObject>(/*LINQ QUERY*/);
In both cases BoundView is the DataContext for the DataGrid.
I'm assuming this happens because IEnumerable doesn't have the machinery to support a datagrid, whereas ObservableCollection does.
The model class needs to implement the interface INotifyPropertyChanged coming from the namespace System.ComponentModel.
Class example:
public class Exemple : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion INotifyPropertyChanged Members
}
even if this thread is old, it could help someone.
The issue is that LINQ returns a IEnumerable, as said VaelynPhi, but the cause is that editing a datagrid requires a source that implements IEditableCollectionView.
You can then use a list, an observableCollection or any collection implementing this interface.
I found this solution thanks to Shoe who gave an answer on this tread.
In my case this exception was thrown when I wanted to edit some cells. The problem was of wrong collection type bound to ItemSource => when I switched from IEnumerable<T> to ObservableCollection<T> everything works correctly.

Binding a ContentControl to a deep path in WPF

The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:
<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>
Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.
This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.
If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.
CLARIFICATION
This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)
So, with that said, the correct question to be answering is the following:
Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?
Although I would expect this to work, there are several problems with your approach.
Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.
Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.
I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.
I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:
<ContentControl x:Name="ActiveItem" />
You can use the ActivateItem() method to change the current active item.
Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.
Every VM is a DependencyObject. Every property is a
DependencyProperty.
why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.
and if you want your different viewmodel be rendered in a different way - you should use DataTemplate.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelA}>
<MyViewA/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyViewModelB}>
<MyViewB/>
</DataTemplate>
</Windows.Resources>
<Grid>
<ContentControl Content="{Binding MyActualVM}"/>
</Grid>
</Window>
EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View
EDIT2: another approach could be to get the BindingExpression of your content control and call.
System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();
EDIT3: and a simple mvvm way - just use INotifyPropertyChanged
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MyFooVM = new FooVM();
this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};
this.DataContext = this;
}
public FooVM MyFooVM { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
}
}
public class INPC : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class FooVM:INPC
{
private BarVM _myBarVm;
public BarVM MyBarVM
{
get { return _myBarVm; }
set { _myBarVm = value;OnPropChanged("MyBarVM"); }
}
}
public class BarVM : INPC
{
private string _view;
public string View
{
get { return _view; }
set { _view = value;OnPropChanged("View"); }
}
}

How to update the UI using bindings

I have a DataGrid and two ListBoxes in my window. I am using Entity Framework to Connect to my SQL Server. Depending on the selections I make in the ListBoxes parameters will be passed to my stored procedure and data for my DataGrid will be retrieved. I was able to implement this functionality without using MVVM. I would like to know ways to implement this using MVVM. Please Help me out. Thanks in Advance.
First of all, MVVM is about separating the concerns of your code into the appropriate area. For example, talking to your database via EF should be done in the Model1. The ViewModel is responsible for holding the data, and for shaping or massaging it to make it more suitable for display (i.e. transforming enums to colors2, etc).
To implement your functionality in a MVVM way, you will need to use binding, and bind your viewmodel to your view:
<MyControl>
<LayoutRoot>
<ListBox ItemsSource={Binding MyItems} SelectedItem={Binding MySelection} />
</LayoutRoot>
</MyControl>
in the code behind for the View:
public class MyControl
{
public MyControl()
{
this.DataContext = new MyViewModel();
}
}
and your ViewModel will look something like this:
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<MyDataObject> MyItems
{
get { return _myItems; }
set
{
_myItems = value;
OnPropertyChanged("MyItems");
}
}
public MyDataObject MySelection { get; set; }
public void DoSomethingWithDatabase()
{
Model.DoSomething(MySelection);
}
}
This is just a VERY simple example to illustrate what is required if you do things the MVVM way (and I've deliberately missed out a bunch of stuff). To do a proper example and document all the essential bits you need to know would take at least a chapter in a book, so I'll refer you to a MSDN article for further reading: Implementing the Model-View-ViewModel Pattern.
1 And the Model may just be a stepping stone if you also implement SOA, the Model might just call a service which then talks to the database.
2 This can also be done with Converters in the View, but may not always be possible or practical in a converter.

WPF Dependency Property workaround

There are 3 UserControls under a MainWindow. Each control have it's own Save Button. The Mainwindow has a SaveAll button.
The MainWindow has a ContentControl and the content property is binded to the VM. At runtime on ButtonClickCommand, the View is instantiated and assigned to the Content Property.
This SaveAll button will internally call methods associated with UserControls Save button. As such, SaveAll doesn't have it's own Method.
This has to be implemented by DependencyProperty.
I had once seen this scenario implemented in a Business App, but somehow missed the concept behind it.
I can't get what was the logic behind this, but it's a very useful thing.
Now I have to implement this, but i'm missing a small thing, I dont know.
I hope the scenario is clear.
Please help me in this scenario, with code.
Thanks,
VJ
Since you mentioned MVVM, here's what you might be looking for. Mind you, this will be a lot cleaner and easier if you use an MVVM framework such as Caliburn, but for this sample, its just vanilla MVVM:
public class MainViewModel
{
public MainViewModel()
{
ViewOneModel = new SubViewModel();
ViewTwoModel = new SubViewModel();
Children = new List<SubViewModel>(new[] { ViewOneModel, ViewTwoModel });
}
public void SaveAll()
{
foreach(var child in Children)
{
child.Save();
}
}
public IList<SubViewModel> Children { get; private set; }
public SubViewModel ViewOneModel { get; set; }
public SubViewModel ViewTwoModel { get; set; }
}
public class SubViewModel
{
public void Save()
{
}
}
and on the UI you basically have subviews (UserControls) composed in your main view:
<StackPanel>
<Button Width="100" Height="20" Content="Save All" />
<local:ViewOne DataContext="{Binding ViewOneModel}" />
<local:ViewTwo DataContext="{Binding ViewTwoModel}" />
</StackPanel>
You just need to bind the save methods to your buttons using an ICommand interface (preferably RelayCommand instance).
Imho in this scenario there is no need for RoutedEvents. The way I would solve it:
There is a Main-ViewModel that exposes 3 properties with the Sub-ViewModels.
The MainViewModel is the Datacontext for the window, and the subviewmodels bound to the datacontext of the 3 usercontrols.
The sub vm's are exposing a property with a Save-Command. This command is bound to the save buttons in the usercontrols.
The main vm is exposing a property with a saveall-command, which is bound to the SaveAll button.
In the handler of the save all command you are then iterating over the sub-vm's and call save on them.

Databinding with INotifyPropertyChanged instead of DependencyProperties

I have been wrestling with getting databinding to work in WPF for a little over a week. I did get valuable help here regarding the DataContext, and I did get databinding to work via DependencyProperties. While I was learning about databinding, I came across numerous discussions about INotifyPropertyChanged and how it is better than DPs in many ways. I figured that I would give it a shot and try it out.
I am using Josh Smith's base ViewModel class and my ViewModel is derived from it. However, I'm having a bit of trouble getting databinding to work, and am hoping that someone here can tell me where I'm going wrong.
In my ViewModel class, I have an ObservableCollection<string>. In my GUI, I have a combobox that is bound to this OC, i.e.
<ComboBox ItemsSource="{Binding PluginNames}" />
The GUI's DataContext is set to the ViewModel, i.e.
private ViewModel _vm;
public GUI()
{
InitializeComponent();
_vm = new ViewModel();
this.DataContext = _vm;
}
and the ViewModel has the OC named "PluginNames":
public class ViewModel
{
public ObservableCollection<string> PluginNames; // this gets instantiated and added to elsewhere
}
When the GUI is loaded, a method is called that instantiates the OC and adds the plugin names to it. After the OC is modified, I call RaisePropertyChanged( "PluginNames"). I was expecting that since the WPF databinding model is cognizant of INotifyPropertyChanged, that this is all I needed to do and it would "magically work" and update the combobox items with the plugins that got loaded... but it doesn't.
Can someone please point out what I've done wrong here? Thanks!
UPDATE: I'm not sure why, but now instead of not doing any apparent updating, it's not finding the property at all. I think I'm being really stupid and missing an important step somewhere.
When you're working with INotifyPropertyChanged, there are two things:
You'll need to use properties, not fields
You should always raise the property changed event when you set hte properties.
You'll want to rework this so it looks more like:
private ObservableCollection<string> pluginNames;
public ObservableCollection<string> PluginNames
{
get { return pluginNames; }
set {
this.pluginNames = value;
RaisePropertyChanged("PluginNames"); // This should raise the PropertyChanged event - use whatever your VM class does for this
}
}
That should cause everything to repopulate.
It looks like you've exposed field not property. Bindings works for properties only... Change it to:
public class ViewModel
{
public ObservableCollection<string> PluginNames {get; private set;}
}

Resources