Xamarin.Forms: MultiBinding IMultiValueConverter recieving Null values - xamarin

Hello Guys i need help.
i am using a converter on a binding to set a background color depending on an object id.(stacklayout inside contentview)
<StackLayout.BackgroundColor>
<Binding Path="objectID" Converter="{StaticResource IntToColorConverter}"/>
</StackLayout.BackgroundColor>
This works.
Now i want to use the multiconverter (new in Xamarin 4.7) to return a different backgroundcolor depending on other object properties.(for context: object is a calendarentry and if its in the past it should be desaturated or something)
<StackLayout.BackgroundColor>
<MultiBinding Converter="{StaticResource MultiColorConverter}">
<Binding Path="objectID"/>
<Binding Path="value"/>
<Binding Path="value2"/>
</MultiBinding>
</StackLayout.BackgroundColor>
This does not work, since the values that are given to the converter are all NULL and the color becomes black (the return value if all vslues are NULL; so the converter is set up correctly also). It also shows this when i use a break point on the converter, that the array contains only NULL variables.
I don't know what i am missing here, the bindingcontext should be inheritated and does not change. Any hint would be appreciated.
The bindingcontext is set programatically on a contentpage on creation of the ContentView where i provide an object from a list of objects.
var evnt = new TimeTableEventView { BindingContext = Model.calenderevents[j] };

You need to return BindableProperty.UnsetValue to use the binding FallbackValue .
in xaml
<StackLayout.BackgroundColor>
<MultiBinding Converter="{StaticResource MultiColorConverter}">
<Binding Path="red"/>
<Binding Path="green"/>
<Binding Path="blue"/>
</MultiBinding>
</StackLayout.BackgroundColor>
in Converter
public class MultiColorConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (var value in values)
{
if (!(value is int b))
{
return Color.White;
// set a default value when unset
}
}
int red = (int)values[0];
int green = (int)values[1];
int blue = (int)values[2];
Color color = Color.FromRgb(red,green,blue);
return color;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
in code behind
public class MyViewModel
{
public int red { get; set; }
public int green { get; set; }
public int blue { get; set; }
public MyViewModel(int r, int g, int b)
{
red = r;
green = g;
blue = b;
}
}
BindingContext = new MyViewModel(120, 60, 180);

Related

Xamarin forms eventToCommand behavior for zxing library

i'm trying to use the event to command behavior for the zxing barcode scanning.
var eventParameters = eventinfo.EventHandlerType
.GetRuntimeMethods().First(m => m.Name == "Invoke")
.GetParameters()
.Select(p => Expression.Parameter(p.ParameterType))
.ToArray();
var actionInvoke = action.GetType()
.GetRuntimeMethods().First(m => m.Name == "Invoke");
_handler = Expression.Lambda(
eventinfo.EventHandlerType,
Expression.Call(Expression.Constant(action), actionInvoke, eventParameters[0], eventParameters[1]),
eventParameters).Compile();
eventinfo.AddEventHandler(item, _handler);
this code works fine for any other event that has (object sender and event args) but the zxing event has only one argument. Do u know how can i make this code to work?
The EventToCommandBehavior will probably pass arguments as object type.
Add this the your eventToCommandBehavior
public static BindableProperty EventArgsConverterProperty = BindableProperty.CreateAttached("EventArgsConverter", typeof(IValueConverter), typeof(EventToCommandBehavior), null, BindingMode.OneWay);
...
public IValueConverter EventArgsConverter
{
get { return (IValueConverter)GetValue(EventArgsConverterProperty); }
set { SetValue(EventArgsConverterProperty, value); }
}
...
This is the implementation of YourEventArgsConverter.
public class YourEventArgsConverter : IValueConverter
{
public object Converter(object value, Type, targetType, object parameter, CultureInfo culture)
{
var eventArgs = value as [PUT YOUR EVENT ARGS TYPE HERE];
if (eventArgs == null)
throw new ArgumentException("Expected YOUR EVENT ARGS TYPE as value", value)
return eventArgs;
}
public object ConvertBack(object value, Type, targetType, object parameter, CultureInfo culture)
{
throw new NoImplementedException();
}
}
You can use the EventArgsConvert like this.
...
<ResourceDictionary>
<converters:YourEventArgsConverter x:Key="YourEventArgsConverter" />
</ResourceDictionary>
...
<behaviors:EventToCommandBehavior
EventName="YOUREVENTNAME"
EventArgsConverter="{StaticResource YourEventArgsConverter}"
Command="{Binding EventCommand}" />
...

Get ViewCell's model in constructor

There are some questions without any real answer for this use case:
How can I retrieve the current ViewCell's model in its constructor?
What I'd like to have access, for instance, to the background of the cell, specified in the model.
I have tried many solutions, like binding string properties to fake Label objects inside the cell, but both in the constructor and in the OnAppearing method they are null.
For example, let's say I have a custom cell that needs to display a title, a description, and an optional icon. If the icon resource string is empty, it will display a default one:
public class ListItem
{
public string title { get; set; }
public string description { get; set; }
public string icon { get; set; }
}
public class CustomCell : ViewCell
{
public CustomCell()
{
StackLayout cell = new StackLayout();
StackLayout text = new StackLayout()
{
Orientation = StackOrientation.Vertical
};
Label titleLabel = new Label();
Label descriptionLabel = new Label();
titleLabel.SetBinding(Label.TextProperty, "title");
descriptionLabel.SetBinding(Label.TextProperty, "description");
Image image = new Image();
// Custom icon
if (titleLabel.Text.Length != 0)
{
image.Source = ImageSource.FromResource(???);
}
else
{
image.Source = ImageSource.FromResource("myproject.icons.default.png");
}
text.Children.Add(titleLabel);
text.Children.Add(descriptionLabel);
cell.Children.Add(text);
cell.Children.Add(image);
View = cell;
}
}
Is there a way?
You can write a custom view cell that takes arguments or model in its constructor like this
public class CustomViewCell : ViewCell
{
public Bar bar;
pulic CustomViewCell(Foo foo)
{
bar = foo;
}
public GetCurrentModel()
{
return bar;
}
}

How to change appearance of viewcell using bound data in Xamarin Forms?

What i am trying to do is, I created a ViewCell and bound it to ListView. In ViewCell, I have title label which I want to change as per data coming from the database.
What would be the best practice to this?
Here my chunk of code
Model-
public class helplineservices
{
public string title { get; set; }
public bool isenable { get; set; }
}
ViewCell -
public class HelpLineCell : ViewCell
{
#region binding view cell logic
public HelpLineCell()
{
BlackLabel title = new BlackLabel
{
FontFamily = Device.OnPlatform(
"Roboto-Black",
null,
null),
FontSize = Device.OnPlatform(
(ScreenSize.getscreenHeight() / 47),
(ScreenSize.getscreenHeight() / 47),
14
),
HorizontalTextAlignment = TextAlignment.Center,
TextColor = Color.FromHex("#FFFFFF"),
WidthRequest = ScreenSize.getscreenWidth()
};
title.SetBinding(Label.TextProperty, "title");
this.View = title;
}
#endregion
}
ListView -
var HelpList = new ListView
{
IsPullToRefreshEnabled = true,
HasUnevenRows = true,
BackgroundColor = Color.Transparent,
RefreshCommand = RefreshCommand,
//row_list is a list that comes from database
ItemsSource = row_list,
ItemTemplate = new DataTemplate(typeof(HelpLineCell)),
SeparatorVisibility = SeparatorVisibility.None
};
I want to change title color by checking a bool value of isenable which comes from database.
Please help me.
You have to Bind the TextColor like you have done for your Text property, then convert with a IValueConverter the Boolean value to a Color
Something like:
title.SetBinding(Label.TextColorProperty, new Binding("isenable", BindingMode.Default, new BooleanToColorConverter()));
Your IValueConverter should be something like
public class BooleanToColorConverter : IValueConverter
{
#region IValueConverter implementation
public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is bool) {
if(((bool) value) == true)
return Color.Red;
else
return Color.Black;
}
return Color.Black;
}
public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException ();
}
#endregion
}
PS: NOT TESTED...
A useful article

Add resource/import image to custom UserControl attribute in VS Designer via Dialog(s)

My aim is to create a custom control displaying some images, which can be added/exchanged by the user of that control. So, if it is added to a Form, the GUI designer should be able to change some or all images provided by the control editing the appropriate attribute.
In my Test-Project I have a simple control with 4 Attributes:
public Image MyImage { get; set; } = null;
public List<int> MyListOfInt { get; set; } = new List<int>();
public List<Image> MyListOfImages { get; set; } = new List<Image>();
public ImageList MyImageList { get; set; } = new ImageList();
Using this control in a Windows Form Project, clicking on
MyImage brings up the 'Select resource' dialog. OK
MyListOfInt brings up the 'Int32 Collection Editor' dialog. OK
MyListOfImages brings up the 'Image Collection Editor' dialog, but using 'Add' button shows message:
'Cannot create an instance of System.Drawing.Image because it is an
abstract class.'
MyImageList shows an emtpy list, which cannot be edited.
My question is, if it's possible to tell VS Designer to use the 'Select resource' dialog when clicking 'Add' button and what needs to be done?
Starting from Marwie's comment, I was able to solve the problem.
There are three requirements that a collection should meet in order to be successfully persisted with the CollectionEditor:
The collection must implement the IList interface (inheriting from System.Collections.CollectionBase is in most of the cases the best option).
The collection must have an Indexer property.
The collection class must implement one or both of the following methods: Add and/or AddRange
So I created a class 'ImageItem' containing
an image
[Category("ImageItem")]
[DefaultValue(typeof(Image), null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image Picture {
get { return m_Picture; }
set { m_Picture = value; }
}
a name (optional)
[Category("ImageItem")]
[DefaultValue(typeof(string), "")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Name {
get { return m_Name; }
set { m_Name = value; }
}
a value (optional)
[Category("ImageItem")]
[DefaultValue(typeof(int), "-1")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Value {
get { return m_Value; }
set { m_Value = value; }
}
and a collection 'ImageCollection' holding instances of this class according to the conditions mentioned above:
public class ImageCollection : CollectionBase
public ImageItem this[int i]
public ImageItem Add(ImageItem item)
Then I created a control containing only this collection, initialized with one image:
public partial class MyControl: UserControl
{
public MyControl() {
InitializeComponent();
}
private ImageCollection m_MyImageCollection = new ImageCollection()
{ new ImageItem(0, "Failure", Properties.Resources.Cross), new ImageItem(1, "OK", Properties.Resources.Tickmark) };
[Browsable(true), Category("A Test"), DisplayName("Image Collection (ImageCollection)"), Description("Edit image collection")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
public ImageCollection MyImageCollection {
get { return m_MyImageCollection; }
}
}
After compiling this code the designer shows that property. Now it is possible to add images using the common designer GUI controls.
I tried to change the default images compiled into this control when using it on my form, but I recognized, that the designer cannot remove content. It only stores the 'Add' action. So I modified the code to search within the collection for another item with the same ID. If there is one available, that instance is removed and replaced with the new one. Therefore I had to implement the AddRange method too.
public ImageItem Add(ImageItem item) {
for(int i = 0; i < InnerList.Count; i++) {
if(InnerList[i] is ImageItem) {
if(((ImageItem)InnerList[i]).Value == item.Value) {
InnerList.RemoveAt(i);
}
}
}
this.InnerList.Add(item);
return item;
}
public void AddRange(ImageItem[] array) {
foreach(ImageItem item in array) {
Add(item);
}
}
So my final classes are:
public class ImageItem {
private int m_Value = -1;
private string m_Name = "ImageItem";
private Image m_Picture = null;
[Category("ImageItem")]
[DefaultValue(typeof(int), "-1")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Value {
get { return m_Value; }
set { m_Value = value; }
}
[Category("ImageItem")]
[DefaultValue(typeof(string), "")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Name {
get { return m_Name; }
set { m_Name = value; }
}
[Category("ImageItem")]
[DefaultValue(typeof(Image), null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image Picture {
get { return m_Picture; }
set { m_Picture = value; }
}
public ImageItem() { }
public ImageItem(int value, string name, Image image) {
this.m_Value = value;
this.m_Name = name;
this.m_Picture = image;
}
}
And ImageCollection:
public class ImageCollection : CollectionBase {
public ImageCollection() {}
public ImageItem this[int i]
{
get { return (ImageItem)this.InnerList[i]; }
set { this.InnerList[i] = value; }
}
public ImageItem Add(ImageItem item) {
for(int i = 0; i < InnerList.Count; i++) {
if(InnerList[i] is ImageItem) {
if(((ImageItem)InnerList[i]).Value == item.Value) {
InnerList.RemoveAt(i);
}
}
}
this.InnerList.Add(item);
return item;
}
public void AddRange(ImageItem[] array) {
foreach(ImageItem item in array) {
Add(item);
}
}
public void Remove(ImageItem item) {
this.InnerList.Remove(item);
}
public bool Contains(ImageItem item) {
return this.InnerList.Contains(item);
}
public ImageItem[] GetValues() {
ImageItem[] item= new ImageItem[this.InnerList.Count];
this.InnerList.CopyTo(0, item, 0, this.InnerList.Count);
return item;
}
protected override void OnInsert(int index, object value) {
base.OnInsert(index, value);
}
}
I've got another answer from MSDN:
How to edit UserControl attribute of type ImageList in Designer PropertyGrid (add/remove/exchange images)
I will describe the idea in short. First create a new control with an ImageList attribute.
public partial class NewControl : UserControl {
public NewControl() {
InitializeComponent();
}
public ImageList MyImageList { get; set; } = null;
}
Then drag this control on any form.
Additionally drag an ImageList control from Toolbox onto this
form - I called it 'MyImages'.
Edit MyImages → Images with designer.
Assign 'MyImages' to NewControl's instance attribute MyImageList in property grid
The only drawback I see here is, that if the control already has an initialized ImageList attribute, the designer cannot handle it. If you try to edit MyImageList before you assigned another list, the designer shows the controls default list, that comes with the control. But it's not possible to edit that list.
This solution is much easier to deal with and much shorter than the first solution above, so that I prefer it more.

Data Binding for images using sql ce for windows phone 7

I have been using the sql ce to bring some data onto my application. Now I need to add some of the images to make it look pretty. What all I want to know is
There must be some image to byte conversion done,
Retrieve the image byte code and convert back into the image.
I've got stuck at the second part and how am I supposed to continue?
Any links or examples are needed for the reference.
Thanks a lot.
Here's some ideas I have used in the past.
The image column in the DB:
[Column]
public byte[] MyImage
{
get { return _myImage; }
set
{
if (_myImage != value)
{
_myImage = value;
NotifyPropertyChanging("MyImage");
NotifyPropertyChanged("MyImage");
}
}
}
Save image:
public void AddNewImage(Stream image, string url)
{
byte[] byteArray = GetImageBytes(image);
var item = new MyDatabaseItem { Count = 1, ItemImageUrl = url, MyImage = byteArray };
MyDatabaseItemModel.Add(item);
MyDatabaseDB.MyDatabaseItems.InsertOnSubmit(item);
MyDatabaseDB.SubmitChanges();
}
Get image:
private byte[] GetImageBytes(Stream stream)
{
using (var ms = new MemoryStream())
{
var writeableBitmap = PictureDecoder.DecodeJpeg(stream, 200, 200);
writeableBitmap .SaveJpeg(ms, 200, 200, 0, 30);
return ms.ToArray();
}
}
Using a value converter:
public class ImageConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is byte[])
{
var memoryStream = new MemoryStream(value as byte[]);
varwriteBitmap = PictureDecoder.DecodeJpeg(memoryStream, 200, 200);
return writeBitmap;
}
else
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And finally bind it in XAML:
<Image Source="{Binding MyImage, Converter={StaticResource ImageConverter}}" Stretch="UniformToFill"/>

Resources