I´m using IDataErrorInfo for validation in WPF, But when the attribute is an array I can to process the error. ( in this case int[] Position)
My code is similar to this: http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
public class Customer : IDataErrorInfo
{
public string FirstName { get; set; }
public int[] Position { get; set; }
#region IDataErrorInfo Members
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string columnName]
{
get
{
string result = null;
if (columName == "FirstName")
{
// Do something
}
if (columnName == "Position")
{
// Do something
}
return result;
}
}
#endregion
}
XAML
<TextBox x:Name="tbFirstName" Grid.Row="2" Grid.Column="1" Width="50" HorizontalAlignment="left"
Validation.Error="Validation_Error" MaxLength="2"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Path=FirstName, ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />
<TextBox x:Name="tbPosition1" Grid.Row="2" Grid.Column="1" Width="50" HorizontalAlignment="left"
Validation.Error="Validation_Error" MaxLength="2"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Path=Position[0], ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />
I have no problem to capture "Firstname" but if I do a change in the textbox of btPosition1 the program don´t through the function to process the errors.
You bind to an item in array, not to array itself.
In this case, I think, WPF looks for IDataErrorInfo in Array class.
So you should either expose positions as separate properties (if they are finite), or use your own collection class: inherit List<> and add IDataErrorInfo implementation.
Related
I have a ListBox control with TypeUsers.When I select some record in Listbox and update Name in TextBox the Name property/textbox return always null. Never take value from TextBox, always null ?
Image description here
This is my code
<ListBox x:Name="LstTypeUsers"
Grid.Row="0" Grid.Column="4"
Width="220" Height="120"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding TypeUsers}"
DisplayMemberPath="Name">
</ListBox>
<TextBox
Grid.Row="0" Grid.Column="2"
x:Name="txtName"
HorizontalAlignment="Left" Height="23"
TextWrapping="Wrap"
VerticalAlignment="Top" Width="170"
Text="{Binding ElementName=LstTypeUsers, Path=SelectedItem.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Validation.ErrorTemplate="{x:Null}"/>
<Button
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="100" Height="30"
Command="{Binding UpdateTypeUserCmd}"
Grid.ColumnSpan="3" Margin="20,90,0,0">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/Save.png" />
<TextBlock Width="55" Height="18" ><Run Text=" "/><Run Text="Update"/></TextBlock>
</StackPanel>
</Button>
EDIT
// Model class
public class UserType: INotifyPropertyChanged
{
[Key]
private int usertypeId;
public int UserTypeId
{
get
{
return this.usertypeId;
}
set
{
this.usertypeId = value;
OnPropertyChanged("UserTypeId");
}
}
[MaxLength(200)]
private string name;
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
OnPropertyChanged("Name");
}
}
[Required]
private bool status;
public bool Status
{
get
{
return this.status;
}
set
{
this.status = value;
OnPropertyChanged("Status");
}
}
public virtual ObservableCollection<User> User { get; private set; }
public UserType()
{
this.User = new ObservableCollection<User>();
}
}
// ViewModelBase class
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// UserTypeViewModel
public class UserTypeViewModel
private UserType _userType;
private ObservableCollection<UserType> _UserTypeList;
// Constructor
public UserTypeViewModel()
{
_userType = new UserType();
_UserTypeList = new ObservableCollection<UserType>(GetUserTypeAll());
}
public ObservableCollection<TypeUsers> TypeUsers
{
get
{
return _UserTypeList;
}
set
{
_UserTypeList = value;
//OnPropertyChanged("TypeUsers");
}
}
public string Name
{
get
{
return _userType.Name;
}
set
{
_userType.Name = value;
//OnPropertyChanged("Name");
}
}
Thank you.
Implement INotifyPropertyChanged interface in UserType class.
You're binding directly to the WPF control (ListBox) and not the ViewModel. I suggest you add a property in your ViewModel that will bind to the TextBox.Text property, once the data changes or the user had changed the value in the TextBox, then the data will update and be reflected in the UI.
Also, if I remember correctly, at launch, the SelectedItem property of the ListBox is null, so there might be a problem there too, but I'm not certain about that...
I have resolved my problem. I have also implemented INotifyPropertyChanged interface in Model class. I'm new in WPF MVVM and I read that this interface is implemented only in the ViewModel for connection with View. Now I have implemented in both classes and everything works great.
Thanks Everyone.
Instance of MyViewModel is set as DataContext of the .Xaml file. Data is bound as below, but the Text for {FName1, FName2, LName1, LName2} are not getting displayed. Only the Text for (ThePerson) is getting displayed. Appreciate if any one have a suggestion to fix it.
.xaml file
<StackPanel>
<TextBlock x:Name="ThePerson" Text="{Binding PersonOne}" />
<TextBlock x:Name="FName1" Text="{Binding PersonOne.FirstName}" />
<TextBlock x:Name="FName2" Text="{Binding Person.FirstName}" />
<TextBlock x:Name="LName1" Text="{Binding Path=PersonOne.LastName}" />
<TextBlock x:Name="LName2" Text="{Binding Path=Person.LastName}" />
</StackPanel>
ViewModel file
public class MyViewModel {
public MyViewModel()
{
PersonOne = new Person()
{
FirstName = "James",
LastName = "San"
};
}
public Person PersonOne { get; set; }
}
public class Person {
public string FirstName;
public string LastName;
public override string ToString()
{
return string.Format("{0}, {1}", LastName, FirstName);
}
}
You need to make the FirstName, etc as properties of the Person class instead of fields. Add a getter and setter to them. You can only bind to properties.
I have a simple class abc
class abc
{
public string a { get; set; }
public string b { get; set; }
public string c { get; set; }
public abc(string d, string e, string f)
{
a = d;
b = e;
c = f;
}
}
public MainPage()
{
InitializeComponent();
abc obj = new abc("abc1", "abc2", "abc3");
LayoutRoot.DataContext = obj;
}
and a grid which contain three textbox 1 2 3 I am trying to bind these 3 properties of a class to a grid usercontrol.
<Grid x:Name="LayoutRoot" Background="White">
<TextBox Height="27" HorizontalAlignment="Left" Margin="125,86,0,0" Name="textBox1" Text="{Binding Path= a}" VerticalAlignment="Top" Width="120" />
<TextBox Height="25" HorizontalAlignment="Left" Margin="21,192,0,83" Name="textBox2" Text="{Binding Path= b}" Width="120" />
<TextBox Height="25" HorizontalAlignment="Left" Margin="250,192,0,0" Name="textBox3" Text="{Binding Path= c}" VerticalAlignment="Top" Width="120" />
</Grid>
it doesn't show any error but it does not show any output to a screen,what specific problem it creating?
Try do not use "Path= " (with space) in the binding expression. Try use:
Text="{Binding a}"
"Path" is present hiddenly in binding expressions. You need read some resources about bindings.
First your type 'abc' should implement INotifyPropertyChanged.
public class abc : INotifyPropertyChanged
{
...
}
Then you need to raise the INotifyPropertyChanged.PropertyChanged event
private void RaiseProperty(string propertyName)
{
var handle = PropertyChanged;
if(handle != null)
{
handle(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _a;
public string a { get{ return _a;} set{ _a = value; RaiseProperty("a"); } }
....
This should work as you need to a mechanism to notify the Binding if you are using CLR proprties; and that mechanism is provided by INotifyPropertyChanged interface
I am trying to bind a listview item to a member of a structure, but I am unable to get it to work.
The structure is quite simple:
public struct DeviceTypeInfo
{
public String deviceName;
public int deviceReferenceID;
};
in my view model I hold a list of these structures and I want to get the "deviceName" to be displayed in a list box.
public class DevicesListViewModel
{
public DevicesListViewModel( )
{
}
public void setListOfAvailableDevices(List<DeviceTypeInfo> devicesList)
{
m_availableDevices = devicesList;
}
public List<DeviceTypeInfo> Devices
{
get { return m_availableDevices; }
}
private List<DeviceTypeInfo> m_availableDevices;
}
I have tried the following but I can't get the binding to work, do I need to use relativesource?
<ListBox Name="DevicesListView" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="10" MinHeight="250" MinWidth="150" ItemsSource="{Binding Devices}" Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding DeviceTypeInfo.deviceName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You need to make the members in the struct properties.
public struct DeviceTypeInfo
{
public String deviceName { get; set; }
public int deviceReferenceID { get; set; }
};
I ran into a similar situation yesterday :P
EDIT: Oh yeah, and like Jesse said, once you turn them into properties, you'll want to set up the INotifyPropertyChanged event.
Your TextBlock's DataContext is an object of type DeviceTypeInfo, so you only need to bind to deviceName, not DeviceTypeInfo.deviceName.
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding deviceName}"/>
</StackPanel>
</DataTemplate>
In addition, you should be binding to Properties, not fields. You can change your fields to properties by adding { get; set; } to them like the other answer suggests
I think you need getters and setters. You also might need to implement INotifyPropertyChanged.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have a dataset that is essentially a list of objects with a boolean property in them that is bound to a DataGrid (DXGrid to be specific). I am trying to get the IsChecked property to populate on the clicking of the checkbox. In the case of a standalone textbox, i would use the UpdateSourceTrigger option of Binding, but in the DXGrid at least, that doesn't seem to be available. As it is, I have to lose focus of the checkbox in order to update the property.
Any Ideas?
Assume that the RaisePropertyChanged function below is an implementation of INotifyPropertyChanged.
Data Object
public class MyObject
{
bool _isChecked;
string _name;
int _id;
public MyObject(OtherObject oo)
{
_name = oo.Name;
_id = oo.ID;
}
public int ID
{ get { return _id; }}
public string Name
{ get { return _name; }}
public bool IsChecked
{
get { return _isChecked; }
set
{
if (value == _isChecked)
return;
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}
}
ViewModel
class MyTestViewModel : BaseViewModel
{
#region Fields
#endregion
public MyTestViewModel(Message message)
: base(message)
{
AvailableObjects = PopulateDataSet();
}
#region Properties
public List<MyObject> AvailableObjects { get; set; }
}
view XAML
<dxg:GridControl x:Name="SearchGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="1024" AutoPopulateColumns="True" DataSource="{Binding Path=AvailableObjects}" >
<dxg:GridControl.Columns>
<dxg:GridColumn Header="Select" Width="60" FixedWidth="True" FieldName="IsChecked" ImmediateUpdateColumnFilter="True"></dxg:GridColumn>
<dxg:GridColumn Header="ID Number" Width="130" FixedWidth="True" ReadOnly="True" FieldName="ID"></dxg:GridColumn>
<dxg:GridColumn Header="Name" FieldName="Name" ReadOnly="True"></dxg:GridColumn>
</dxg:GridControl.Columns>
<dxg:GridControl.View>
<dxg:TableView AllowEditing="True" x:Name="view" IndicatorWidth="0" AutoWidth="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
Give this a try:
<dxg:GridColumn>
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<dxe:CheckEdit Checked="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
Is it a special Checkbox (like a DXCheckbox or something) or just a normal WPF Checkbox. In the case of the latter, the binding would be IsChecked={Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}.
Do you get any binding errors in your output window?