I have the following object:
public class Notification : INotifyPropertyChanged
{
private bool _trafficNot;
public bool TrafficNot
{
get { return _trafficNot; }
set {
if (value.Equals(_trafficNot))
return;
_trafficNot = value;
OnPropertyChanged();
}
}
private bool _newsNot;
public bool NewsNot
{
get { return _newsNot; }
set
{
if (value.Equals(_newsNot))
return;
_newsNot = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName]String propertyName=null)
{
var handler=PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I get the data from an a object like this:
//set up the notification object according to what is stored in the DB
Notification notification = new Notification
{
TrafficNot = uInfo.NotificationTraffic,
NewsNot = uInfo.NotificationNews
};
and I want to bind the data to these switchells
TableView tableView = new TableView
{
BindingContext = notification,
Intent = TableIntent.Form,
Root = new TableRoot
{
new TableSection
{
new SwitchCell
{
Text = "News",
BindingContext = "NewsNot"
},
new SwitchCell
{
Text = "Traffic",
BindingContext = "TrafficNot"
},
new SwitchCell
}
}
};
What else do i need to do to bind it?
Cheers
You didn't bind view properties at all. Instead of assigning text to BindingContxt and Text property you should bind those Text properties, i.e.:
var sc = new SwitchCell();
sc.SetBinding(SwitchCell.TextProperty, new Binding("NewsNot"));
BindingContext is the source object while you are binding against its properties. See also DataBinding docs.
Related
I am making WPFToolkit based Graph where i am trying to update AreaSeries every time i click on a button.
I have implemented INotifyPropertyChanged on my data class. but when i reload the data in the source object id doesn't updates in chart(target object)
the code is as below:
public partial class MainWindow : Window
{
static List<Ready4LOS> Ready4LOS = new List<Data.Ready4LOS>();
public MainWindow()
{
InitializeComponent();
chart1.DataContext = Ready4LOS;
InitChart();
LoadData();
}
private void LoadData()
{
var path = #"zxzxzxz.log";
Ready4LOS.Clear();
List<APISTATDataModel> daa = APISTATDataModel.GetFromFile(path, new string[] { "|" }, "Ready4TOS");
List<APISTATDataModel> lastn = daa.GetRange(daa.Count - 10, 10);
foreach (APISTATDataModel d in lastn)
{
Ready4LOS.Add(new Ready4LOS() { Case = d.Current_Count, Time = d.Current_Time });
}
}
private void InitChart()
{
System.Windows.Data.Binding indi = new System.Windows.Data.Binding("Case");
System.Windows.Data.Binding dep = new System.Windows.Data.Binding("Time");
dep.Mode = System.Windows.Data.BindingMode.OneWay;
indi.Mode = System.Windows.Data.BindingMode.OneWay;
AreaSeries ares = new AreaSeries();
ares.ItemsSource = Ready4LOS;
ares.IndependentValueBinding = dep;
ares.DependentValueBinding = indi;
ares.Title = "Ready4LOS";
DateTimeAxis dta = new DateTimeAxis();
dta.Interval = 10;
dta.IntervalType = DateTimeIntervalType.Minutes;
dta.Title = "Time";
dta.Orientation = AxisOrientation.X;
// dta.Minimum = DateTime.Now.AddMinutes(-90);
// dta.Maximum = DateTime.Now;
LinearAxis yaxis = new LinearAxis();
yaxis.Minimum = 0;
yaxis.Interval = 2;
yaxis.Title = "Case";
yaxis.Orientation = AxisOrientation.Y;
yaxis.ShowGridLines = true;
chart1.Axes.Add(yaxis);
chart1.Axes.Add(dta);
chart1.Series.Add(ares);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
LoadData();
chart1.UpdateLayout();
}
}
}
the data model is here
class Ready4LOS : INotifyPropertyChanged
{
int _case;
DateTime _time;
public int Case
{
get
{
return _case;
}
set
{
_case = value;
NotifyPropertyChanged("Case");
}
}
public DateTime Time
{
get
{
return _time;
}
set
{
_time = value;
NotifyPropertyChanged("Time");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
It loads perfectly when it starts as i've called the LoadData() in the beginning.
The problem is when i click on the refresh button it loads the data in the source object but the target object's data in not updated i.e. chart is not updated it remains the same as of initial data.
Use ObservableCollection<Ready4LOS>, not List<Ready4LOS>. ObservableCollection<> already implements INotifyPropertyChanged and also INotifyCollectionChanged. Your implementation of INotifyPropertyChanged for Ready4LOS may only be necessary if you're going to dynamically change values for Case and Time for existing Ready4LOS already in your collection.
Please assume this entire question deals in code, without any XAML.
I have a static ObservableCollection named myStaticList. It's a part of a non-static class named myClass.
public class myClass
{
public static ObservableCollection<CheckBoxStructure> myStaticList { get; set; }
static myClass()
{
myStaticList = new ObservableCollection<CheckBoxStructure>();
}
}
And the definition of CheckBoxStructure:
public class CheckBoxStructure
{
public string Description { get; set; }
public bool IsSelected { get; set; }
}
In addition, there's an array of checkboxes called checkBoxArray[], holding 3 elements. each checkbox has as content a textbox.
What I want to do is programmatically bind (two-way) these two, in such a manner that the IsChecked property of the checkboxes in the checkBoxArray[] array will bind to the IsSelected property of the myStaticList's CheckBoxStructure, and similarly so between the text of the textboxes inthe checkboxes' content and the Description property of the myStaticList's CheckBoxStructure.
In addition, I would like to avoid using loops, since it is preferable that this two lists will update each other if they change in size.
How is this possible?
Thanks!
Using XAML, an easy way would be to the declare an ItemsControl and a DataTemplate for it so that you can have a UserControl (CheckBox and TextBox inside) with its DataContext being a CheckBoxStructure. This way the bindings work between CheckBox.IsChecked and IsSelected property and between TextBox.Text and Description property.
If you need to this only in code then you would have to create same behavior (ItemsControl with a DataTemplate). You have at least 2 options
1.
DataTemplate template = new DataTemplate();
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(StackPanel));
template.VisualTree = factory;
FrameworkElementFactory childFactory = new FrameworkElementFactory(typeof(CheckBox));
childFactory.SetBinding(CheckBox.IsChecked, new Binding("IsSelected"));
factory.AppendChild(childFactory);
childFactory = new FrameworkElementFactory(typeof(TextBox));
childFactory.SetBinding(Label.ContentProperty, new Binding("Description"));
factory.AppendChild(childFactory);
2.
MemoryStream sr = null;
ParserContext pc = null;
string xaml = string.Empty;
xaml = "<DataTemplate><StackPanel><TextBlock Text="{Binding Description"/><CheckBox IsChecked="{Binding IsSelected"/></StackPanel></DataTemplate>";
sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
DataTemplate datatemplate = (DataTemplate)XamlReader.Load(sr, pc);
this.Resources.Add("dt", datatemplate);
Later edit, after discussion from comments; this example works only one way of binding but is easily to make it two ways. Please note that this is only a trivial example of a concept and is not complete: you need to modify the list classes to suit how you wish for objects to be paired, you may need to add more guards for corner cases, you may need to make it thread safe and so on...
First the basic binding objects:
class Binder
{
public Binder()
{
_bindings = new Dictionary<string, List<string>>();
}
private INotifyPropertyChanged _dataContext;
public INotifyPropertyChanged DataContext
{
get { return _dataContext; }
set
{
if (_dataContext != null)
{
_dataContext.PropertyChanged -= _dataContext_PropertyChanged;
}
_dataContext = value;
_dataContext.PropertyChanged += _dataContext_PropertyChanged;
}
}
void _dataContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_bindings.ContainsKey(e.PropertyName))
{
var bindableType = _dataContext.GetType();
var bindableProp = bindableType.GetProperty(e.PropertyName);
if (bindableProp == null)
{
return;
}
var binderType = this.GetType();
foreach (var binderPropName in _bindings[e.PropertyName])
{
var binderProp = binderType.GetProperty(binderPropName);
if (binderProp == null)
{
continue;
}
var value = bindableProp.GetValue(_dataContext);
binderProp.SetValue(this, value);
}
}
}
Dictionary<string, List<string>> _bindings;
public void AddBinding(string binderPropertyName, string bindablePropertyName)
{
if (!_bindings.ContainsKey(bindablePropertyName))
{
_bindings.Add(bindablePropertyName, new List<string>());
}
_bindings[bindablePropertyName].Add(bindablePropertyName);
}
}
class Bindable : INotifyPropertyChanged
{
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then the holding lists for them:
class BindableList<T> : List<T> where T : Bindable
{
public event Action<T> ItemAdded;
public new void Add(T item)
{
base.Add(item);
NotifyItemAdded(item);
}
private void NotifyItemAdded(T item)
{
if (ItemAdded != null)
{
ItemAdded(item);
}
}
}
class BinderList<T> : List<T> where T : Binder
{
public BinderList()
{
_bindingRules = new Dictionary<string, string>();
}
private BindableList<Bindable> _dataContextList;
public BindableList<Bindable> DataContextList
{
get { return _dataContextList; }
set
{
if (_dataContextList != null)
{
_dataContextList.ItemAdded -= _dataContextList_ItemAdded;
}
_dataContextList = value;
_dataContextList.ItemAdded += _dataContextList_ItemAdded;
}
}
void _dataContextList_ItemAdded(Bindable obj)
{
foreach (var pair in _bindingRules)
{
this[Count-1].AddBinding(pair.Key, pair.Value);
this[Count - 1].DataContext = obj;
}
}
private Dictionary<string, string> _bindingRules;
public void AddBindingRule(string binderPropertyName, string bindablePropertyName)
{
_bindingRules.Add(binderPropertyName, bindablePropertyName);
}
}
Now the actual classes with properties:
class BinderElement : Binder
{
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
}
class BindableElement : Bindable
{
private string _description;
public string Description
{
get
{
return _description;
}
set
{
_description = value;
NotifyPropertyChanged("Description");
}
}
}
And an example to use them:
static void Main(string[] args)
{
var bindableList = new BindableList<Bindable>();
var binderList = new BinderList<BinderElement>()
{
new BinderElement(),
new BinderElement()
};
binderList.DataContextList = bindableList;
binderList.AddBindingRule("Description", "Description");
bindableList.Add(new BindableElement());
bindableList.Add(new BindableElement());
((BindableElement)bindableList[1]).Description = "This should arrive in BinderElement Description property";
Console.WriteLine(binderList[1].Description);
Console.ReadLine();
}
I have created an ObservableCollection and binded that to my Listview. Before my items are loaded into the ListView they are being sorted using Linq and then added to the ListView:
//Get's the Items and sets it
public ObservableCollection<ItemProperties> ItemCollection { get; private set; }
//Orders the Items alphabetically using the Title property
DataContext = ItemCollection.OrderBy(x => x.Title);
<!--ItemCollection has been binded to the ListView-->
<ListView ItemsSource="{Binding}"/>
The problem I'm having is that I can't remove a selected item from the collection. The problem occurs only if I add the DataContext = ItemCollection.OrderBy(x => x.Title);. If it's DataContext = ItemCollection then I can delete the selected item.
My delete method gets activated once the ContextMenu (MenuFlyout) is being opened and the 'Delete' item is clicked. My delete method is:
private void btn_Delete_Click(object sender, RoutedEventArgs e)
{
var edit_FlyOut = sender as MenuFlyoutItem;
var itemProperties = edit_FlyOut.DataContext as ItemProperties;
ItemCollection.Remove(itemProperties);
}
This is my ItemProperties class:
public class ItemProperties : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ItemProperties() { }
private string m_Title;
public string Title
{
get { return m_Title; }
set
{
m_Title = value;
OnPropertyChanged("Title");
}
}
private string m_Post;
public string Post
{
get { return m_Post; }
set
{
m_Post = value;
OnPropertyChanged("Post");
}
}
private string m_Modified;
public string Modified
{
get { return m_Modified; }
set
{
m_Modified = value;
OnPropertyChanged("Modified");
}
}
private string m_ID;
public string ID
{
get { return m_ID; }
set
{
m_ID = value;
OnPropertyChanged("ID");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
}
Edit
How I load my Items:
public async void GetList()
{
var AppStorage = ApplicationData.Current.LocalFolder;
var noteFolders = await AppStorage.GetFolderAsync(#"folder\files\");
var Folders = await noteFolders.GetFoldersAsync();
ItemCollection = new ObservableCollection<ItemProperties>();
foreach (var noteFolder in Folders)
{
ItemCollection.Add(new ItemProperties { Title = readTitle, Post = readBody, ID = noteFolder.Name, Modified = timeFormat });
}
//code which readers and adds text to the properties...
DataContext = ItemCollection.OrderBy(x => x.Title);
}
You can get selected item from ListView and delete it:
if (lvElement.SelectedIndex == -1) return;
ItemProperties selectedProperty = (ItemProperties)lvElement.SelectedItem;
// remove selectedProperty from original collection
My solution is:
ItemCollection = new ObservableCollection<ItemProperties>(ItemCollection.OrderBy(a => a.Title));
DataContext = ItemCollection;
It seemed like I had to reinitialize the ItemCollection to a new ObservableCollection and order it all in one go rather than loading the items then sorting. What this did was it had one list which was first adding the items then the other list which had to sort. To avoid this I had do to it all in one go. Above helped me. I got it from here.
I am new to Xamarin.Forms and the binding concept. Can someone please tell me why this is not working? The name of the object itself is changing when I'm pressing the button. Why wont the Text-property update?
var red = new Label
{
Text = todoItem.Name,
BackgroundColor = Color.Red,
Font = Font.SystemFontOfSize (20)
};
red.SetBinding (Label.TextProperty, "Name");
Button button = new Button
{
Text = String.Format("Tap for name change!")
};
button.Clicked += (sender, args) =>
{
_todoItem.Name = "Namie " + new Random().NextDouble();
};
The todoItem is an object of the class below. The notification itself works, I am almost positive. I guess there's something wrong with my binding, or I am missing something with this concept.
public class TodoItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string _name;
public string Name
{
get { return _name; }
set
{
if (value.Equals(_name, StringComparison.Ordinal))
{
// Nothing to do - the value hasn't changed;
return;
}
_name = value;
OnPropertyChanged();
}
}
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You need to set the Label's BindingContext:
red.BindingContext = _todoItem;
I have a object whose data members have INotifypropertychange event implemented.I want to maintain a list of objects separately where I do not want to reflect the property change.How can I do it
Wrapping/proxying example:
class MyItem : INPC
{
public string Name { get { ... } set { this.name = value; raisePropChanged("Name") } } ....
}
var item = new MyItem();
collection.Add(item);
item.Name = "John"; // notifies whoever listens on collection
class MyItemWrapper
{
private MyItem theBrain;
public string Name { get{return theBrain.Name;} set{theBrain.Name = value;}}
}
var item = new MyItem();
var wrapped = new MyItemWrapper { theBrain = item };
collectionOne.Add(item);
collectionTwo.Add(wrapped);
item.Name = "John";
// notifies whoever listens on collectionOne
// but whoever listens on collectionTwo will not get any notification
// since "wrapper" does not notify about anything.
// however, since wrapper forwards everything to 'brain':
var name = wrapped.Name; // == "John"
Call the function GetDeepCopy() to get your object which would not raise INPC.
public class ValidationModel : INotifyPropertyChanged
{
private string _validationName;
public string validationName
{
get { return _validationName; }
set { _validationName = value; NotifyPropertyChanged("ValidationName"); }
}
public ValidationModel GetDeepCopy()
{
var model = new ValidationModel();
model.validationName = validationName;
return model;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
}