Binding TextBox.Text to a Property in a class in ObservableCollection - c#

I have two TextBoxes. I have two ObservableCollections. The ObservableCollection has items of the following type:
public class ChartData : INotifyPropertyChanged
{
DateTime _Name;
double _Value;
#region properties
public DateTime Name
{
get
{
return _Name;
}
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
public double Value
{
get
{
return _Value;
}
set
{
_Value = value;
OnPropertyChanged("Value");
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I need to bind each of the TextBox.Text to the Value Property in each of the ObservableCollections. The ObservableCollections are DataContext for other controls in the window too. Since I have more than one ObservableCollection, I cannot set the DataContext to the Window.
New data is added to the ObservableCollection using:
ObservableCollection<ChartData>lineSeries1Data = new ObservableCollection<ChartData>();
lineSeries1Data.Add(new ChartData() { Name = DateTime.Now, Value = 0.0 });
When a new Value is added to the Collection, I want the TextBox to show the Value property

You can try something like this if you don't need a "real" binding, but just need to display the Value of the last object which is added (pseudo code):
public string NewItem { get; set+notify; }
ctor(){
myCollection = new ObservableCollection<T>();
myCollection.CollectionChanged += OnMyCollectionChanged;
}
private void OnMyCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (args.Action == NotifyCollectionChangedAction.Add){
var last = args.NewItems.FirstOrDefault();
if (last == null) return;
NewItem = last.Value;
}
}
//XAML:
<TextBox Text="{Binding NewItem, Mode=OneWay}" />

Related

How to bind a property of a XAML view to a public variable in the code behind every time the property changes

I am trying to take a property from a XAML control, specifically the TranslationX property, and store it in a public variable every time the value is changed.
I have tried using data binding by implementing the INotifyPropertyChanged interface and binding the TranslationX property to the public variable from my interface implementation, but had no luck
Essentially, I am needing the TranslationX property of a control to trigger function calls depending on the total displacement, ex. if the control is dragged to -200 in the X direction, it triggers function "Y". I cannot seem to access this translation value in a way that allows me to check if it is above or below a certain value.
I am very new to C# and Xamarin, so any advice is much appreciated.
EDIT:
Here is my current ViewModel class:
public class ReceiptPageViewModel : INotifyPropertyChanged
{
double shift = 0;
public double Shift
{
get => shift;
set
{
if (shift == value)
return;
else
{
shift = value;
OnPropertyChanged(nameof(Shift));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
And here is my syntax for my Binding:
TranslationX="{Binding Shift}"
in your XAML
<SomeElement ... TranslationX="{Binding TransX}" ... />
in your ViewModel
double transX;
public double TransX {
get { return transX; }
set {
transX = value;
if (transX > somethresholdvalue) {
...
}
}
}
Follow the MVVM pattern.
Create a Base View Model with the INotifyPropertyChanged.
Then your custom view model is going to inherit from that base class.
BaseViewModel
public class BaseViewModel : INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
Your custom class:
ReceiptPageViewModel
public class ReceiptPageViewModel : BaseViewModel
{
double shift = 0;
public double Shift
{
get { return shift; }
set { SetProperty(ref shift, value); }
}
}
And in your Xamarin Page set the BindingContext to the ViewModel
(This is an example)
public partial class Page1 : ContentPage
{
private ReceiptPageViewModel viewModel;
public Page1()
{
BindingContext = viewModel = new ReceiptPageViewModel();
InitializeComponent();
}
}
Now you can set the property in the XAML view:
<SomeElement ... TranslationX="{Binding Shift}" ... />
Here you can view a full episode about MVVM Pattern with #JamesMontemagno as host.

TextBox binding value int, in order to use it

how to get the value of the textbox and try to use it as int with binding?
<TextBox Text="{Binding SelectedAmount}"/>
I have tried like this, but the value of the binding is 0
public string SelectedAmount
{
get { return _selectedAmount; }
set { _selectedAmount = value; }
}
That is my main class, but the valau of the textbox stay 0, it does´t change
public partial class MainWindow : Window
{
int deposit;
int weeks;
int total;
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyClass();
}
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int _selectedAmount;
public int SelectedAmount
{
get
{
return this._selectedAmount;
}
set
{
if (value != this._selectedAmount)
{
this._selectedAmount = value;
NotifyPropertyChanged();
}
}
}
}
public void BtnCompute_Click_1(object sender, RoutedEventArgs e)
{
MyClass ff = new MyClass();
int cc = ff.SelectedAmount;
deposit = cc;
}
}
}
You can bind Text to int with no effort.
When using bindings, you should either derive the class containing bindable properties from the interface INotifyPropertyChanged or the class DependencyObject. otherwise the binding will show only the default (initial) values.
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int _selectedAmount;
public int SelectedAmount
{
get
{
return this._selectedAmount;
}
set
{
if (value != this._selectedAmount)
{
this._selectedAmount = value;
NotifyPropertyChanged();
}
}
}
}
as in here
or
public class MyClass : DependencyObject
{
/// <summary>
/// Gets or Sets SelectedAmount Dependency Property
/// </summary>
public int SelectedAmount
{
get { return (int)GetValue(SelectedAmountProperty); }
set { SetValue(SelectedAmount Property, value); }
}
public static readonly DependencyProperty SelectedAmountProperty =
DependencyProperty.Register("SelectedAmount ", typeof(int), typeof(MyClass), new PropertyMetadata(0));
}
also do not forget to set the DataContext of your view.
//in view's constructor:
this.DataContext = new MyClass();
or
<UserControl>
<UserControl.DataContext>
<vm:MyClass/>
</UserControl.DataContext>
</UserControl>
Simply use like this,
public void BtnCompute_Click_1(object sender, RoutedEventArgs e)
{
MyClass ff = new MyClass();
int amount;
int.TryParse(ff.SelectedAmount, out amount);
deposit = amount;
}

Issue of propertyChanged update for all subproperty in a class in winform

I implemented a model class and want to raise PropertyChanged events for all subproperty when the object is modified. But I found it 's not working. When I push the button, the label's text is't changed.Does i miss something?I got this from MSDN -"The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs."
the platform is .net framework 4.0 and VS2015
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Model = new Model()
{
data = new User()
{
Name = "test"
}
};
label1.DataBindings.Add("Text", Model.data, "Name", false, DataSourceUpdateMode.OnPropertyChanged);
}
private Model model;
public Model Model
{
get
{
return this.model;
}
set
{
model = value;
}
}
private void button1_Click(object sender, EventArgs e)
{
User temp = new User()
{
Name = "test1"
};
Model.data = temp;
}
}
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
OnPropertyChanged(null);
return true;
}
}
public class Model : NotifyPropertyChanged
{
private User m_data;
public User data
{
get { return m_data; }
set
{
SetField(ref m_data, value,"data");
}
}
}
public class User : NotifyPropertyChanged
{
private string name;
public string Name
{
get { return this.name; }
set
{
SetField(ref name, value, "Name");
}
}
private string tel;
public string Tel
{
get { return this.tel; }
set
{
SetField(ref tel, value, "Tel");
}
}
}
Your problem is that your binding on Model.data, but later on, assign it a new value.
So the instance that is being monitored by the binding, is no more being used.
You've 2 options:
First one: don't create new User, just change it's Name:
private void button1_Click(object sender, EventArgs e)
{
Model.data.Name = "test1";
}
Or, if you really need to support both case (creation and assigment), then you have to change the binding to the Model and take the text from data.Name:
label1.DataBindings.Add("Text", Model, "data.Name", false,
DataSourceUpdateMode.OnPropertyChanged);
And the set part of the User Property in the Model to this:
set
{
SetField(ref m_data, value, "data");
this.data.PropertyChanged += (sender, args) => this.OnPropertyChanged("data");
}
So, this will create a PropertyChanged on the data, if data.Name has been changed, well if the data property itself has been set

dataGridView bind to List - Convert bool to Image

I have a BindingList of Items that is bound to my dataGridView. The Item class is like this;
public class Item : INotifyPropertyChanged
{
private string _Name;
private bool _Active;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return _Name; }
set {
_Name = value;
this.NotifyPropertyChanged("Name");
}
}
public bool Active
{
get { return _Active; }
set {
_Active = value;
this.NotifyPropertyChanged("Active");
}
}
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Then I have the Bindinglist & the dataGridView;
BindingList<Item> ItemList = new BindingList<Item>();
dataGridView1.DataSource = ItemList;
I want the bool Active to be shown on the dataGridView as an Checked image when it is true, otherwise display nothing. A button on top of the dataGridView allows users to mark a row as Active.
Currently the dataGridView shows a checkbox. How can I have a correct binding from a bool in the item object to an image in the dataGridView?
Fixed it, I changed the item class to hold the image instead of trying to translate the bool in the binding;
public Image CheckImage
{
get
{
if (Active)
return Properties.Resources.check;
else
return null;
}
}

BindingSource with Generic SubClass in Windows Forms

I'm attempting to do what I considered simple data binding between a BindingSource and a ComboBox. I run into issues when the class I am using as the DataSource of the BindingSource has a property that is an instance of a generic class.
I have the following generic class:
public class GenericClass<T>
{
public T Code { get; set; }
public string Description { get; set; }
public override string ToString()
{
return Description;
}
}
I have a class that has an integer Code:
public class IntegerClass : GenericClass<int>
{
// Nothing unique here, for simple test.
}
I also have the class that is set to the BindingSource's DataSource:
public class ClassBindingClass : INotifyProperty Changed
{
private int _id;
private IntegerClass _choice;
private string _name;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public IntegerClass Choice
{
get { return _choice; }
set
{
_choice = value;
OnPropertyChanged("Choice");
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertName));
}
}
On my form I create a collection of IntegerClass and set my combobox's datasource as that collection. (This part works fine, the combo box displays the values appropriately.) Then I set the combobox's SelectedValue Binding to the BindingSource's Choice property updating on OnPropertyChanged.
If I replace IntegerClass with a non-generic class when you select a value in the combo box the BindingSource's Choice property changes the NotifyPropertyChanged event is fired and on my form I can update a label saying "Choice has changed!".
When the IntegerClass is part of the ClassBindingClass this no longer works and instead I cannot navigate out of the combo box and instead get a FormatException.
Is what I want to do possible? Can databinding handle generics?
You mention SelectedValue... but your source (and the bound property) are both IntegerClass - so it isn't a value you want to bind, but the item itself. Unfortunately, there is no ComboBox.SelectedItemChanged so you might need to hack it a bit to get 2-way binding...
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
IntegerClass[] choices = new[] {
new IntegerClass { Code = 123, Description = "a b c"},
new IntegerClass { Code = 456, Description = "d e f"},
new IntegerClass { Code = 789, Description = "g h i"},
};
ComboBox cbo = new TwoWayComboBox();
cbo.DropDownStyle = ComboBoxStyle.DropDownList;
cbo.DataSource = choices;
Form form = new Form();
ClassBindingClass obj = new ClassBindingClass();
cbo.DataBindings.Add("SelectedItem", obj, "Choice", true, DataSourceUpdateMode.OnPropertyChanged);
form.DataBindings.Add("Text", obj, "Choice", true, DataSourceUpdateMode.OnPropertyChanged); // show it
form.Controls.Add(cbo);
Application.Run(form);
}
}
class TwoWayComboBox : ComboBox {
public new object SelectedItem
{
get { return base.SelectedItem; }
set { base.SelectedItem = value; }
}
private static readonly object SelectedItemChangedKey = new object();
public event EventHandler SelectedItemChanged {
add { Events.AddHandler(SelectedItemChangedKey, value);}
remove { Events.RemoveHandler(SelectedItemChangedKey, value);}
}
protected override void OnSelectedIndexChanged(EventArgs e)
{
EventHandler handler = (EventHandler)Events[SelectedItemChangedKey];
if (handler != null) { handler(this, EventArgs.Empty); }
base.OnSelectedIndexChanged(e);
}
}

Categories

Resources