Determine if Textboxes Contain Values MVVM - c#

I have a WPF page which consists of multiple text boxes and a search button. When one presses the search button the program needs to enumerate all the controls to determine if at least one of the textboxes is filled. If all are empty error then an error will be shown. The mvvm-light pattern is being used.
How does one enumerate all the textboxes under MVVM

There's an easy way that you can achieve your goal.
You can create a ViewModel and create properties in this ViewModel, so you can bind these properties to your TextBox on XAML, doing so your properties on ViewModel will maintain the value of the textbox's, here's an example.
XAML
<TextBox Text="{Binding Name, Mode=TwoWay}"/>
<TextBox Text="{Binding Age, Mode=TwoWay}"/>
"Mode=TwoWay" means whenever the bind property change, the textBox will be updated automatically.
XAML Code behaind.
public MainWindow(){
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
You need set the DataContext in order to be able to bind the properties.
ViewModel
public class MainWindowViewModel{
private string _name;
public string Name{
get{
return _name;
}
set{
_name = value;
}
}
private int _age;
public int Age{
get{
return _age;
}
set{
_age = value;
}
}
}
That is it, everytime when you need to check if your TextBoxes are empty, you can do so checking if your properties on View Model are empty.

Follow this logic to enumerate the controls and inform the user of its status.
Bind each of the controls on the view to individual properties on the View Model.
Create a method on the ViewModel, maybe called AreAllBlank (?) to enumerate each of the properties checking whether they are blank or not. If they are all blank, return true, otherwise return false.
On the view, for the button press's code behind operation, call the method created in step 2. If the result shows that they controls do not have data call MessageBox.Show() with an appropriate message.

Related

Getting IDataErrorInfo validation to update in child UserControl when bound ViewModel OnPropertyChanged is called but the value is the same

I have a reusable UserControl defined that will be used multiple times within the parent form, to represent different instances of a configured object. This UserControl has several TextBoxes representing configurable properties. For one of these properties, the value must be unique across all instances of the reusable UserControl.
My parent form utilizes these usercontrols like this:
<namespace:ReusableControl
Property1="{Binding Path=ViewModelProperty1a, Mode=TwoWay}"
Property2="{Binding Path=ViewModelProperty2a, Mode=TwoWay}"
UniqueProperty="{Binding Path=VMUniquePropertya, Mode=TwoWay}"/>
<namespace:ReusableControl
Property1="{Binding Path=ViewModelProperty1b, Mode=TwoWay}"
Property2="{Binding Path=ViewModelProperty2b, Mode=TwoWay}"
UniqueProperty="{Binding Path=VMUniquePropertyb, Mode=TwoWay}"/>
And the UserControl property looks like this:
<TextBox
x:Name="UniquePropertyTextBox"
Text="{Binding Path=UniqueProperty,
RelativeSource={RelativeSource AncestorType=local:ReusableControl},
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
/>
The codebehind for the UserControl contains IDataErrorInfo validation:
public string this[string columnName]
{
get
{
string error = string.Empty;
switch (columnName)
{
case nameof(UniqueProperty):
if (!((MyViewModel)DataContext).UniquePropertiesAreUnique())
{
error = "not unique";
}
break;
//Other cases here, omitted from example
}
return error;
}
}
//-------------------------------
//Just to show the codebehind for the property:
public string UniqueProperty
{
get { return (string)GetValue(UniquePropertyDP); }
set { SetValue(UniquePropertyDP, value); }
}
public static readonly DependencyProperty UniquePropertyDP=
DependencyProperty.Register(
"UniqueProperty",
typeof(string),
typeof(ReusableControl),
new PropertyMetadata(string.Empty));
Everything appears to be wired up and bound correctly; the values update when the UI is changed as desired. If I change one of the unique property values such that it is no longer unique, I get the red border around that text box, but this is where the issue comes in - the red border only appears around the text box I just changed, not both of the instances of UniqueProperty. In the ViewModel, when either of the UniqueProperty values are changed, it triggers OnPropertyChanged for the other, but this still isn't causing the validation border to appear. If I replace OnPropertyChange with an explicit call to update the value i.e:
//In the setter for VMUniquePropertyb:
var temp = VMUniquePropertya;
VMUniquePropertya = null;
VMUniquePropertya = temp;
Then I do get the validation border to appear on both text boxes when that value is changed to match the other, and both borders disappear when either value is changed to be unique again. Of course, this is a hack, and also will cause an infinite loop if used on both properties. How can I accomplish the same result with OnPropertyChanged?
I found a solution that works for me. There may be better ways to do this, but this works fairly well.
By using the CoerceValueCallback on the DependencyProperty, we can execute code whenever the value of the property is re-evaluated, not just when it actually changes. This will fire when the PropertyChange event occurs in the ViewModel because the binding is re-evaluated. This looks like this:
public string UniqueProperty
{
get { return (string)GetValue(UniquePropertyDP); }
set { SetValue(UniquePropertyDP, value); }
}
public static readonly DependencyProperty UniquePropertyDP=
DependencyProperty.Register(
"UniqueProperty",
typeof(string),
typeof(ReusableControl),
new PropertyMetadata(string.Empty, null, UniquePropertyCoerceValueCallback));
private static object UniquePropertyCoerceValueCallback(DependencyObject d, object value)
{
((ReusableControl)d).UniquePropertyTextBox.GetBindingExpression(TextBox.TextProperty)
.UpdateTarget();
return value;
}
When the value of one of the unique properties changes and the ViewModel fires the PropertyChange event for the other unique property in the ViewModel, the DependencyProperty in the UserControl will be re-coerced, triggering this callback and updating validation.

Transferring Textbox data to my ViewModel

I have two textboxes with userinput, of which I need to transfer the data to my ViewModel. I tried looking around how to do this by binding it to a button (as the transfer is supposed to take place upon a buttonclick), but most advice to use bindings. However, to use bindings you have to declare properties in the ViewModel (afaik), but as these strings are used to create a new object, holding properties for them would be all but ideal because the two textboxes might expand to over 10 in the future. I've also tried messing around with CommandParameter but I only seem to be able to declare one.
So for clarification:
How do I transfer the contents of two (or more) textboxes to the corresponding ViewModel so I can create a new Object with them?
Edit:
In addition I'd also like to be able to reset the Text= field to be empty once the method handling the data has succesfully completed.
The View
<TextBox Name="UI1"/>
<TextBox Name="UI2"/>
<Button Source="*ImageSource*" Command="{Binding CallCreateObject}"/>
and the ModelView
private void OnCallCreateObject()
{
Object newObject = new Object(UI1, UI2, false)
}
This is a general example of what I'm trying to achieve
If you want to insert data from UI to ViewModel on Button Click than there is no reason to use binding. Binding is mainly used to sync data between UI and underlying models.
Still if you want only that then on button_click event you can do something like this.
private void button_Click(object sender, RoutedEventArgs e)
{
Model model = new Model();
model.Property1 = textBox1.Text;
model.Property2 = textBox2.Text;
textBox1.Text = string.Empty;
textBox2.Text = string.Empty;
}
That will solve your issue. But this approach is not recommended when you have a better thing that is called 'Binding'
If you want to bind your view with a viewmodel then try this:
Your view model:
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Great, you have set up your view model. Now the view:
XML PersonView.xml:
<Grid Name="MyContainer">
<TextBox Text="{Binding PersonName}" />
<Button Name="SaveInfoButton" OnClick="SaveInfoButton_Click">Save info</Button>
</Grid>
Now that we have indicated with which property the textbox will be bind, lets indicate to the view the model that will use to update the property named PersonName. The idea is that when you click over the button, the property PersonName of our model Person gets updated with the value of the TextBox.
The xml class:
public partial class PersonView : UserControl
{
private readonly Person Model;
public PersonView()
{
//Components initialization, etc. etc...
this.Model = new Person();
this.DataContext = this.Model; // Here we are binding the model with our view.
}
private void SaveInfoButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.Model.PersonName); // this will print the value of your textbox.
}
}
Dont know if you noticed, but we didnt have the need of creating a new object when the user click the button. We just use our model and update the model properpies. If you add more textbox to your view, you'll have to added to our viewmodel as well as the given example.
Here is some post that can help you a little bit more(dont have enough time)
http://blog.scottlogic.com/2012/04/20/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-two.html
http://www.tutorialspoint.com/wpf/wpf_data_binding.htm
You could use bindings like this:
<TextBox Name="UI1" Text="{Binding Path=Ut1Value}"/>
<TextBox Name="UI2" Text="{Binding Path=Ut2Value}"/>
<Button Source="*ImageSource*" Command="{Binding CreateTheThingCommand}"/>
Then in your viewmodel you'll need to have the properties and command for those:
private string _ut1Value;
private string _ut2Value;
public string Ut1Value
{
get
{
return _ut1Value;
}
set
{
if (_ut1Value!= value)
{
_ut1Value= value;
OnPropertyChanged("Ut1Value");
}
}
}
public string Ut2Value
{
get
{
return _ut2Value;
}
set
{
if (_ut2Value!= value)
{
_ut2Value= value;
OnPropertyChanged("Ut2Value");
}
}
}
public ICommand CreateTheThingCommand
{
get { return new RelayCommand(CreateTheThing); }
}
private void CreateTheThing()
{
Object newObject = new Object(_ut1Value, _ut2Value, false);
// Do whatever with your new object
}
It sounds as if you need at least two ViewModel objects:
One to present the data from an existing object. This would be, essentially, what you have already.
A container ViewModel. This encapsulates the behaviours of the IEnumerable collection of objects, including the functionality required to Add a new object.
The container ViewModel would have the properties that you are struggling with, plus the CreateObject command, along with an IEnumerable (ObservableCollection) property to hold the existing ViewModel objects.
In your View, you would have one control to present the data in an existing ViewModel object, and a second control with a ListView (or similar) control to display the existing view controls and the set of TextBox controls, plus the button to create a new object (and add it to the list).
This would also allow you to add 'remove', 'sort', etc. functionality to the container ViewModel, without having to change the existing ViewModel.
A way to accomplish a scalable solution with minimal lines of code, would be to create hold a list of items you bind to in the view model.
This way you can use an ItemsControl in the UI to display a textbox for each item:
public class ViewModel
{
public List<Item> Items {get;} = new List<Item>
{
new Item { Value = "UI1" },
new Item { Value = "UI2" },
};
public class Item
{
public string Value {get;set;}
}
}
View:
<ItemsControl ItemsSource="{Binding Test}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Commit" Margin="5" Click="ButtonBase_OnClick"/>
You can then create the object either from a click event or command:
private void OnCallCreateObject()
{
Object newObject = new Object(Items[0], Items[1], false);
}
The downside is that the order of the items is not explicit, so either you need to assume that the indexed order is correct, or order them manually.

WPF Bind once and never update?

I have a ListView and a GridView that lists users in an application by names. Whenever the user selects an user to edit, I add a new tab to a TabControl, and bind all editable properties to the WPF controls.
However, when the user is editing in the Edit Tab, the information in the List (specifically, the name field) is also being updated.
Currently I'm making a copy of the object to be edited and leaving the original so it doesn't update the ListView, but isn't there a better/easier way to do this?
I've tried setting the Binding Mode=OneWay, didn't work, and also UpdateSourceTrigger=Explicit in the GridView but also didn't work.
Is there any easier way to do this?
Edit: The way I implemented my INotifyPropertyChanged class is part of the issue, since I have this:
public partial class MyTabControl : UserControl
{
public MyTabControl()
{
InitializeComponent();
//Here, DataContext is a List<Users>
//Users being my Model from the Database
//Some of it's properties are bound to a GridView
//User doesn't implement INPC
}
public void OpenTab(object sender, RoutedEventArgs e)
{
User original = (sender as Button).DataContext as User;
// - This will create a new ViewModel below with the User I'm sending
MyTabControl.AddTab(original);
}
}
And my ViewModel of Users is:
public class UserViewModel : INotifyPropertyChanged
{
public User Original { get; private set; }
public string Name { get { return Original.Name; } set { Original.Name = value; OnPropertyChanged("Name"); } }
public UserViewModel(User original)
{
Original = original ?? new User();
}
// - INPC implementation
}
Since my ViewModel is the one reporting the property changes, I didn't expect my original User to report it as well to the GridView.
The Mode=OneWay causes the information flow to go from the bound data entity to the target UI property only, any change to the UI property will not be bound back.
The reason why the UI content is changing is because the underlying property is read/write (i.e. has a getter and a setter) and is notifying any value change (due to the implementation of the INPC interface).
Presuming that it is a list of User objects you've bound to the GridView, you have two simple options to fix this. Which one is best depends on how much scope for change you have:
change the current Name property on the User object, remove the setter for it. Replace the setter with a method to set the property (i.e. SetUserName(string name)) which then sets the private member variable. Or pass the name as an argument to the constructor of the User entity.
create a new property with only a getter which returns the name and set your binding to that; i.e. public string UserName { get { return Name; }}. As there is only a getter there will be no notification of this property, so if the name does change it won't be propagated via this new property.

Binding with custom object in MVVM textbox

I have started learning MVVM with some basic applications and I just encountered below issue with binding.
I have 2 textboxes in my View say- Student_name and Student_year. I have a Student class implemented in my viewmodel with its properties. But, the actual Student class is in my Model layer.
<TextBox x:Name="StuName"
Text="{Binding Path=MyStudent.Name, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="StuYear"
Text="{Binding Path=MyStudent.Year, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
ViewModel:
private Student _myStudent = new Student();
public Student MyStudent
{
get { return _myStudent ; }
set
{
if (value != _myStudent )
{
_myStudent = value;
OnPropertyChanged("MyStudent");
}
}
}
Model (Student Class):
public string Name
{
get { return _name; }
set
{
if (_name!= value)
{
_name= value;
OnPropertyChanged("Name");
}
}
}
I can see everything working fine on binding the values from VM to View. But, the other way is behaving little tricky here..
Wheneven I change Name/Year in the textbox, the control has to land on Viewmodel's Set property? Rather, it straight away goes to Model's Set property.
For Instance, When I modify txtbox 'StuName', SET method of Student class is invoked. But not SET method of Viewmodel(MyStudent object).
I am not sure why this behaves in such a way. Is it because I have directly bounded Student.Name to the textbox? What are the alternatives to handle this SET operation in Viewmodel class..
Thanks in advance.
PS: I have implemented INotifyPropertyChanged interface properly and rest other bindings(of primitive data type) are working fine with other controls.
As Philip Stuyck correctly pointed out in his answer, the ViewModel only has a setter for the Student instance, which never changes. So the setter on the ViewModel is never invoked. The binding goes to the name property of that instance.
A different approach would be to wrap the name property in you ViewModel explicitly. This allows for a clearer separation of concerns between Model and ViewModel. I.e. right now your Model implements INotifyPropertyChanged which IMO belongs into the ViewModel, because in general it is only used for triggering View updates. Your ViewModel would look like this:
class StudentViewModel
{
private Student _myStudent = new Student();
public string Name
{
get { return _myStudent.Name ; }
set
{
if (value != _myStudent.Name )
{
_myStudent.Name = value;
OnPropertyChanged("Name");
}
}
}
}
Your Model on the other hand becomes simpler, because it doesn't have to implement INotifyPropertyChanged anymore.
That is normal behavior because your binding is to MyStudent.Name.
So the Mystudent setter is never called because the instance never changes.
The setter of the name is called because in fact that is where your binding is going to.

Problems WPF binding through a contained object

I'm trying to use WPF binding to an object that is contained by the DataContext object and it is not working.
If I place all binding elements in the DataContext ViewModel object, everything works, but if I separate out the data list and data elements into a separate class that is contained by the ViewModel class, the ListBox data will work, but the binding to the individual data elements is not working.
With my experimentation, I assume that the bound object needs to be directly bound to the DataContext ViewModel class. I can do that and it works, but it's not as object oriented or reusable as I would like it to be. I've separated out the data list for the ListBox into it's own class and I'm assuming that since it is an ObservableCollection it works regardless of it being attached to the contained object. Since the individual data elements of the objects are only notified through OnPropertyChanged, no matter what I've tried, I can't get the WPF form to recognize the data, even though the DataContext.CurrentRecord shows the correct data. I can only assume that the OnPropertyChanged notification are not going where I need them to go.
Partial Code for DataContext ViewModel is as follows:
public ObservableCollection<ItemModel> Items
{
get { return ItemListMgr.Items; }
set { ItemListMgr.Items = value; }
}
public ItemModel CurrentItem
{
get { return ItemListMgr.CurrentItem; }
set { ItemListMgr.CurrentItem = value; }
}
Corresponding code in Contained ItemListMgr object is as follows:
public readonly ItemListModel _ItemList;
public ObservableCollection<ItemModel> Items
{
get { return _ItemList.Items; }
set
{
_ItemList.Items = value;
OnPropertyChanged("Items");
}
}
private ItemModel _currentItem;
public ItemModel CurrentItem
{
get { return _currentItem; }
set
{
_currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
The "Items" object is the list that gets displayed and even using the contained object "ItemListMgr", this part still works great. As the user scrolls through the list CurrentItem is set to the active item in "Items", but even though the WPF data entry elements are bound to the CurrentItem elements, they will not change when I scroll through the list.
The Xaml code to bind a text box is as follows:
Text="{Binding CurrentItem.ItemName, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}"
The Xaml code binds to the DataContext object, which is using a simple pass through to the contained ItemListMgr class. I've tried to add the OnPropertyChange to the pass through elements, but that didn't help and I would guess only added to the confusion.
As this is a large amount of code and fairly complex, I hope that I've given enough key elements for you to understand what I'm doing. Hopefully there is a way to allow this type of binding as it will greatly enhance what I'm doing.
Whatever help you can give will be greatly appreciated,
Paul

Categories