I have a property Name which is required in the model. Then in the VM I have a Name property that access the one of the model. And in my view I bind a textbox with the Name property of the VM. But the textbox does not get in red when it is empty. I do get the error : Field Name is required. But once again the textbox does not turn red. Please help
here is the model :
public class Task : BasicAuditTrail
{
[Required]
[MaxLength(256)]
public string Name { get; set; }
}
Here is the VM :
public class TaskManagerViewModel : ViewModelBase, ITaskManagerViewModel
{
public Task CurrentTask => taskManager.CurrentTask;
public string Name
{
get
{
return CurrentTask.Name;
}
set
{
CurrentTask.Name = value;
IsDirty = true;
RaisePropertyChanged();
}
}
The View :
<TextBox Grid.Row="0" Grid.Column="3" Name="Name" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
As mentioned in comments, the Name property of CurrentTask should be directly bind to the Textbox. This makes the error to work correctly.
To set the IsDirty property on the viemwodel, subscribe to the PropertyChanged event of the CurrentTask and set IsDirty there!
PS: make sure to remove the event when the CurrentTask is changing so as to not have any memory leaks
Related
Here is a simplified example of what i need:
I have class A that have a property Name. the Name property is changed asynchronously and there is no way to know when the modification occurs.
In order to show the updated value of it in the view, I wired a propertychanged event in it and bind it with {Binding A.Name}. In the VM it works fine.
But in my case, there is a lot of custom properties that shouldn't be in the class A. I'm thinking once propertychanged is raised in class A, the Name property in AViewModel should get notified and raise the OnPropertyChanged too
is there any way to do so ?
C# :
public class A : BaseViewModel
{
string name;
public string Name
{
get { return name; }
set { Set(()=> Name, ref name, value); }
}
}
public class AViewModel : BaseViewModel
{
A a;
public A A
{
get { return a; }
set { Set(()=> A, ref a, value); }
}
public string Name
{
get { return A.Name; }
set { Set(()=> Name, ref A.Name, value); }
}
}
XAML :
<TextBox Text="{Binding Name}" />
try to add the "RaisePropertyChanged" to the Name object:
string name;
public string Name
{
get { return name; }
set { Set(()=> Name, ref name, value); RaisePropertyChanged();}
}
Then include the update trigger on the Xaml:
<TextBox Text="{Binding Name}" UpdateSourceTrigger=PropertyChanged />
The class A must have a classic C# event, for example, so the AViewModel can subscribe it.
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 a simple dialog that contains edit boxes such as this:
<TextBox Text="{Binding Path=EmailSettings.SmtpServer, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" />
The dialog uses a Model as its data context (to simplify the model example INotifyPropertyChanged has not been shown nor is the code that creates the model and
sets the dialog data context to the model instance):
class EmailSettingsModel : IDataErrorInfo
{
public EmailSettingsModel ()
{
EmailSettings = new EmailSettings();
}
public EmailSettings EmailSettings
{ get; set; }
string _error;
public string Error
{
get { return _error; }
set { _error = value; }
}
public string this[string propertyName]
{
get
{
string errorMessage = null;
if ( string.Compare( propertyName, "EmailSettings.SmtpServer" ) == 0 )
{
if ( !string.IsNullOrWhiteSpace( EmailSettings.SmtpServer ) )
errorMessage = "SMTP server is not valid";
}
Error = errorMessage;
}
}
}
The model contains a property that is a simple POCO class that has several properties on it.
class EmailSettings
{
public string SmtpServer
{ get; set; }
}
I could not get the IDataErrorInfo indexer to fire and spent hours looking. When I changed the binding on the text box to use a simple property:
<TextBox Text="{Binding Path=SmtpServer, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" />
on the Model as below the IDataErrorInfo indexer fired.
class EmailSettingsModel
{
public string SmtpServer
{ get; set; }
}
Was IDataErrorInfo not called because I used a compound property for the binding statement. I have used complex properties like this for normal data binding and they work but for this example IDataErrorInfo was not called.
IDataErrorInfo fires only at the level where implemented
For example if you have Binding Path looking like this "viewModel.property1.property2.property3" you will need to implement IDataErrorInfo inside the class of viewModel and inside the class of property1 and inside the class of property2. Property3 is a string.
So in order to make it work for you just implement IDataErrorInfo anywhere else.
I am trying to update a textblock on the view by databinding to a property in the viewmodel (the datacontext for the view).
In the code below; when SelectedItem changes, I want the textblock text to update with the value of the Name property on SelectedItem.
In an attempt to achieve this I have set the binding source to the property that is changing and the binding path to the data I want to update the textblock with.
I.e. I am expecting that the binding engine will see a change on the binding Source (SelectedItem) and pull the data from the binding Path (SelectedItem.Name).
http://msdn.microsoft.com/en-us/library/ms746695.aspx
Setting the SelectedItem raises INPC but the text does not update.
public class ViewModel
{
public IConfiguration Configuration { get; set;}
}
public class Configuration : IConfiguration, INotifyPropertyChanged
{
public Item SelectedItem
{
get { return _item;}
set
{
_item = value;
ItemName = _item.Name;
RaisePropertyChangedEvent("SelectedItem");
}
}
public string ItemName
{
get { return _itemName;}
set
{
_itemName= value;
RaisePropertyChangedEvent("ItemName");
}
}
}
public class Item
{
public string Name { get; set;}
}
I know that changes on Configuration are seen because this works:
<TextBlock Text="{Binding Configuration.ItemName}"/>
But this does not:
<TextBlock Text="{Binding Path=Name, Source=Configuration.SelectedItem}"/>
And nor does this:
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name, Source=Configuration.SelectedItem}"/>
I'm assuming that this should be straightforward - what have I missed?
I've never actually seen anyone use Binding.Source before, so I don't know much about it. But my guess is that it's not dynamic. When you create your binding, it's grabbing a reference to the object specified in your Source, and then that's it: it uses that same reference for the lifetime of the binding.
Why make this complicated? Just use Path. That's the normal way of doing binding, and it's dynamic all the way -- what you're doing is exactly what Path is intended for.
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name}"/>
This is probably working, you just can not see it. The Binding engine has not been notified that the Name property of the Item object has changed.
Try implementing the INotifyPropertyChanged interface on the Item class as well (raising the PropertyChanged event as necessary)
This will work for your third binding situation, and also for a similar definition as below
<TextBlock DataContext="{Binding Path=Configuration.SelectedItem}" Text="{Binding Path=Name}"/>
But for a simpler fix, this should work:
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name}" />
Edit:
public class Configuration : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private Item _SelectedItem = null;
public Item SelectedItem
{
get
{
return _SelectedItem;
}
set
{
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
public class Item
{
public string Name { get; set; }
}
Then in a Command Execute somewhere I have this:
Configuration.SelectedItem = new Item() { Name = "test" };
Which updates the TextBlock in the View fine:
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name}" />
I am trying to bind a combo box to a list of objects, and it works great, besides the selected value, am I missing somethign?
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValuePath="country_code" DisplayMemberPath="country_name"
SelectedValue="{Binding OrderInfoVm.BillingCountry}" />
Basically I want to bind value to country codes and set the selected value to the country code bound to OrderInfoVm.BillingCountry (which implements INotifyPropertyChanged)
Initially when the control loads selected value is empty, but on click BillingCountry is populated. Selected value does not seem to change. How can I remedy that?
I do agree with Alex that using SelectedItem gives the desired behaviour. See the code below. It works and will hopefully help you further:
<Window x:Class="SelectedValueSpike.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValuePath="country_code" DisplayMemberPath="country_name"
SelectedItem="{Binding OrderInfoVm.BillingCountry}"
IsSynchronizedWithCurrentItem="True"
Name="AllCountriesBox"/>
<TextBox Text="{Binding ElementName=AllCountriesBox, Path=SelectedValue}"/>
<Button>
Change the textbox to "Ca","NL",or "US" and click!
</Button>
</StackPanel>
</Window>
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace SelectedValueSpike
{
public partial class Window1 : Window
{
public OrderInfoVm OrderInfoVm{ get; set;}
public Window1()
{
InitializeComponent();
OrderInfoVm=new OrderInfoVm();
OrderInfoVm.AllCountries.Add(new Country("US","US of A"));
OrderInfoVm.AllCountries.Add(new Country("NL","Netherlands"));
OrderInfoVm.AllCountries.Add(new Country("Ca","Canada"));
OrderInfoVm.BillingCountry = OrderInfoVm.AllCountries[1];
DataContext = this;
}
}
public class OrderInfoVm:INotifyPropertyChanged
{
public OrderInfoVm()
{
AllCountries=new ObservableCollection<Country>();
}
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Country> _allCountries;
public ObservableCollection<Country> AllCountries
{
get { return _allCountries; }
set
{
_allCountries = value;
OnPropertyChanged("AllCountries");
}
}
private Country _billingCountry;
public Country BillingCountry
{
get { return _billingCountry; }
set
{
_billingCountry = value;
OnPropertyChanged("BillingCountry");
}
}
private void OnPropertyChanged(string property)
{
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs(property));
}
}
public class Country
{
public string country_code { get; set; }
public string country_name { get; set; }
public Country(string code, string name)
{
country_code = code;
country_name = name;
}
}
}
Maybe you are trying to implement something similar to this: Bound ComboBox
Try changing it to SelectedItem and set Mode=TwoWay...
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValuePath="country_code" DisplayMemberPath="country_name"
SelectedItem="{Binding OrderInfoVm.BillingCountry, Mode=TwoWay}" />
Edit: You may not need to change it to SelectedItem, perhaps just setting TwoWay will work, but that is how I've done it in my own code.
Please ensure that you've specified correct binding path.
Try starting project in debug mode and look at the output window to see if there are any binding errors
Give this a shot; I believe you have your SelectedValuePath and SelectedValue mixed up:
<ComboBox ItemsSource="{Binding OrderInfoVm.AllCountries}"
SelectedValue="country_code"
DisplayMemberPath="country_name"
SelectedValuePath="{Binding OrderInfoVm.BillingCountry}" />
For Reference:
ItemsSource = Gets or sets a collection used to generate the content of the ItemsControl (ComboBox).
SelectedValue = Gets or sets the value of the SelectedItem, obtained by using SelectedValuePath.
SelectedValuePath = Gets or sets a value that indicates the path used to get the SelectedValue from the SelectedItem.
DisplayMemberPath = Gets or sets a path to a value on the source object to serve as the visual representation of the object.