How do I use a combination of booleans values, to set the enabled property on a MvxBind button?
For example:
Using one boolean value, the binding is achieved with:
<Button
android:text="Next"
local:MvxBind="Enabled IHaveDoneEverything"/>
But how do I implement this using multiple boolean values?
Things I've tried that didn't work:
Using an OR statement in axml. local:MvxBind="Enabled (IHaveDoneThis | IHaveDoneThat)"
Using an extra property in my ViewModel. This didn't work due to the property not being 'set' and thus not being updated in the view.
public bool IHaveDoneAtleastSomething
{
get { return (IHaveDoneThis | IHaveDoneThat); }
}
Using a custom valueconverter.
local:MvxBind="Enabled [IHaveDoneThis , IHaveDoneThat], Converter=MultipleBooleansToOneBooleanUsingORValueConverter"/>
Using || instead of | actually resolved this issue.
local:MvxBind="Enabled (IHaveDoneThis || IHaveDoneThat)"
For future reference, in order to get the extra ViewModel property working you would have to make sure that the ViewModel calls the RaisePropertyChanged method for the extra property whenever IHaveDoneThis or IHaveDoneThat changed otherwise the binding won't trigger.
What I usually do is something like:
private bool _internalIHaveDoneThis;
public bool IHaveDoneThis
{
get{return _internalIHaveDoneThis;}
set
{
if(_internalIHaveDoneThis != value)
{
_internalIHaveDoneThis = value;
RaisePropertyChanged(() => IHaveDoneThis);
RaisePropertyChanged(() => IHaveDoneAtleastSomething);
}
}
}
private bool _internalIHaveDoneThat;
public bool IHaveDoneThat
{
get{return _internalIHaveDoneThat;}
set
{
if(_internalIHaveDoneThat != value)
{
_internalIHaveDoneThat = value;
RaisePropertyChanged(() => IHaveDoneThat);
RaisePropertyChanged(() => IHaveDoneAtleastSomething);
}
}
}
public bool IHaveDoneAtleastSomething
{
get { return (IHaveDoneThis | IHaveDoneThat); }
}
Related
Is there differences (performance, behavior) between these ways of properties binding?
private string _texto;
public string Texto
{
get => _texto;
set
{
Set(ref _texto, value);
NomeHome = _texto;
}
}
public string Texto
{
get => _texto;
set
{
_texto = value;
NotifyOfPropertyChange(() => Texto);
}
}
I doubt there is a difference in performance but it could be argued using the Set method is using code that has been tested to perform correctly and therefore less prone to bugs. At some point you may well forget to put the NotifyOfPropertyChange call into a property.
I'm getting stuck into MVVM in WPF and I have setup an ObservableCollection and an ICollectionView. The ICollectionView is set as the ItemsSource of a DataGrid, and the model is a type of Job.
I've setup getters and setter for both of the collections however when I am setting a Filter on the ICollectionView instead of the Job being filtered by the SearchString they're just replicated over and over again, leading me to believe that they way I have the collections setup is totally wrong.
Here is how the two collections are get/set:
public ObservableCollection<Job> AllJobs
{
get
{
foreach (var job in _allJobsList)
_allJobs.Add(job);
return _allJobs;
}
set
{
if (_allJobs == value) return;
OnPropertyChanged("AllJobs");
}
}
public ICollectionView AllJobsView
{
get
{
_allJobsView = CollectionViewSource.GetDefaultView(AllJobs);
return _allJobsView;
}
set
{
if (_allJobsView == value)
{
return;
}
_allJobsView = value;
OnPropertyChanged("AllJobsView");
}
}
Now I have a stringcalled SearchString that is bound to a TextBox.Text. When the text changes I do the following:
public string SearchString
{
get => _searchString;
set
{
if (_searchString == value) return;
_searchString = value;
FilterJobs();
OnPropertyChanged("SearchString");
}
}
private void FilterJobs()
{
AllJobsView.Filter = x =>
{
var viewJob = x as Job;
return viewJob != null && viewJob.Number.Contains(_searchString);
};
}
Now when the page first loads, there are the correct Jobs loaded into the DataGrid. However, as soon as the user types the Jobs are duplicated if the Job.Number does contain the SearchString. How am I able to setup the collections so that I can appropriately set a filter?
The problem is in the getter of your ObservableCollection. Every time you "get" the collection, you are adding every item to the collection all over again.
Your code:
get
{
foreach (var job in _allJobsList)
_allJobs.Add(job);
return _allJobs;
}
Instead, it should be:
get
{
return _allJobs;
}
The setter of your ObservableCollection is also missing the "setter" (private field = value) code:
set
{
if (value != _allJobs)
{
_allJobs = value;
OnPropertyChanged("AllJobs");
}
}
Your Property AllJobs would then be:
private ObservableCollection<Job> _allJobs;
public ObservableCollection<Job> AllJobs
{
get
{
return _allJobs;
}
set
{
if (value != _allJobs)
{
_allJobs = value;
OnPropertyChanged("AllJobs");
}
}
}
The initialization of your collection should be someplace else (and not in the getter of your property), like in the constructor of the ViewModel or/and in a method that a command calls after the user asks for a refresh of the collection.
For example, if your VieModel is called MyViewModel and your List<Job> is called _allJobsList, you can initialize your collection like so:
public MyViewModel()
{
//fill the _allJobsList first, getting from a database for example: _allJobsList = GetJobs();
//and then create an observable collection from that list
AllJobs = new ObservableCollection<Job>(_allJobsList);
}
I'm building a windows8.1 app and trying to tamper with the ConventionManager of caliburn.micro . I want to enable name convention binding with ValueConverters.
More specifically I have a collection of ProductViewModels that have two properties Value and IsValid. I want to bind the Value property to a textblock and the IsValid property to the BorderColorBrush of a Border via a ValueConverter.
Here's my code inside Configure in app.xaml.cs
var oldApplyConverterFunc = ConventionManager.ApplyValueConverter;
ConventionManager.ApplyValueConverter = (binding, bindableProperty, property) =>
{
if (bindableProperty == Control.BorderBrushProperty && typeof(bool).IsAssignableFrom(property.PropertyType))
// ^^^^^^^ ^^^^^^
// Property in XAML Property in view-model
binding.Converter = booleanToBrush;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// My converter used here. Code never reaches this point
// else I use the default converter
else if (bindableProperty == TextBlock.TextProperty && typeof(int).IsAssignableFrom(property.PropertyType))
{
//Code reaches this point when it should.
oldApplyConverterFunc(binding, bindableProperty, property);
}
else
{
oldApplyConverterFunc(binding, bindableProperty, property);
}
};
The problem is that the program never enters the first if clause. Does anybody know why???
My VM and xaml are these.
public class ProductViewModel
{
public ProductViewModel(int value)
{
_value = value;
}
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
}
}
private bool _isValid;
public bool IsValid
{
get
{
_isValid = some validation logic;
return _isValid;
}
}
}
the view is a user control
<Border x:Name="IsValid">
<TextBlock x:Name="Value"/>
</Border>
I'm building a c# class that works with two different data sources. It will load a data source and take a configuration set from a function. Then I want to do several tasks on all properties within the object.
for example.
public String StreetAddress
{
get { return _streetAddress; }
set
{
if (value.Length <= 64)
_streetAddress = value;
else
_streetAddress = value.Substring(0, 1024).Trim();
}
}
public String City
{
get { return _city; }
set
{
if (value.Length <= 128)
_city = value;
else
_city = value.Substring(0, 128).Trim();
}
}
public String State
{
get { return _state; }
set
{
if (value.Length <= 128)
_state = value;
else
_state = value.Substring(0, 128).Trim();
}
}
So that holds the data from one side. I was hoping to be able to store and set a change flag on each property. So if we take State for example. If the person is moved from Texas to Illinois I want to set a bool within that property to note the change then be able to loop over all changes before saving the object to the DB. But I don't see any way to assign another state variable within that property. Is the best way to write another object on top of this to control it or is there another more creative way to store multiple strings within the one property?
If you'd like an OOP way of doing the thing, you can:
Define an interface and a class for holding your property, such as:
interface IPropertySlot
{
bool IsDirty { get; }
void ResetIsDirty();
object UntypedValue { get; }
}
class PropertySlot<T>:IPropertySlot
{
public T Value { get; private set; }
public bool SetValue(T value)
{
if (!Equals(_value, Value))
{
Value = value;
IsDirty = true;
return true;
}
return false;
}
public bool IsDirty { get; private set; }
public void ResetIsDirty()
{
IsDirty = false;
}
public object UntypedValue
{
get { return Value; }
}
}
Store your properties inside your class in a dictionary from String (for name of property) to IPropertySlot and get/set them through a pair of methods:
void SetProperty<T>(string name, T value)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
((PropertySlot<T>)property) .SetValue(value);
}
T GetProperty<T>(string name)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
return ((PropertySlot<T>)property).Value;
}
Finding the changed properties later is just a matter of going over the _properties.Values and finding which of them are IsDirty.
This approach also gives you a way to add more functionality to your properties in an OO manner (such as raising PropertyChanged/PropertyChanging events, mapping it to DB fields, etc.).
In such a situation I'd prefer an approach external to the Dto implementation.
Implement some unit that would take two instances of a class, and determine all the differences.
Map each property to compare:
static PropertyManager<Dto> manager = new PropertyManager<Dto>()
.Map(x => x.City)
.Map(x => x.StreetAddress);
Use two instances to compute difference:
var a = new Dto{ StreetAddress = "Foo", City = "Bar" };
var b = new Dto{ StreetAddress = "Foo", City = "Baz" };
var differences = manager.ComputeDifferences(a,b).ToList();
if( differences.Any() )
{
Console.WriteLine("Instances differ");
}
foreach (var diff in differences)
{
Console.WriteLine(diff);
}
This sample code prints out:
Instances differ
x.City
Here is a complete code example:
https://dotnetfiddle.net/4sNeoN
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?