CommunityToolkit: is it possible to have a readonly property? - c#

I am trying the CommmunityToolkit MVVM.
I would like to have a read only property so it wouldn't be changed in the view because of the binding. I want to ensure only the view model can change it and to notify to the view.
Until now, I was using this way:
private int myVar;
public int MyProperty
{
get { return myVar; }
private set { myVar = value; }
}
But I don't know if it is possible with the toolkit, because I have tried this:
readonly int MyProperty
But I get an error because it is readonly.
Is there some way to declare a readonly property with the toolkit?
Thanks.

Related

Binding doesn't work - No property, bindable property, or event found error

Am having an issue with binding, but I first searched with several questions on this, but no luck, below is the error am getting :
Error: Position 18:36. No property, bindable property, or event found
for 'Lat', or mismatching type between value and property
Below is my xaml file :
<controls:MapView x:Name="map" VerticalOptions="FillAndExpand">
<controls:MapView.Center>
<controls:Position Lat="{Binding latitude}" Long="{Binding longitude}" />
</controls:MapView.Center>
</controls:MapView>
Then the c# code is as below :
public partial class DisplayMap : ContentPage
{
private double latitude { get; }
private double longitude { get; }
public DisplayMap()
{
InitializeComponent();
this.latitude = 0.3476;
this.longitude = 32.5825;
BindingContext = this;
}
What am I missing ?
The issue seems to be a lack of publicly-accessible bindable properties in the Position class (notice that the error mentions Lat which is a member of Position). Position should look something like this:
public class Position : BindableObject
{
public static readonly BindableProperty LatProperty = BindableProperty.Create(nameof(Lat), typeof(double), typeof(Position), 0);
public double Lat
{
get { return (double)this.GetValue(LatProperty); }
set { this.SetValue(LatProperty, value); }
}
public static readonly BindableProperty LongProperty = BindableProperty.Create(nameof(Long), typeof(double), typeof(Position), 0);
public double Long
{
get { return (double)this.GetValue(LongProperty); }
set { this.SetValue(LongProperty, value); }
}
// ...
I suggest you take a look at the official documentation for Bindable Properties. Essentially, the error message you are getting is because it is looking for LatProperty when trying to bind using the accessor Lat.
The reason why you are not able to use the Lat and Long property is that if you check the Position class there is no Bindable Property defined for them in it which means you cannot access them in XAML,
A possible solution is to download the Sample project and take the code and make the respective changes for it to have Bindable Properties.
For that, you can check #Aaron's answer

Allow only OneWayToSource binding mode

I have EntitiesUserControl responsible for EntitiesCount dependency property:
public static readonly DependencyProperty EntitiesCountProperty = DependencyProperty.Register(
nameof(EntitiesCount),
typeof(int),
typeof(EntitiesUserControl),
new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public int EntitiesCount
{
get { return (int)this.GetValue(EntitiesCountProperty); }
set { this.SetValue(EntitiesCountProperty, value); }
}
Another (primary) control include EntitiesUserControl and read it property through binding:
<controls:EntitiesUserControl EntitiesCount="{Binding CountOfEntities, Mode=OneWayToSource}" />
CountOfEntities property in view model just store and process changing of count value:
private int countOfEntities;
public int CountOfEntities
{
protected get { return this.countOfEntities; }
set
{
this.countOfEntities = value;
// Custom logic with new value...
}
}
I need EntitiesCount property of EntitiesUserControl to be read-only (primary control must not change it, just read) and it works this way only because Mode=OneWayToSource declared explicitly. But if declare TwoWay mode or don't explicitly declare mode, then EntitiesCount could be rewritten from outside (at least right after binding initialization, because it happens after default dependency property value assigned).
I can't do 'legal' read-only dependency property due to binding limitations (best described in this answer), so I need to prevent bindings with mode other than OneWayToSource. It would be best to have some OnlyOneWayToSource flag like BindsTwoWayByDefault value in FrameworkPropertyMetadataOptions enumeration...
Any suggestions how to achieve this?
It's a „bit” hacky, but you can create a Binding-derived class and use that instead of Binding:
[MarkupExtensionReturnType(typeof(OneWayToSourceBinding))]
public class OneWayToSourceBinding : Binding
{
public OneWayToSourceBinding()
{
Mode = BindingMode.OneWayToSource;
}
public OneWayToSourceBinding(string path) : base(path)
{
Mode = BindingMode.OneWayToSource;
}
public new BindingMode Mode
{
get { return BindingMode.OneWayToSource; }
set
{
if (value == BindingMode.OneWayToSource)
{
base.Mode = value;
}
}
}
}
In XAML:
<controls:EntitiesUserControl EntitiesCount="{local:OneWayToSourceBinding CountOfEntities}" />
The namespace mapping local might be something else for you.
This OneWayToSourceBinding sets the Mode to OneWayToSource and prevents setting it to anything else.

Binding from ViewModel to behaviour

I have a property on my ViewModel that I need bound to a BindableProperty in my behaviour but I cant seem to get it to bind.
private int _test;
public int Test {
get {
return _test;
}
set {
if (SetProperty (ref _test, value)) {
_profileIsDirty = true;
NotifyPropertyChanged ("AllowUpdate");
}
}
}
Here is the property in the behaviour
public static readonly BindableProperty MinLengthProperty = BindableProperty.Create("MinLength", typeof(int), typeof(MinLengthValidator), 0);
public int MinLength
{
get { return (int)GetValue(MinLengthProperty); }
set { SetValue(MinLengthProperty, value); }
}
This is the property on the ViewModel and this is how I am trying to bind to it in XAML
<behave:TelephoneNumberValidatorBehaviour x:Name="phoneValidator" MinLength="{Binding Test, Mode=OneWay}"/>
But it never binds. Am I doing something wrong here?
Have you tried to change the value of your NotifyPropertyChanged to "Test" instead of "AllowUpdate"? Otherwise the UI is never notified that the value has changed of Test. Instead it raises that the value the AllowUpdate property has changed, which does not exist in your ViewModel.
The third parameter of BindableProperty.Create should be type of the class where the BindableProperty is defined. Based on your sample, I guess it should be typeof(TelephoneNumberValidatorBehaviour) and not typeof(MinLengthValidator)

Tackling properties in the programming

I am asking a beginner level question. Though I am working in MVC but I am really confused with a simple concept and that is "Properties". There are lot of questions that
I have already gone through but there is surely a doubt in mind and did'nt able to clear it up.
Actually c# properties used for getting and setting the value to the private fields.
Like
Public class MyClass
{
private int number;
public int Number{
get{ return this.number;}
set{ number=Value }
}
}
class Program
{
static void Main()
{
MyClass example = new MyClass();
example.Number = 5; // set { }
Console.WriteLine(example.Number); // get { }
}
}
Now , the value is assigned to property also and to the variable also. Right?
Now , here is my doubt::
When we create property in model for MVc structure, we only have
public int Number{get;set;}
If this is okay to work with then why we are creating unnecessorily one more field of private access specifier. If encapsulation is the reason for that or hiding the data then why not in model in MVC?
Actually, in the above class example can I only use
Console.WriteLine(example.number);
after declaring it public?
Then what's the use of creating property over here?
Properties can be used to a store and retrieve values from a backing field (number in your case) directly as in your first sample. But property getters and setters are ordinary blocks of code that you can use as you want. So you don't have to assign a backing field, but can derive the value of a property also from another property in a getter, e.g.
public int NumberTimesTwo
{
get
{
return Number * 2;
}
}
However, as a common scenario is to have a property retrieve and assign the value of a backing field, there is a shortcut that you can use:
public int Number { get; set; }
In this case, the compiler automatically creates a private backing field that the property retrieves in the getter and assigns in the setter, so the code is equivalent to the following, but less to type:
private int _number;
public into Number
{
get
{
return _number;
}
set
{
_number = value;
}
}
As the backing field is also private, you cannot access it from outside of the class directly.
private int myVar;
public int MyProperty
{
get { return myVar; }
set { myVar = value; }
}
You are implementing Encapsulation by using MyProperty, which is public to access myVar which is private and is accessible only in the block where defined, that is, your class and not outside it.
Btw, in what way does this QA not answer your question? Try going through this for further reference.

DependencyProperty default value depends on coercion logic

I have this simple example in the ViewModel of a WPF application:
class VM_DiskPartition : DependencyObject
{
// (...) Other properties
public bool IsLowOnSpace
{
get { return (bool)GetValue(IsLowOnSpaceProperty); }
set { SetValue(IsLowOnSpaceProperty, value); }
}
public static readonly DependencyProperty IsLowOnSpaceProperty = DependencyProperty.Register("IsLowOnSpace", typeof(bool), typeof(VM_DiskPartition), new PropertyMetadata(false, OnLowOnSpaceChanged));
private static void OnLowOnSpaceChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
((VM_DiskPartition)d).CoerceValue(BgColorProperty);
}
public Brush BgColor
{
get { return (Brush)GetValue(BgColorProperty); }
set { SetValue(BgColorProperty, value); }
}
public static readonly DependencyProperty BgColorProperty = DependencyProperty.Register("BgColor", typeof(Brush), typeof(VM_DiskPartition), new PropertyMetadata(Brushes.Red, null, Coerce_BgColor));
private static object Coerce_BgColor(DependencyObject d, object baseValue)
{
return UIUtils.GetBgColor(((VM_DiskPartition)d).IsLowOnSpace);
}
}
I want the BgColor property to have its default value automatically set by its coercion function.
Is there a more elegant way to achieve this instead of calling CoerceValue(BgColorProperty) from the constructor?
The reason is that I may have many properties like this in the future and it doesn't look very clean to use a lot of CoerceValue() calls in the constructor.
Maybe it's better to use Converters in this scenario? I was trying to go without them and create new ViewModel properties instead.
You seem to be somewhat confused... the DependencyObject and DependencyProperty classes are UI classes. They don't belong in a view model. In view models, we use normal CLR properties and the INotifyPropertyChanged interface to handle property change notification. Therefore, there's no need to use them in a view model at all.
If you want to set a default value in a view model, you simply do this:
private int number = 5; // <-- default value
public int Number
{
get { return number; }
set { number = value; NotifyPropertyChanged("Number"); }
}
If you want property value coercion in a view model, you just do this:
public int Number
{
get { return number; }
set { number = Math.Max(0, value); NotifyPropertyChanged("Number"); }
}
UPDATE >>>
Looking again at your code, it occurs to me that it shouldn't be in a view model at all. It looks like it should be in the code behind of some UserControl. We put data in view models, not UI elements like Brushes. If you want to set a default value for a DependencyProperty, the correct way to do it is how you have shown us:
public static readonly DependencyProperty BgColorProperty =
DependencyProperty.Register("BgColor", typeof(Brush), typeof(VM_DiskPartition),
new PropertyMetadata(Brushes.Red/* <-- default value */, null, Coerce_BgColor));
Property coercion is for ensuring that a value stays within certain bounds like the example I gave above that ensures that the value will never be negative.

Categories