Reputation: 585
I am just starting out with WPF and MVVM. I know this question was probably asked a lot on SO but I have read everything I could find on MVVM and EF, tried every example and tutorial out there, read "How to survive MVVM for Enterprise" but I still cannot understand how to properly use the pattern and the framework for a simple bind to a textbox. Can someone please help me with an easy to understand example?
I have created a model using EF Designer. Inside my Model folder, the LocationModel.Context looks like this:
MODEL:
namespace Location.Model
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class DailyEntities : DbContext
{
public DailyEntities()
: base("name=DailyEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<LocationKPI> LocationKPI { get; set; }
}
}
LocationKPI.cs
namespace Location.Model
{
using System;
using System.Collections.Generic;
public partial class LocationKPI
{
public long sMonth { get; set; }
public Nullable<decimal> Efficiency { get; set; }
}
}
I've created a ViewModel folder with a class that implements INotifyPropertyChanged:
VIEWMODEL:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Location.ViewModel
{
public class LocationViewModel: INotifyPropertyChanged
{
public decimal Efficiency
{
get { return _Efficienecy; }
set
{
_Efficiency = value;
OnPropertyChanged("Efficiency");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="Location.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Location"
xmlns:vm="clr-namespace:Location.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DatePicker HorizontalAlignment="Left" Margin="392,17,0,0" VerticalAlignment="Top"/>
<TextBlock Text="Efficiency"></TextBlock>
<TextBox Text="{Binding Efficiency}" ></TextBox>
</Grid>
</Window>
I am really losing my mind here. I am looking for some guidance on how to "glue" the model and the viewmodel to my view and I just need a simple example to understand. I am sorry if the question is too broad.
Is the query supposed to be written in the model or the viewmodel? How do I define the data context and write a simple select Efficiency where sMonth = 9
statement using linq? How do add the month from the datepicker as a parameter to the above query? I would be so grateful for any kind of help. Thank you.
Upvotes: 1
Views: 1445
Reputation: 657
I am going to address your MVVM issue and leave the data source for another discussion. I am going to try to give you some good habits along with showing you how to wire together you MVVM environment.
Let's start with the INotifyPropertyChanged interface and how to implement it. Generally speaking I try to create an abstract classes that both the Model and ViewModel utilize that contains everything that is common among all my models and viewmodels. Here are those Abstract Classes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfStackOverflow.Models
{
/// <summary>
/// Abstract class that provides common functionality accross all View Models.
/// </summary>
public abstract class modelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
/// <summary>
/// Provides a simplefied INotifyPropertyChanged interface.
/// requires System.Runtime.CompilerServices and
/// System.ComponentModel.
/// </summary>
/// <param name="property"></param>
public void SetPropertyChanged([CallerMemberName] string property = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public event PropertyChangedEventHandler PropertyChanged;
}
#endregion
}
namespace WpfStackOverflow.ViewModels
{
/// <summary>
/// Abstract class that provides common functionality accross all View Models.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
/// <summary>
/// Provides a simplefied INotifyPropertyChanged interface.
/// requires System.Runtime.CompilerServices and
/// System.ComponentModel.
/// </summary>
/// <param name="property"></param>
public void SetPropertyChanged([CallerMemberName] string property = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
each class resides in a Models and ViewModels folder respectfully. Here is the View Model I created:
namespace WpfStackOverflow.ViewModels
{
class BasicMVVMViewModel:ViewModelBase
{
private ObservableCollectionEx<LocationKPI> _locations;
public BasicMVVMViewModel()
{
CreateTestData();
}
private DateTime _selectedDate;
public DateTime SelectedDate
{
get { return _selectedDate; }
set
{
_selectedDate = value;
SetPropertyChanged();
SetSelectedLocationKPI( _selectedDate.Month);
}
}
private LocationKPI _selectedLocationKPI;
public LocationKPI SelectedLocationKPI
{
get { return _selectedLocationKPI; }
set
{
_selectedLocationKPI = value;
SetPropertyChanged();
}
}
private void SetSelectedLocationKPI(long sMonth)
{
SelectedLocationKPI = _locations.Where(p => p.SMonth.Equals(sMonth)).FirstOrDefault();
}
private void CreateTestData()
{
_locations = new ObservableCollectionEx<LocationKPI>();
for(int i=0; i < 12;i++)
{
_locations.Add(new LocationKPI() { SMonth = i, Efficiency = i*17 });
}
}
}
}
Finally here is the XAML Markup:
<UserControl x:Class="WpfStackOverflow.Controls.BasicMVVM"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfStackOverflow.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DatePicker HorizontalAlignment="Left" Margin="30,5,0,0" Grid.Row="1" VerticalAlignment="Top" Width="150" SelectedDate="{Binding SelectedDate}"/>
<TextBlock x:Name="textBlock" HorizontalAlignment="Stretch" Grid.Row="2" TextWrapping="Wrap"
Text="{Binding SelectedLocationKPI.Efficiency,Mode=TwoWay}" VerticalAlignment="Stretch" Margin="5"/>
</Grid>
</UserControl>
In the Viewmodel I call a method that creates the test data. In your case you can get your data from a linq query. (you figure it out :))
You will have to adjust some references and namespaves to fit your environment. What I posted here is tested and works. I hope it helps you understand MVVM better. Oh yeah, one more thing, the XAML you see here is from a UserControl not a XAML Window. In the codebehind you have to instantiate the ViewModel and set it as the DataContext:
namespace WpfStackOverflow.Controls
{
/// <summary>
/// Interaction logic for BasicMVVM.xaml
/// </summary>
public partial class BasicMVVM : UserControl
{
public BasicMVVM()
{
InitializeComponent();
DataContext = new BasicMVVMViewModel();
}
}
}
Good Luck with this. StackOverflow is mostly used to solve actual coding issues. This case is actually a coding lesson. Try to avoid using StackOverflow for lessons in the future.
Upvotes: 2