I have a set of pages between which I navigate and between which I need to pass some parameters, namely some objects of the type of my Models. When I navigate to a page I Setup this new page's constructor with a parameter of the type of the object I need, so the class is implemented as follows:
public partial class ArtigoEdit : ContentPage
{
EditionsViewModel viewModel;
public ArtigoEdit(Models.Artigo artigo)
{
InitializeComponent();
BindingContext = viewModel = new EditionsViewModel();
this.viewModel.Artigo = artigo;
}
}
As you can see I'm also using the MVVM pattern and on its side I have a variable "Artigo" to which I want to assign the object my constructor receives:
Models.Artigo artigo;
public Models.Artigo Artigo
{
get { return artigo; }
set { artigo = value; this.Notify("Artigo"); }
}
The problem is that if I try to access the attributes of this object in order to change the way they are displayed and bind to these new variables instead the object is null when the page opens so the variables are not shown:
string tipoArtigo = "";
public string TipoArtigo
{
get { return tipoArtigo; }
set
{
if (Artigo != null)
{
if(Artigo.TipoArtigo == "P")
tipoArtigo = "Produto";
else if (Artigo.TipoArtigo == "S")
tipoArtigo = "Serviço";
else if (Artigo.TipoArtigo == "O")
tipoArtigo = "Outro";
else if (Artigo.TipoArtigo == "I")
tipoArtigo = "Imposto";
else
tipoArtigo = value;
}
this.Notify("TipoArtigo");
}
}
Is this not the correct way of doing this? If not, what is my alternative? Beware that my ViewModels all implemente a ViewModelBase class that itself implements INotifyPropertyChanged so do not worry about that! :)
Silly me, I'm sorry for disturbing and I thank to all the people who tried to help. What was happening was: I was binding these variables to an Entry, but for some reason it was not expanding to fit the size of information so although it was there it did not appear to be...luckily I've noticed it.
Related
I read several similar issues with MVVM, but just cannot find the right fix to this problem. I am sure it's really simple, but I cannot work out how to update an UI from two different classes.
I have one ModelView (Homepage.xaml) and MVVM binded label, which works fine from the Homepage.cs, but how can I update this label from from a different class (anotheractivty.cs). Is there a way to reference the string from from the second class file or do I need to somehow call and pass the string between the anotheractivty to the homepage class?
Many thanks
Please note the code is simplified:
Homepage.xaml
<Label
Text={"Binding Updatetext}"
/>
Homepage.xaml.cs
String updatetext = "";
public Homepage()
{
BindingContent = this;
}
Public string Updatetext
{
get=> updatetext
set
{
if (value == updatetext)
return;
updatetext = value;
OnPropertyChange(nameof(Updatetext));
}
}
Public updatetest()
{
Updatetext = "new text";
}
anotheractivty.cs
How to link this to the Homepage.cs?
Public updatetest()
{
var page = new Homepage;
Homepage.Updatetext = "new text";
}
I'm new to C#/WPF and I would like some clarification on whether I have the proper implementation of my ViewModel.
I have created a simple window with a search text box and list box for the results.
<TextBox Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ListBox ItemsSource="{Binding Results}" />
Then I have a ViewModel with the following code.
private List<string> lstStr;
public ViewModel()
{
lstStr = new List<string>();
lstStr.Add("Mike");
lstStr.Add("Jerry");
lstStr.Add("James");
lstStr.Add("Mikaela");
}
public List<string> LstStr
{
get
{
return lstStr;
}
set
{
if (lstStr != value)
{
lstStr = value;
OnPropertyChanged("LstStr");
}
}
}
private string searchText;
public string SearchText
{
get
{
return searchText;
}
set
{
if (searchText != value)
{
searchText = value;
OnPropertyChanged("SearchText");
UpdateResults();
}
}
}
private ObservableCollection<string> results = new ObservableCollection<string>();
public ObservableCollection<string> Results
{
get
{
return results;
}
set
{
if (results != value)
{
results = value;
OnPropertyChanged("Results");
}
}
}
public void UpdateResults()
{
int i = 0;
results.Clear();
while (i < LstStr.Count)
{
if (LstStr.ElementAt(i).ToString() != null)
{
if (searchText != null && searchText != "")
{
if (LstStr.ElementAt(i).Trim().Contains(searchText))
{
results.Add(LstStr.ElementAt(i));
Console.WriteLine(LstStr.ElementAt(i));
}
}
else
results.Clear();
}
else
Console.WriteLine("NULL");
i++;
}
}
I see myself writing logic in the Get or Set section of code in the ViewModel. Let's say I will have more text boxes and lists that will want to implement. Is this the correct way of coding my logic in the properties or am I completely missing the point? Please help me understand this. Thanks in advance.
No, this isn't exactly right.
First, logic normally goes in the model, not the view model. That said, you have a filter, which is basically UI logic, so its probably OK here.
Second, the filter will only change when you set the search text, so the logic would go in the setter, not the getter. I also wouldn't inline the whole thing, put it in its own function so you can reuse it later:
public String SearchText
{
...
set
{
serachText = value;
NotifyPropertyChanged();
UpdateResults();
}
}
public void UpdateResults()
{
...
}
The one thing to keep in mind (and there isn't really a good way around this) is that if that function takes a long time to run, your UI will really slow down while the user is typing. If the execution time is long, try shortening it, then consider doing it on a separate thread.
ViewModels should have only the responsibility of "converting" data into another form that the view can handle (think INotifyPropertyChanged, ObservableCollection, etc.)
The only time where you'd get away with the ViewModel having any of the logic is when the logic is encapsulated entirely in a collection. e.g. if you can get everything you need out of List<T>, then the ViewModel effectively has all the logic. If you need value beyond that, it should be outside of the ViewModel.
EDIT 2
I have got some help over the past few days on a problem that I am trying to work through. After receiving helpful support from several users, I have come across an error that I have been trying to fix over the weekend and still not succeeded.
I created a Dictionary, where I pass a string Country and also a ICollection of Places for that Country.
Dictionary<string, NewCountryClass> NTCD = new Dictionary<string, NewCountryClass>();
public void AddCountryCollection()
{
newCountryClass = new NewCountryClass(newCountry);
Collections.Add(newCountryClass);
NTCD.Add(newCountryClass.Country, newCountryClass);
}
public void AddPlace()
{
string Country = selectedItem.Country;
RenameQuestion(placeName);
NTCD[Country].Place.Add(placeName);
}
Here is my newCountryClass where I stored the Country and Places in that Country.
private ICollection<string> _places;
public ICollection<string> Places
{
get
{
return _places;
}
set
{
if (value == _places)
return;
_places = value;
RaisePropertyChanged(() => Places);
}
}
This is where the places could be added, but I create an instance of my class at the adding Country stage, and therefore can't pass a place at that time. (EDIT - I have moved the initialising of the collection into the constructor instead of in the Places property, as advised).
public NewCountryClass(string country)
{
_places = new ObservableCollection<string>();
if (country != null)
{
_country = country;
}
}
Therefore, I attempted to create a renamePlace() method:
public void RenamePlace(string place)
{
_places.Add(place);
}
However, _places still seems to be null even with this attempt. Any further ideas or anything I am doing wrong?
You need to learn how to debug a program. Basically, you try to instantiate your NewCountryClass here:
public void AddCountryCollection()
{ // <<< Put breakpoint here <<<
newCountryClass = new NewCountryClass(newCountry, placeName);
Collections.Add(newCountryClass);
NTCD.Add(newCountryClass.Country, newCountryClass);
}
If the placeName input parameter is null in the constructor, then it is also null here... you need to add a breakpoint here and find out why the value is null and ensure that it has a value by this stage in your program.
Would you not be better off initialising the _places collection in the constructor instead of the property get accessor?
public NewCountryClass(string country)
{
_places = new ObservableCollection<string>();
if (country != null)
{
_country = country;
}
}
I have a WPF application that includes ~50 controls that are bound to properties on my business object which implements INotifyPropertyChanged. Here's a quick snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// properties begin here
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
// constructor, etc. not shown
}
I also have several validation rules that are used to validate the user input in these controls. I'm using command binding to prevent my user from saving the data if there are any validation errors. My application also includes a "Reset default values" button which, obviously, will reset the default value for all of the properties on my business object. This all works exactly as I'd like it to with one exception. If my user enters invalid data into one or more controls and then clicks the "Reset default values" button, the controls that contain invalid data don't always update as I'd expect. This happens because of the following code in my property setters:
if (_name == value)
{
return;
}
This code exists to prevent unnecessary property changed notifications from occurring when the value entered by my user in the bound UI control is the same value that the property is already set to. As an example, I have an IntegerUpDown control in my UI (this control is part of the Extended WPF Toolkit from Xceed). The default value of the property that my control is bound to is 10. My user deletes the value from the control and my validation rule is triggered which results in a validation error and the UI is updated appropriately with an error adorner, etc. The value of the property that this control is mapped to hasn't been changed so it's still set to 10. Now my user clicks the "Reset default values" button which will result in the default value (10) for the property being reset. However, the value for the property is already set to 10 so the short circuit logic in my setter will return instead of setting the property value.
So now, after my user clicks "Reset default values", I am also forcing an update on my binding target like this:
this.myIntegerUpDown.GetBindingExpression(Xceed.Wpf.Toolkit.IntegerUpDown.ValueProperty).UpdateTarget();
This solves my problem but only for this particular control. Is there any easy way to do this for all of my bound controls without having to specify each one? Thanks.
OnPropertyChanged(new PropertyChangedEventArgs(string.Empty));
This is intended to imply that ALL properties on that object have changed.
Could you do one of the following?
1) Reset the DataContext - Either recreate it, or re-set the property
var context = this.DataContext;
this.DataContext = null;
this.DataContext = context;
2) Loop through all properties programmatically via reflection and manually call OnPropertyChanged with the relevant property names.
var properties = typeof(ViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(property.Name));
}
You've mentioned validation and reset values, and of course the obvious one is to persist it.
Why don't you implement IEditableObject Interface on your entity that has three signature methods. BeginEdit(), CancelEdit() and EndEdit()
That way you can easily roll back your entity to the whatever you want, or validate it and lastly persist it. A good example is found here
Sample code
public class Customer : IEditableObject
{
struct CustomerData
{
internal string id ;
internal string firstName ;
internal string lastName ;
}
private CustomersList parent;
private CustomerData custData;
private CustomerData backupData;
private bool inTxn = false;
// Implements IEditableObject
void IEditableObject.BeginEdit()
{
Console.WriteLine("Start BeginEdit");
if (!inTxn)
{
this.backupData = custData;
inTxn = true;
Console.WriteLine("BeginEdit - " + this.backupData.lastName);
}
Console.WriteLine("End BeginEdit");
}
void IEditableObject.CancelEdit()
{
Console.WriteLine("Start CancelEdit");
if (inTxn)
{
this.custData = backupData;
inTxn = false;
Console.WriteLine("CancelEdit - " + this.custData.lastName);
}
Console.WriteLine("End CancelEdit");
}
void IEditableObject.EndEdit()
{
Console.WriteLine("Start EndEdit" + this.custData.id + this.custData.lastName);
if (inTxn)
{
backupData = new CustomerData();
inTxn = false;
Console.WriteLine("Done EndEdit - " + this.custData.id + this.custData.lastName);
}
Console.WriteLine("End EndEdit");
}
public Customer(string ID) : base()
{
this.custData = new CustomerData();
this.custData.id = ID;
this.custData.firstName = "";
this.custData.lastName = "";
}
public string ID
{
get
{
return this.custData.id;
}
}
public string FirstName
{
get
{
return this.custData.firstName;
}
set
{
this.custData.firstName = value;
this.OnCustomerChanged();
}
}
public string LastName
{
get
{
return this.custData.lastName;
}
set
{
this.custData.lastName = value;
this.OnCustomerChanged();
}
}
internal CustomersList Parent
{
get
{
return parent;
}
set
{
parent = value ;
}
}
private void OnCustomerChanged()
{
if (!inTxn && Parent != null)
{
Parent.CustomerChanged(this);
}
}
public override string ToString()
{
StringWriter sb = new StringWriter();
sb.Write(this.FirstName);
sb.Write(" ");
sb.Write(this.LastName);
return sb.ToString();
}
}
Wouldn't it be easier to just always call OnPropertyChanged regardless of whether its the same? How much of a performance boost does that give you?
Is it correct to implement my caching object like this in my controller :
public class HomeController : BaseController
{
public static Cache cachingControl = new Cache();
...
And I Use it like this :
public ActionResult Home()
{
IndexViewData view = new IndexViewData();
view.UserId = UserId;
if (cachingControl.Get("viewHome") != null) {
view = (IndexViewData)cachingControl.Get("viewHome");
} else {
view.allAdsList = AllAds(5000, 0);
if (Request.QueryString["voirTous"] != null)
view.loadGeneral(true);
else
view.loadGeneral(false);
cachingControl.Insert("viewHome", view);
}
view.adsList = SetupSearch(5, false, 0);
return View(view);
}
But When I Call this line :
if (cachingControl.Get("viewHome") != null) {
They trow me the error
NullErrorException
But I know it can be null this is why i'm put this condition to
Do you have an alternative or a tips for me thank you!
P.S.: I Know that the code is weird :P but I must to support it ...
The System.Web.Caching.Cache object is available already for you by adding this to the controller:
this.HttpContext.Cache
That is the already in-built cache that's also available in web forms.