I have a problem with Catel in showing the results of field validations (done in a view model or a model) in a view: with an error in a field, the corresponding textbox shall be marked like with a red frame. But for some reason I do not get this working.
This is a very simplified test scenario with a viewmodel having 2 integer fields, and validation rules requiring both having values < 100:
public class MainViewModel : ViewModelBase
{
public MainViewModel() : base()
{ }
protected override async Task InitializeAsync()
{
await base.InitializeAsync();
}
protected override async Task CloseAsync()
{
await base.CloseAsync();
}
public override string Title { get { return "Test"; } }
public int Value1
{
get { return GetValue<int>(Value1Property); }
set { SetValue(Value1Property, value); }
}
public static readonly PropertyData Value1Property = RegisterProperty(nameof(Value1), typeof(int), 42 );
public int Value2
{
get { return GetValue<int>(Value2Property); }
set { SetValue(Value2Property, value); }
}
public static readonly PropertyData Value2Property = RegisterProperty(nameof(Value2), typeof(int), 99);
protected override void ValidateFields(List<IFieldValidationResult> validationResults)
{
if (Value1 >= 100)
{
validationResults.Add(FieldValidationResult.CreateError(Value1Property, "Value1 must be < 100" ));
}
if (Value2 >= 100)
{
validationResults.Add(FieldValidationResult.CreateError(Value1Property, "Value2 must be < 100"));
}
}
protected override void ValidateBusinessRules(List<IBusinessRuleValidationResult> validationResults)
{ }
}
}
Please note: in my real project, the fields and validation would be in a model, but for test reasons I have stripped this down to just a view and a view model.
And this simple view has the viewmodel as the datacontext:
<catel:Window x:Class="WPF_Catel_Validation.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:catel="http://schemas.catelproject.com">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<TextBox Text="{Binding Value1, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" Width="100" />
<TextBox Text="{Binding Value2, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" Width="100" />
</StackPanel>
</catel:Window>
The connection between view and viewmodel works. The view also shows an error when a non-numeric text is entered in a textbox. The viewmodel with the method ValidateFields() also identifies any errors, but the view does not show these validation errors with a red frame around the textbox.
I have done my tests with Catel 5.8.0 and .NET 4.7.2. I wonder a little how the Catel-class ViewModelBase can implement INotifyDataErrorInfo, but the event ErrorsChanged is not visible in that class. But in general I have no clue if something is wrong with my viewmodel, or my view, or with Catel, or anything else? I also did not find any up-to-date documentation on Catel. Any suggestions are highly appreciated - thanks!
Probably you need to set DeferValidationUntilFirstSave to false if you want to show them immediately.
Related
I'm trying to make a control that has a current value with an optional equation string.
I have 2 textboxes:
One (a) where you can enter an equation shortcut to a value to put into the other (b).
(b) contains the actual value.
(for example, in (a), if you enter 'pi', the second will then fill with "3.1415926535897931")
I'm using 2 textboxes so the user can refine their equation if they need to, and watch the value change as they modify it.
The data has 2 fields, one being the equation string and the other being the current value.
so I have (a).Text bound to the string, a new property on (a) that holds the value, and I bind (b).Text to the value also.
(a).Text is TwoWay
(a).Value is OneWayToSource (since changes to the text should only be pushed to b)
(b).Value is TwoWay
This all works fine if I have the data set in the constructor before any XAML binding, but does not work at all if I add the data after binding.
Here is a minimal amount of code that shows the problem.
The only comment is at the line that can make it work or not.
As a last resort I could turn it into a custom control and handle it in the code-behind, but I'd think this should work in the first place.
Any ideas why this isn't working?
Thanks!
Here is the XAML:
<Window x:Class="twoBindingsOnSameField.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:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:twoBindingsOnSameField"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Content="load data" Click="Button_Click" Width="80" IsEnabled="{Binding NeedsData}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="enter text:" Width="80"/>
<local:TextBoxCalc Text="{Binding Item.ItemString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextBoxCalculatedValue="{Binding Item.ItemValue, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
Width="200"
IsEnabled="{Binding HasData}"
/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="updated text:" Width="80"/>
<TextBox Text="{Binding Item.ItemValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="200"
IsEnabled="{Binding HasData}"
/>
</StackPanel>
</StackPanel>
</Window>
Here is the codebehind.
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace twoBindingsOnSameField
{
public partial class MainWindow : Window
{
data data;
public MainWindow()
{
InitializeComponent();
data = new data();
/// ---- Does not work with the following line commented out, but does if it is uncommented ----
/// ---- use the button to set the data ----
//setdata();
DataContext = data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
setdata();
}
void setdata()
{
if (data.Item == null)
data.Item = new dataitem();
}
}
public class data : notifybase
{
dataitem item;
public data()
{
}
public dataitem Item
{
get
{
return item;
}
set
{
if (item != value)
{
item = value;
notifyPropertyChanged("Item");
notifyPropertyChanged("HasData");
notifyPropertyChanged("NeedsData");
}
}
}
public bool HasData
{
get
{
return Item != null;
}
}
public bool NeedsData
{
get
{
return Item == null;
}
}
}
public class dataitem : notifybase
{
string itemString;
string itemValue;
public dataitem()
{
itemString = "3";
itemValue = "4";
}
public virtual string ItemString
{
get
{
return this.itemString;
}
set
{
if (!object.Equals(this.itemString, value))
{
this.itemString = value;
notifyPropertyChanged("ItemString");
}
}
}
public virtual string ItemValue
{
get
{
return this.itemValue;
}
set
{
if (!object.Equals(this.itemValue, value))
{
this.itemValue = value;
notifyPropertyChanged("ItemValue");
}
}
}
}
public class TextBoxCalc : TextBox
{
public TextBoxCalc()
{
TextProperty.AddHandler(this, (o,e)=>TextBoxCalculatedValue="updated:" + Text);
}
#region TextBoxCalculatedValue
public static DependencyProperty TextBoxCalculatedValueProperty = DependencyProperty.Register("TextBoxCalculatedValue", typeof(string), typeof(TextBoxCalc), new PropertyMetadata(""));
public string TextBoxCalculatedValue
{
get
{
return (string)GetValue(TextBoxCalculatedValueProperty);
}
set
{
if (!object.Equals(TextBoxCalculatedValue, value))
SetValue(TextBoxCalculatedValueProperty, value);
}
}
#endregion
}
public class notifybase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
protected virtual void notifyPropertyChanged(string propertyName)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
OnPropertyChanged(e);
}
}
static class extensions
{
public static void AddHandler(this DependencyProperty prop, object component, EventHandler handler)
{
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(prop, component.GetType());
if (dpd != null)
dpd.AddValueChanged(component, handler);
}
}
}
The reason why it works when you uncomment //setdata(); is because it is initializing the object in what is effectively your viewmodel, therefore you can change its properties via binding. To clarify as a side note, data would be your view model, and dataitem is your model, however you're dataitem is using INPC, so it doesn't really make sense in this case to have a viewmodel necessarily.
Anyways, the issue is that TextBoxCalculatedValue is set to a OneWayToSource binding. When you run the code commented out, its going to try and bind to a null value. When it does, it tries to update a null value, which isn't possible. WPF handles what would normally be a null exception automatically. When you update the dataItem by clicking the button, it doesn't update the object TextBoxCalc is bound to, so instead, it will continue trying to bind & update the null object. Change it to a TwoWay binding and you'll see a difference. Changing to TwoWay is probably your best option.
Good practice is to use constructor injection to practice dependency injection. With that being said, passing a dataItem to data would be the best route, and at the very least, initializing dataItem in data's constructor would be an ideal approach. So,
public data(dataItem item)
{
Item = item;
}
or
public data()
{
Item = new dataitem();
}
I have a custom class which a usercontrol has implemented as a dependency property in it's code behind.
public partial class HandControl
{
public HandControl()
{
InitializeComponent();
}
public Seat Seat
{
get
{
return (Seat)GetValue(SeatProperty);
}
set
{
SetValue(SeatProperty, value);
}
}
public static readonly DependencyProperty SeatProperty = DependencyProperty.Register("Seat", typeof(Seat), typeof(HandControl), new PropertyMetadata(null));
}
In my case I've bound the name property in that class to a label inside the usercontrols xaml.
<Label Content="{Binding Seat.Player.Name, RelativeSource={RelativeSource AncestorType={x:Type controls:HandControl}}}"/>
The view model of my window contains the property SeatTl and the xaml is binding to it:
public Seat SeatTr
{
get { return _seatTr; }
private set
{
_seatTr = value;
OnPropertyChanged();
}
}
<customControls:HandControl Grid.Row="1"
Grid.Column="3"
Seat="{Binding SeatTr}" />
However, when I change my class content (the name property) and manually raise OnPropertyChanged in my viewmodel (not the usercontrol), the label is not updated and still has the same content.
private void OnSeatChanged(Player player, SeatPosition seatPosition)
{
//... doing the changes ...\\
OnPropertyChanged("SeatTr");
}
Whats my problem? Anyone got a clue?
I think u should raise OnPropertyChanged for Seat.Player.Name property as It is being chaged.
I have an ObservableCollection of "Layouts" and a "SelectedLocation" DependencyProperty on a Window. The SelectedLocation has a property called "Layout", which is an object containing fields like "Name" etc. I'm trying to bind a combobox to the SelectedLayout but it's not working.
The following does not work, I've tried binding to SelectedItem instead to no avail. I believe it may be something to do with the fact that I'm binding to a subProperty of the SelectedLocation DependencyProperty (though this does implement INotifyPropertyChanged.
<ComboBox Grid.Row="2" Grid.Column="0" x:Name="cboLayout" ItemsSource="{Binding Layouts,ElementName=root}" SelectedValue="{Binding SelectedLocation.Layout.LayoutID,ElementName=root}" DisplayMemberPath="{Binding Name}" SelectedValuePath="LayoutID" />
However, the following works (Also bound to the "SelectedLocation" DP:
<TextBox Grid.Row="4" Grid.Column="1" x:Name="txtName" Text="{Binding SelectedLocation.Name,ElementName=root,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
What type property Layouts has? I suppose something like this this: IEnumerable<Layout>.
But you bind selected value to Layout.LayoutID. So you got situation, when combo box contains Layout objects, and you try to select it by Int identifier. Of course binding engine can't find any Int there.
I have no idea about details of your code, so one thing I could propose: try to reduce your binding expression: SelectedItem="{Binding SelectedLocation.Layout,ElementName=root}.
If no success, provide more code to help me understand what's going on.
====UPDATE====
As I've said, you are obviously doing something wrong. But I am not paranormalist and couldn't guess the reason of your fail (without your code). If you don't want to share your code, I decided to provide simple example in order to demonstrate that everything works. Have a look at code shown below and tell me what is different in your application.
Class Layout which exposes property LayoutId:
public class Layout
{
public Layout(string id)
{
this.LayoutId = id;
}
public string LayoutId
{
get;
private set;
}
public override string ToString()
{
return string.Format("layout #{0}", this.LayoutId);
}
}
Class SelectionLocation which has nested property Layout:
public class SelectedLocation : INotifyPropertyChanged
{
private Layout _layout;
public Layout Layout
{
get
{
return this._layout;
}
set
{
this._layout = value;
this.OnPropertyChanged("Layout");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
And Window class with dependency properties (actually, in my example StartupView is UserControl, but it doesn't matter):
public partial class StartupView : UserControl
{
public StartupView()
{
InitializeComponent();
this.Layouts = new Layout[] { new Layout("AAA"), new Layout("BBB"), new Layout("CCC") };
this.SelectedLocation = new SelectedLocation();
this.SelectedLocation.Layout = this.Layouts.ElementAt(1);
}
public IEnumerable<Layout> Layouts
{
get
{
return (IEnumerable<Layout>)this.GetValue(StartupView.LayoutsProperty);
}
set
{
this.SetValue(StartupView.LayoutsProperty, value);
}
}
public static readonly DependencyProperty LayoutsProperty =
DependencyProperty.Register("Layouts",
typeof(IEnumerable<Layout>),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
public SelectedLocation SelectedLocation
{
get
{
return (SelectedLocation)this.GetValue(StartupView.SelectedLocationProperty);
}
set
{
this.SetValue(StartupView.SelectedLocationProperty, value);
}
}
public static readonly DependencyProperty SelectedLocationProperty =
DependencyProperty.Register("SelectedLocation",
typeof(SelectedLocation),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
}
XAML of StartupView:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:HandyCopy"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Root">
<WrapPanel>
<ComboBox ItemsSource="{Binding Path=Layouts,ElementName=Root}"
SelectedItem="{Binding Path=SelectedLocation.Layout, ElementName=Root}"/>
</WrapPanel>
</UserControl>
I'm building an application using WPF and MVVM. I've come across a situation where I have a view containing a usercontrol (representing a Timer). This usercontrol has a property in it's code behind which performs some calculations before getting and setting data.
TimerControl.xaml.cs:
public DateTime? DateTimeValue
{
get
{
string hours = this.txtHours.Text;
string minutes = this.txtMinutes.Text;
string amPm = this.txtAmPm.Text;
if (!string.IsNullOrWhiteSpace(hours) && !string.IsNullOrWhiteSpace(minutes) && !string.IsNullOrWhiteSpace(amPm))
{
string value = string.Format("{0}:{1} {2}", this.txtHours.Text, this.txtMinutes.Text, this.txtAmPm.Text);
DateTime time = DateTime.Parse(value);
return time;
}
else
{
return null;
}
}
set
{
DateTime? time = value;
if (time.HasValue)
{
string timeString = time.Value.ToShortTimeString();
//9:54 AM
string[] values = timeString.Split(':', ' ');
if (values.Length == 3)
{
this.txtHours.Text = values[0];
this.txtMinutes.Text = values[1];
this.txtAmPm.Text = values[2];
}
}
}
}
Now I wanted to bind this property to a property present in view model of the view. Following is property in the VM:
public DateTime? StartTime
{
get
{
return _StartTime;
}
set
{
_StartTime = value;
RaisePropertyChanged("StartTime");
}
}
This is how I am performing binding in the xaml of View.
MyView.xaml:
<my:TimeControl Background="White" Grid.Column="1" Grid.Row="2" Margin="3" x:Name="StartTimeControl" DateTimeValue="{Binding StartTime}" Width="150" Height="26" HorizontalAlignment="Left">
But it is giving me an error that:
A 'Binding' cannot be set on the 'DateTimeValue' property of type 'TimeControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
I've been struggling for hours trying to figure out a way to make this binding work. I have even tried to make a dependency property in the TimeControl's code behind for the DateTimeValue property, which has resolved the above exception, but the binding still doesn't work. Whenever I access StartTime property in the VM's code behind, it is showing null. Although it should show a valid value by getting the DateTimeValue property.
Kindly suggest me a way to make this work. Thanks.
Your implementation of DateTimeValue property shown in this question is certainly wrong and leads to exception, because DateTimeValue should be dependency property.
But you mentioned that you have tried to use dependency property with no success. I suppose the reason is in collision of DataContexts and your XAML looks like this:
<UserControl x:Class="Test.SomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:Test"
Name="Root">
<WrapPanel>
<self:TimerControl Time="{Binding StartTime}"/>
</WrapPanel>
</UserControl>
This code doesn't work. Why? DataContext of TimerControl is inherited (or maybe you replace it at all), meanwhile when you address to StartTime you have in mind ViewModel as DataContext. So you should clearly point to correct DataContext:
<self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
===UPDATE===
The whole code of my Timer control (as you can see my Timer has textbox, when you input some text, textbox raises appropriate event, which we handle and set Time property):
public partial class Timer : UserControl
{
public Timer()
{
InitializeComponent();
}
public DateTime? Time
{
get
{
return (DateTime?)this.GetValue(Timer.TimeProperty);
}
set
{
this.SetValue(Timer.TimeProperty, value);
}
}
public static readonly DependencyProperty TimeProperty =
DependencyProperty.Register("Time",
typeof(DateTime?),
typeof(Timer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) => { }));
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (DateTime.Now.Ticks % 2 == 0)
{
this.Time = DateTime.Now;
}
else
{
this.Time = null;
}
}
}
And XAML:
<UserControl x:Class="Test.Timer">
<Grid>
<TextBox TextChanged="TextBox_TextChanged"/>
</Grid>
</UserControl>
Usage of Time control in XAML:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:Test"
Name="Root">
<WrapPanel>
<self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
</WrapPanel>
</UserControl>
Code behind of StartupView:
public StartupView()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
Property in ViewModel remains the same. During debugging setter of StartTime property fires every time when I change text in Timer.
What excatly do you want to do?
You can't bind to a standard property. If you want to bind you should use a dependency property.
public DateTime? DateTimeValue
{
get { return (DateTime?)GetValue(DateTimeValueProperty); }
set { SetValue(DateTimeValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DateTimeValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DateTimeValueProperty =
DependencyProperty.Register("DateTimeValue", typeof(DateTime?), typeof(TimeControl), new UIPropertyMetadata(null));
Inside the UserControl:
<TextBox Text="{Binding DateTimeValue,RelativeSource={RelativeSource AncestorLevel=1, Mode=FindAncestor,AncestorType=UserControl}, Converter=...}" />
To bind directly to a DateTimeValue is not possible because there is no converter available for string->DateTime so you have to write an IValueConverter and specify this in your binding.
From outside of course you should be able to bind the value directly.
I have created a user control "SearchControl"(which will be reused further in other screens as well.
SearchControl ->
<usercontrol name="SearchControl"......>
<stackpanel orientation="horizontal"...>
<TextBox Text"{Binding Path=UserId}"...>
<Button Content="_Search" ....Command="{Binding Path=SearchCommand}"..>
</stackpanel>
</usercontrol>
public partial class SearchControl : UserControl
{
public SearchControl()
{
InitializeComponent();
DataContext=new UserViewModel();
}
}
I then use this control in a window "UserSearch"
<window name="UserSearch".............
xmlns:Views="Namespace.....Views">
<Grid>
<Grid.RowDefinitions>
<RowDefinition..../>
<RowDefinition..../>
<RowDefinition..../>
<RowDefinition..../>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition..../>
<ColumnDefinition..../>
</Grid.ColumnDefinitions>
<Views:SearchControl Grid.Row="0" Grid.Colspan="2"/>
<TextBlock Text="User Id" Grid.Row="1" Grid.Column="0"..../>
<TextBox Text="{Binding Path=UserId}" Grid.Row="1" Grid.Column="1".../>
<TextBlock Text="First Name" Grid.Row="2" Grid.Column="0"..../>
<TextBox Text="{Binding Path=FirstName}" Grid.Row="2" Grid.Column="1".../>
<TextBlock Text="Last Name" Grid.Row="3" Grid.Column="0"..../>
<TextBox Text="{Binding Path=LastName}" Grid.Row="3" Grid.Column="1".../>
</Grid>
</window>
public partial class UserSearch : Window
{
public UserSearch()
{
InitializeComponent();
DataContext=new UserViewModel();
}
}
What I am aimimg for:
When I enter UserId inthe textbox in SearchControl and click on Search button, the resulting record which is retieved should be displayed in the textboxes for UserId, FirstName, LastName
class UserViewModel:INotifyPropertyChanged
{
DBEntities _ent; //ADO.Net Entity set
RelayCommand _searchCommand;
public UserViewModel()
{
_ent = new DBEntities();
}
public string UserId {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public ICommand SearchCommand
{
get
{
if(_searchCommand == null)
{
_searchCommand = new RelayCommand(param = > this.Search());
}
return _searchCommand;
}
}
public void Search()
{
User usr = (from u in _ent
where u.UserId = UserId
select u).FirstOrDefault<User>();
UserId = usr.UserId;
FirstName = usr.FirstName;
LastName = usr.LastName;
OnPropertyChanged("UserId");
OnPropertyChanged("FirstName");
OnPropertyChanged("LastName");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertChangedEventArgs(propertyName);
}
}
Here as I am using two separate instances of the UserViewModel for the SearchControl and UserSearch, even though I retieve the record for the particular user on searching by UserId, I am unable to bind the properties UserId, FullName , LastName with the respective textboxes...How do I fix this problem??
1) Don't let the View initialize the presentation model, it should be the other way round. The presentation model is the object of interest, not the particular view.
public interface IView
{
void SetModel(IPresentationModel model);
}
publiv class View : UserControl, IView
{
public void SetModel(IPresentationModel model)
{
DataContext = model;
}
}
public class PresentationModel : IPresentationModel
{
public PresentationModel(IView view)
{
view.SetModel(this);
}
}
2) Don't set the data context of the subview in the code behind file. Usually, the view that uses the subview sets the data context in the xaml file.
3) Usually each view has its own presentation model. The presentation model should have one type of view. That means that different views of a single presentation model may differ in appearance but not in functionality (in your case one view is used to search, the other one is used to display and edit data). So, you have vialoted the Single Responsibilty Principle.
4) Abstract your data access layer, otherwise you won't be able to unit test your presentation model (because it needs access to the data base directly). Define an repository interface and implementation:
public interface IUserRepository
{
User GetById(int id);
}
public class EntityFrameworkUserRepository : IUserRepository
{
private readonly DBEntities _entities;
public EntityFrameworkUserRepository(DBEntities entities)
{
_entities = entities;
}
public User GetById(int id)
{
return _entities.SingleOrDefault(u => u.UserId == id);
}
}
5) Don't use FirstOrDefault because an ID is unique, so there must not be several users for one id. SingleOrDefault (used in the code snippet above) throws an exception if more than one result is found but returns null if none is found.
6) Bind directly to your entity:
public interface IPresentationModel
{
User User { get; }
}
<StackPanel DataContext="{Binding Path=User}">
<TextBox Text="{Binding Path=FirstName}" />
<TextBox Text="{Binding Path=LastName}" />
</StackPanel>
7) Use the CommandParameter to provide the user id you are searching for directly with your command.
<TextBox x:Name="UserIdTextBox">
<Button Content="Search" Command="{Binding Path=SearchCommand}"
CommandParameter="{Binding ElementName=UserIdTextBox, Path=Text}" />
public class PresentationModel
{
public ICommand SearchCommand
{
// DelegateCommand<> is implemented in some of Microsoft.BestPractices
// assemblies, but you can easily implement it yourself.
get { return new DelegateCommand<int>(Search); }
}
private void Search(int userId)
{
_userRepository.GetById(userId);
}
}
8) If only data binding causes issues, look at the following website to get some ideas how to debug wpf data bindings: http://beacosta.com/blog/?p=52
9) Don't use strings that contain property names. Once you refactor your code and properties change their names, to will have a stressful time finding all property names in strings and fixing them. Use lambda expressions instead:
public class PresentationModel : INotifiyPropertyChanged
{
private string _value;
public string Value
{
get { return _value; }
set
{
if (value == _value) return;
_value = value;
RaisePropertyChanged(x => x.Value);
}
}
public PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(Expression<Func<PresentationModel, object>> expression)
{
if (PropertyChanged == null) return;
var memberName = ((MemberExpression)expression.Body).Member.Name;
PropertyChanged(this, new PropertyChangedEventArgs(memberName));
}
}
I wish you the best to solve your problem and I hope that I could help you a little bit.
Best Regards
Oliver Hanappi