Would anyone know if its possible to bind data to a SfGauge?
If so how according to the needle pointer value,
NeedlePointer needlePointer = new NeedlePointer();
needlePointer.Value = 60;
needlePointer.Color = Color.Gray;
needlePointer.KnobColor = Color.FromHex("#2bbfb8");
needlePointer.Thickness = 5;
needlePointer.KnobRadius = 20;
needlePointer.LengthFactor = 0.8;
scale.Pointers.Add(needlePointer);
Thanks
Here is my Updated code however still showing zero on the guage.
namespace Drip
{
public partial class Guage : ContentPage
{
private const string Url = "https://thingspeak.com/channels/301726/field/1.json";
private HttpClient _client = new HttpClient();
private ObservableCollection<Feed> _data2;
SfCircularGauge circular;
NeedlePointer needlePointer;
public Guage()
{
...
needlePointer = new NeedlePointer();
needlePointer.Color = Color.Gray;
needlePointer.KnobColor = Color.FromHex("#2bbfb8");
needlePointer.Thickness = 5;
needlePointer.KnobRadius = 20;
needlePointer.LengthFactor = 0.8;
scale.Pointers.Add(needlePointer);
Content = circular;
}
protected override async void OnAppearing()
{
var content = await _client.GetStringAsync(Url);
var data = JsonConvert.DeserializeObject<RootObject>(content);
_data2 = new ObservableCollection<Feed>(data.Feeds);
this.BindingContext = _data2[0];
needlePointer.SetBinding(Pointer.ValueProperty, "Field1");
}
}
Model
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Drip
{
public class Feed : INotifyPropertyChanged
{
private decimal _field1 = 0;
public decimal Field1
{
get
{
return _field1;
}
set
{
if (_field1 != value)
{
_field1 = value;
OnPropertyChanged();
}
}
}
public DateTime Created_at { get; set; }
public int Entry_id { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyField1 = null)
{
{
var handler = PropertyChanged;
if (handler != null)
{
PropertyChangedEventArgs propertyChangedEvent
= new PropertyChangedEventArgs(propertyField1);
handler(this, propertyChangedEvent);
}
}
}
}
}
I have changed the following above to my code however the guage is still showing value zero, have i missed anything or used something wrong?
assuming you want to bind to the property "Value" of your BindingContext
needlePointer.SetBinding(NeedlePointer.ValueProperty, "Value");
in order for data binding to work, you need to set a BindingContext
this.BindingContext = _data2[0];
The value which we are getting for _data2[0] from json is 0. Please refer the following attached screenshot of the json data.
In the attached screenshot, highlighted the field1 item value of 0th index from the collection. So, if you bound those value with the needle pointer it will show only the 0 value. Can you please ensure what value you are getting from _data2[0]? If you are getting 0 then please get the field1 value from _data[1] or _data[2].
Related
I have an object class that derives from BoxView. The object has TouchEffect attached and when the OnTouchAction starts, I change the object's properties.
I would also like to change the property of a label based on the text attached to the string that label is bound to.
What I did is I created an instance of the page that contains bindable string and label, then, I tried to change the value of string by referencing it in code inside the OnTouchAction method.
I don't get errors and the Breakpoint tells me that code arrives to the line, but the label is not being updated.
I am trying to update the string from the class that is not the same where the string is.
Is there anyone who could help me out here?
class Element : BoxView
{
List<long> ids = new List<long>();
MainPage mainPage = new MainPage();
public event EventHandler StatusChanged;
public Element()
{
TouchEffect effect = new TouchEffect();
effect.TouchAction += OnTouchEffectAction;
Effects.Add(effect);
}
public Color DefaultColor { set; get; }
public Color HighlightColor { set; get; }
public bool IsPressed { private set; get; }
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Pressed:
AddToList(args.Id);
mainPage.LeftLabelText = "entered";
break;
case TouchActionType.Entered:
if (args.IsInContact)
{
AddToList(args.Id);
mainPage.LeftLabelText = "entered";
}
break;
case TouchActionType.Moved:
break;
case TouchActionType.Released:
case TouchActionType.Exited:
RemoveFromList(args.Id);
break;
}
}
void AddToList(long id)
{
if (!ids.Contains(id))
{
ids.Add(id);
}
CheckList();
}
void RemoveFromList(long id)
{
if (ids.Contains(id))
{
ids.Remove(id);
}
CheckList();
}
void CheckList()
{
if (IsPressed != ids.Count > 0)
{
IsPressed = ids.Count > 0;
Color = IsPressed ? HighlightColor : DefaultColor;
mainPage.LeftLabelText = "entered";
StatusChanged?.Invoke(this, EventArgs.Empty);
}
}
}
and the MainPage relevant code:
Element element = new Element();
element.HighlightColor = Color.Accent;
element.DefaultColor = Color.Transparent;
element.Color = bar.DefaultColor;
element.HeightRequest = sGrid.Height;
element.VerticalOptions = LayoutOptions.End;
(...) // adding element view to the grid.
The string:
public string _leftLabelText = "testing";
public string LeftLabelText
{
get => _leftLabelText;
set
{
_leftLabelText = value;
NotifyPropertyChanged("LeftLabelText");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged2;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged2?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
<Label x:Name="leftLabel" x:FieldModifier="public" Text="{Binding LeftLabelText, Mode=TwoWay}" TextColor="Black" FontSize="10" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"/>
I know this is terribly common issue, but I just can't get the button to update to "Pressed1" and "Pressed2" content when changing "Default" of buttonContent. Having looked at few questions, I can't find the answer that'd work for me, I simply can't find out what is wrong here, so here's the crappy code:
The window with a button
public partial class MainWindow : Window
{
Code_Behind cB;
public MainWindow()
{
cB = new Code_Behind();
this.DataContext = cB;
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
cB.buttonPressed();
}
}
And here's the separate class
public class Code_Behind : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _buttonContent = "Default";
public string buttonContent
{
get { return _buttonContent; }
set {
if (_buttonContent != value)
{
buttonContent = value;
OnPropertyChanged("buttonContent");
}
}
}
public void buttonPressed()
{
int timesPressed = 0;
if (timesPressed != 1)
{
_buttonContent = "Pressed1";
timesPressed++;
}
else if (timesPressed != 2)
{
_buttonContent = "Pressed2";
timesPressed++;
timesPressed = 0;
}
}
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
You are not setting the property, but the backing field. Hence the PropertyChanged event is not fired.
Replace
_buttonContent = "Pressed1";
...
_buttonContent = "Pressed2";
with
buttonContent = "Pressed1";
...
buttonContent = "Pressed2";
Besides that, it is a widely accepted convention to write property names with Pascal casing, i.e. ButtonContent instead of buttonContent.
Moreover, your property setter looks odd (probably because you try to squeeze too much code in one line).
Instead of
set
{
if (_buttonContent != value)
{
_buttonContent = value;
}
OnPropertyChanged("buttonContent");
}
it should certainly be
set
{
if (_buttonContent != value)
{
_buttonContent = value;
OnPropertyChanged("buttonContent");
}
}
I've created a custom usercontrol in my application and I try to bind to its property (BindableProperty) to ViewModel but it doesn't work for me. Am I doing something wrong?
This is the usercontrol. It is just custom stepper for the purpose of test project: "decrease" and "increase" buttons and quantity label between them.
Please notice that binding inside the usercontrol works perfect (for example Commands bindings) . What I am trying to do is bind QuantityProperty to ViewModel
namespace UsercontrolBindingTest.Usercontrols
{
using System.Windows.Input;
using Xamarin.Forms;
public class CustomQuantityStepper : ContentView
{
public static readonly BindableProperty QuantityProperty =
BindableProperty.Create(nameof(Quantity), typeof(int), typeof(CustomQuantityStepper), 0, BindingMode.TwoWay);
public int Quantity
{
get
{
return (int)base.GetValue(QuantityProperty);
}
set
{
base.SetValue(QuantityProperty, value);
this.OnPropertyChanged(nameof(this.Quantity));
}
}
public ICommand DecreaseQuantityCommand { get; private set; }
public ICommand IncreaseQuantityCommand { get; private set; }
public CustomQuantityStepper()
{
this.BindingContext = this;
this.DecreaseQuantityCommand = new Command(() => this.Quantity--);
this.IncreaseQuantityCommand = new Command(() => this.Quantity++);
this.DrawControl();
}
private void DrawControl()
{
var quantityEntry = new Entry();
quantityEntry.SetBinding(Entry.TextProperty, new Binding("Quantity", BindingMode.TwoWay));
quantityEntry.WidthRequest = 50;
quantityEntry.HorizontalTextAlignment = TextAlignment.Center;
var increaseQuantityButton = new Button { Text = "+" };
increaseQuantityButton.SetBinding(Button.CommandProperty, "IncreaseQuantityCommand");
var decreaseQuantityButton = new Button { Text = "-" };
decreaseQuantityButton.SetBinding(Button.CommandProperty, "DecreaseQuantityCommand");
var ui = new StackLayout()
{
Orientation = StackOrientation.Horizontal,
Children =
{
decreaseQuantityButton,
quantityEntry,
increaseQuantityButton
}
};
this.Content = ui;
}
}
}
The view with proof that binding between View and VM is working:
namespace UsercontrolBindingTest
{
using Usercontrols;
using Xamarin.Forms;
public class App : Application
{
public App()
{
MainPage = new ContentPage
{
BindingContext = new MainPageVM(),
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
Children =
{
this.GetLabel("Title"),
this.GetCustomStepper(),
this.GetLabel("SelectedQuantity")
}
}
};
}
private Label GetLabel(string boundedPropertyName)
{
var ret = new Label();
ret.HorizontalOptions = LayoutOptions.CenterAndExpand;
ret.SetBinding(Label.TextProperty, new Binding(boundedPropertyName));
return ret;
}
private CustomQuantityStepper GetCustomStepper()
{
var ret = new CustomQuantityStepper();
var dataContext = this.BindingContext as MainPageVM;
ret.SetBinding(CustomQuantityStepper.QuantityProperty, new Binding("SelectedQuantity", BindingMode.TwoWay));
return ret;
}
}
}
And my simple ViewModel:
namespace UsercontrolBindingTest
{
using System.ComponentModel;
internal class MainPageVM : INotifyPropertyChanged
{
private int _selectedQuantity;
public int SelectedQuantity
{
get
{
return this._selectedQuantity;
}
set
{
this._selectedQuantity = value;
this.NotifyPropertyChanged(nameof(this.SelectedQuantity));
}
}
public string Title { get; set; } = "ViewModel is bound";
public MainPageVM()
{
this.SelectedQuantity = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
I've checked plenty of different topis and blog posts but I wasn't able to find solutions for my issue. Hope for help here...
Attached sample project here: https://1drv.ms/u/s!Apu16I9kXJFtl28YBjkfwDztT9j0
You are setting the BindingContext of CustomQuantityStepper to itself (this). That's why the binding engine is looking for a public property named "SelectedQuantity" in your custom control and not in the ViewModel.
Don't set the BindingContext in the control and instead let it use the context that is currently defined and (hopefully) points to the VM.
Set the Source property of the Binding and let it point to the correct source (which would be the VM).
If I had an Observable collection like so :
public ObservableCollection<SpecialPriceRow> SpecialPriceRows = new ObservableCollection<SpecialPriceRow>();
SpecialPriceRow class :
public class SpecialPriceRow : INotifyPropertyChanged
{
public enum ChangeStatus
{
Original,
Added,
ToDelete,
Edited
}
public ChangeStatus Status { get; set; }
public string PartNo { get; set; }
private decimal _price;
public decimal Price
{
get
{
return _price;
}
set
{
if (value != _price)
{
_price = value;
Status = ChangeStatus.Edited;
OnPropertyChanged("Status");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Would it be possible for me to bind a Label in the XAML to the count of objects that are say ... Added? So I could get something like this :
Where green is the count of "Added" objects within the collection. How would I go about doing something like this?
I've written up a ViewModel which will perform the desired functionality you are looking for.
class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<SpecialPriceRow> _SpecialPriceRows = new ObservableCollection<SpecialPriceRow>();
private int _Original = 0;
private int _Added = 0;
private int _ToDelete = 0;
private int _Edited = 0;
public VM()
{
PropertyChanged = new PropertyChangedEventHandler(VM_PropertyChanged);
//The following lines in the constructor just initialize the SpecialPriceRows.
//The important thing to take away from this is setting the PropertyChangedEventHandler to point to the UpdateStatuses() function.
for (int i = 0; i < 12; i++)
{
SpecialPriceRow s = new SpecialPriceRow();
s.PropertyChanged += new PropertyChangedEventHandler(SpecialPriceRow_PropertyChanged);
SpecialPriceRows.Add(s);
}
for (int j = 0; j < 12; j+=2)
SpecialPriceRows[j].Price = 20;
}
private void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
private void SpecialPriceRow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Status")
UpdateStatuses();
}
public ObservableCollection<SpecialPriceRow> SpecialPriceRows
{
get
{
return _SpecialPriceRows;
}
}
private void UpdateStatuses()
{
int original = 0, added = 0, todelete = 0, edited = 0;
foreach (SpecialPriceRow SPR in SpecialPriceRows)
{
switch (SPR.Status)
{
case SpecialPriceRow.ChangeStatus.Original:
original++;
break;
case SpecialPriceRow.ChangeStatus.Added:
added++;
break;
case SpecialPriceRow.ChangeStatus.ToDelete:
todelete++;
break;
case SpecialPriceRow.ChangeStatus.Edited:
edited++;
break;
default:
break;
}
}
Original = original;
Added = added;
ToDelete = todelete;
Edited = edited;
}
public int Original
{
get
{
return _Original;
}
set
{
_Original = value;
PropertyChanged(this, new PropertyChangedEventArgs("Original"));
}
}
public int Added
{
get
{
return _Added;
}
set
{
_Added = value;
PropertyChanged(this, new PropertyChangedEventArgs("Added"));
}
}
public int ToDelete
{
get
{
return _ToDelete;
}
set
{
_ToDelete = value;
PropertyChanged(this, new PropertyChangedEventArgs("ToDelete"));
}
}
public int Edited
{
get
{
return _Edited;
}
set
{
_Edited = value;
PropertyChanged(this, new PropertyChangedEventArgs("Edited"));
}
}
}
Take note of the comments in the constructor. You need to point the PropertyChanged event of each SpecialPriceRow to the UpdateStatuses function in order for this to work properly.
Now all you need to do is bind your labels to the appropriate properties in the ViewModel.
If your SpecialPriceRows list becomes very large, you may want to consider calculating the status counts a bit differently. Currently, it is iterating through the entire list every time one instance is updated. For this to perform better, you may want to keep the old value of the status in the SpecialPriceRow class and every time an update occurs, increment the new status count and decrement the old one.
I'm not aware of any builtin functionality to do this. I would create a custom property in your data context class that does the counting and bind to this.
Something like this:
public int AddedCount
{
get
{
return SpecialPriceRows.Where(r => r.Status == ChangeStatus.Added).Count();
}
}
Then whenever an item is changed or added you need to explicitly raise the property changed for this:
public void AddItem()
{
...
OnPropertyChanged("AddedCount");
}
Then you only need to bind in your XAML code like:
<TextBlock Text="{Binding AddedCount}" />
You may need to subscribe to the change events in your collection to know when an item changes.
Alternative:
You can also create a ListCollectionView with a specific filter and bind to its Count property:
AddedItems = new ListCollectionView(TestItems);
AddedItems.Filter = r => ((SpecialPriceRow)r).Status == ChangeStatus.Added;
In your XAML you would then bind to the Count property of this:
<TextBlock Text="{Binding AddedItems.Count}" />
This has the advantage that it will automatically keep track of added and removed items in the collection and update itself. You have to refresh it manually though when the property of an item changes which affects the filter.
I'm trying to bind a TextBlock using INotifyPropertyChanged event. But it is not updating anything to the TextBlock. The TextBlock is blank. My goal is to update the status of items which are displayed in different rows. I need to update the TextBlock's text and color based on the status.
Could anyone tell me what is wrong with my code?
public class ItemStatus : INotifyPropertyChanged
{
string itemStatus;
Brush itemStatusColor;
public string ItemStatus
{
get { return itemStatus; }
set
{
itemStatus = value;
this.OnPropertyChanged("ItemStatus");
}
}
public Brush ItemStatusColor
{
get { return itemStatusColor; }
set
{
itemStatusColor = value;
this.OnPropertyChanged("ItemStatusColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
public class Items
{
List<ItemStatus> currentItemStatus;
public List<ItemStatus> CurrentItemStatus
{
get { return currentItemStatus; }
set { currentItemStatus = value; }
}
}
public partial class DisplayItemStatus : Page
{
....
....
public DisplayItemStatus()
{
foreach (Product product in lstProductList)
{
TextBlock tbItemStatus = new TextBlock();
....
Items objItems = new Items();
Binding bindingText = new Binding();
bindingText.Source = objItems;
bindingText.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
bindingText.Path = new PropertyPath(String.Format("ItemStatus"));
tbItemStatus.SetBinding(TextBlock.TextProperty, bindingText);
Binding bindingColor = new Binding();
bindingColor.Source = objItems;
bindingColor.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
bindingColor.Path = new PropertyPath(String.Format("ItemStatusColor"));
tbItemStatus.SetBinding(TextBlock.ForegroundProperty, bindingColor);
grdItemsList.Children.Add(tbItemStatus);
}
}
private void UpdateItems_Click(object sender, MouseButtonEventArgs e)
{
int intCount = 0;
List<Product> ProductList = new List<Product>();
List<ItemStatus> ItemList = new List<ItemStatus>();
ProductList = GetProducts();
foreach (Product product in ProductList)
{
intCount++;
UpdateStatus(intCount, ItemList);
}
}
public void UpdateStatus(int intIndex, List<ItemStatus> ItemList)
{
ItemStatus status = new ItemStatus();
status.ItemStatus = strOperationStatus;
status.ItemStatusColor = brshForegroundColor;
ItemList.Add(status);
}
}
Well, the specific problem here is that you're binding the TextBlock to an Item and not the ItemStatus. But you're also doing things the hard way, you really should do the binding details in XAML. Expose a collection of ItemStatus's from your view model, and have a ListBox or something with its ItemsSource bound to the collection. Then you'll need a DataTemplate which defines the TextBlock and the bindings to the ItemStatus.
Here's a good walkthrough for it in general