well, i'm trying to make a custom view which is like user thumbnail view.
the view(ViewCell?) displays UserModel(my custom model)'s thumbnail + username.
this is the code in UploaderCell.xaml.cs
public static readonly BindableProperty UploaderProperty =
BindableProperty.Create(
propertyName: "Uploader",
returnType: typeof(UserModel),
declaringType: typeof(UploaderCell)
);
public UserModel Uploader
{
get
{ return GetValue(UploaderProperty) as UserModel; }
set
{ SetValue(UploaderProperty, value); }
}
and this is used like <UploaderCell Uploader="{Binding post.uploader}"/>
in some where else.
and the error i get is System.InvalidCastException Class
it's not my mistake about actually passing the wrong data.
so my question is, is it impossible to make BindableProperties with non basic types(string, int ,ect..)?
Yes, it is possible to use complex types and is something I do all the time. What you have posted of your code looks correct.
I would check the code in your view model to see what you are actually binding to that value as that might be the cause of your error. If you are binding the wrong type then that might lead to the error.
Make sure your data model has
{ get; set; }
this..
ah..
Related
The inital problem is coming from a personal project about the polyline of the Xamarin.Forms.Map where the initialization is realized by a binding from the XAML part..
Let me be clear by an example :
I have an object CustomMap.cs which inherit from Xamarin.Forms.Map (This file is in the PCL part -> CustomControl/CustomMap.cs)
public class CustomMap : Map, INotifyPropertyChanged
{
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(List<string>), typeof(CustomMap), null);
public List<string> PolylineAddressPoints
{
get { return (List<string>)GetValue(PolylineAddressPointsProperty); }
set
{
SetValue(PolylineAddressPointsProperty, value);
this.GeneratePolylineCoordinatesInner();
}
}
// ...
}
As you can see, I have a bindable property with an assessor and the XAML doesn't seem to use this assessor..
So the MainPge.xaml part of the page, where the control is called, looks like that:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:control="clr-namespace:MapPolylineProject.CustomControl;assembly=MapPolylineProject"
x:Class="MapPolylineProject.Page.MainPage">
<ContentPage.Content>
<control:CustomMap x:Name="MapTest" PolylineAddressPoints="{Binding AddressPointList}"
VerticalOptions="Fill" HorizontalOptions="Fill"/>
</ContentPage.Content>
</ContentPage>
The MainPge.xaml.cs part:
public partial class MainPage : ContentPage
{
public List<string> AddressPointList { get; set; }
public MainPage()
{
base.BindingContext = this;
AddressPointList = new List<string>()
{
"72230 Ruaudin, France",
"72100 Le Mans, France",
"77500 Chelles, France"
};
InitializeComponent();
//MapTest.PolylineAddressPoints = AddressPointList;
}
}
So, everything is fine if I edit the PolylineAddressPoints from the object instance (if the commented part isnt' commented..), but if I init the value from the XAML (from the InitializeComponent();), it doesn't work, the SetValue, in the Set {}, of the CustomMap.PolylineAddressPoints, isn't called..
I then searched on the web about it and get something about the Dependency Properties? or something like that. So I tried some solutions but, from WPF, so some methods, such as DependencyProperty.Register();. So yeah, I can't find the way to solve my problem..
I also though about something, if DependencyProperty.Register(); would exists in Xamarin.Forms, then it means I would have to do it for each values? Because, if every value has to be set by a XAML binding logic, it would not work, I would have to register every value, doesn't it?
I'm sorry if I'm not clear, but I'm so lost about this problem.. Please, do not hesitate to ask for more details, thank in advance !
Finaly, the initial problem is that I'm trying to set a value of an object/control, from the XAML. Doing this by a Binding doesn't work, it seems like it ignored.. However, it does work if I do the following:
MapTest.PolylineAddressPoints = AddressPointList;
There are multiple questions in this:
Why is the property setter never called when using Xaml ?
Am I properly defining my BindableProperty ?
Why is my binding failing ?
Let me answer them in a different order.
Am I properly defining my BindableProperty ?
The BindableProperty declaration is right, but could be improved by using an IList<string>:
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(IList<string>), typeof(CustomMap), null);
but the property accessor is wrong, and should only contains this:
public IList<string> PolylineAddressPoints
{
get { return (IList<string>)GetValue(PolylineAddressPointsProperty); }
set { SetValue(PolylineAddressPointsProperty, value); }
}
I'll tell you why while answering the next question. But you want to invoke a method when the property has changed. In order to do that, you have to reference a propertyChanged delegate to CreateBindableProperty, like this:
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(IList<string>), typeof(CustomMap), null,
propertyChanged: OnPolyLineAddressPointsPropertyChanged);
And you have to declare that method too:
static void OnPolyLineAddressPointsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
((CustomMap)bindable).OnPolyLineAddressPointsPropertyChanged((IList<string>)oldValue, (IList<string>)newValue);
}
void OnPolyLineAddressPointsPropertyChanged(IList<string> oldValue, IList<string> newValue)
{
GeneratePolylineCoordinatesInner();
}
Why is the property setter never called when using Xaml ?
The property, and the property accessors, are only meant to be invoked when accessing the property by code. C# code.
When setting a property with a BindablePrperty backing store from Xaml, the property accessors are bypassed and SetValue() is used directly.
When defining a Binding, both from code or from Xaml, property accessors are again bypassed and SetValue() is used when the property needs to be modified. And when SetValue() is invoked, the propertyChanged delegate is executed after the property has changed (to be complete here, propertyChanging is invoked before the property change).
You might wonder why bother defining the property if the bindable property is only used by xaml, or used in the context of Binding. Well, I said the property accessors weren't invoked, but they are used in the context of Xaml and XamlC:
a [TypeConverter] attribute can be defined on the property, and will be used
with XamlC on, the property signature can be used to infer, at compile time, the Type of the BindableProperty.
So it's a good habit to always declare property accessors for public BindableProperties. ALWAYS.
Why is my binding failing ?
As you're using CustomMap as both View and ViewModel (I won't tell the Mvvm Police), doing this in your constructor should be enough:
BindingContext = this; //no need to prefix it with base.
As you're doing it already, your Binding should work once you've modified the BindableProperty declaration in the way I explained earlier.
I created custom controls in Xamarin. One of it is ValidatedEntryCell:
public class ValidatedEntryCell: EntryCell
{
public static readonly BindableProperty ShowBorderProperty = BindableProperty.Create(nameof(ShowBorder), typeof(bool), typeof(ValidatedEntryCell), false);
public bool ShowBorder
{
get { return (bool)GetValue(ShowBorderProperty); }
set
{
SetValue(ShowBorderProperty, value);
OnPropertyChanged();
}
}
}
I want to change that ShowBorder property in case if Text is empty. I tried several cases how to invoke that property:
Invoking that property straight forward in xaml. Smth like: renderers:ValidatedPicker x:Name="Gender" Title="Gender" ShowBorder="True"
That case works fine.
<renderers:ValidatedEntryCell ShowBorder="{Binding FirstNameIsNotValid}" x:Name="FirstName"... (I setup FirstNameIsNotValid = true in codebehind)
I used converters <renderers:ValidatedEntryCell x:Name="LastName" ShowBorder="{Binding Source={x:Reference LastName}, Converter={StaticResource EmptyTextToBoolConverter}, Path=Text.Length}" that always return true
Tried to use style triggers for property.
Unfortunately anything helps here. Will be glad for any advice
Edit: partially helps that link. For now I can setup property in constructor in codebehind and changes made will be visible.
MVC 4 does present me some strange behaviour at the moment.
Imagine the following Code:
TestController.cs
public class TestController : Controller
{
public ActionResult Index(Function function, string action)
{
return View();
}
public class Function
{
public string Action { get; set; }
}
}
It seems, that when I call the URL directly through the browser (localhost:PORT/Test), the Action-Property gets automatically filled with "Index".
If the Action would be named "MySuperDuperActionWhichGetsInvokedQuiteOften", exactly this Methodname would be in the property.
Can somebody explain what MVC is doing here?
The Problem is, that I of course want to fill that stuff myself, for example through an AJAX-Query. But if MVC is filling in this property all by itself, this breaks some behaviour.
I could, of course, just rename my property and it would be working, but it would still be quite interesting what's going on.
EDIT
I would understand it that my second parameter, string action, get's filled with the method-name. But why on earth would MVC bind any property/parameter that is named the same to the request-value of it?
It is problem with default model binder. It "maps" request fields to properties in your class. There is an article of MSDN describing how does it works but to simply this situation the code will be like this:
Action = Request["action"] //where of course Request["action"] equals to name of your action
I have a control with a inner TextBox. I want to make a direct relationship between the Text property of the UserControl and the Text property of the TextBox. The first thing I realized is that Text was not being displayed in the Properties of the UserControl. Then I added the Browsable(true) attribute.
[Browsable(true)]
public override string Text
{
get
{
return m_textBox.Text;
}
set
{
m_textBox.Text = value;
}
}
Now, the text will be shown for a while, but then is deleted. This is because the information is not written automatically within the xxxx.Designer.cs file. How can this behviour be changed?
You need more attributes:
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public override string Text { get; set; }
Reflector is a crucial tool for a .NET developer. It is immediately obvious what you need to do when you use it to look at the UserControl.Text property:
[Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
Ho showed you what you need to do to cancel these attributes, too bad he didn't show you how he found out. Reflector is was free, download it from redgate.com or check the alternatives here : Something Better than .NET Reflector?
For serialization within the InitializeComponent(), all you need is the DesignerSerializationVisibilityAttribute:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
this is the method definition:
public static bool TryValidateObject(
Object instance,
ValidationContext validationContext,
ICollection<ValidationResult> validationResults,
bool validateAllProperties
)
what i am confused is the validateAllProperties parameter, I understand when it is true-validate all properties.
What about when it is false, not validate all properties, but which property will be validated?
See here for a good answer:
http://connect.microsoft.com/VisualStudio/feedback/details/605635/missleading-parametername-validateallproperties-in-validator-try-validate-componentemodel-dataannotations
It seems that when validateAllProperties is set to false that only the RequiredAttribute is validated.
When the property is false the Validator should validate each of the properties on the object that have a ValidationAttribute applied to them. This can include any of these attributes: CustomValidationAttribute, DataTypeAttribute, RangeAttribute, RegularExpressionAttribute, RequiredAttribute, and StringLengthAttribute, along with any other attributes that derive from ValidationAttribute.
See the MSDN library on the TryValidateObject method for more information.
In the following example, Foo should be validated, while Bar should not.
public class Example
{
[Required(ErrorMessage = "Foo is a required property.")]
public object Foo { get; set; }
public object Bar { get; set; }
}
I also don't fully understand it but after struggling with Unit Testing custom validators written by me I noticed one interresting thing.
When I launched my tests without this parameter (so by default it was false), my custom validators were omitted! if I set it to true, they were taken into account in my tests and now I can happily continue TDD. Hope this helps you a bit.
Arjen is right, only the Required attribute is validated when the validateAllProperties parameter is false.
I wrote a post about OData validation using DataAnnotations and I found the same issue.
http://blog.jorgef.net/2011/01/odata-dataannotations.html