I am receiving a NullReferenceException when trying to add a record to a table using LINQ on WP7. I am relatively new to C#/LINQ so I have copied one of my existing methods which works OK, but now I cannot make it work for the new record. The code is below;
private ObservableCollection<DBControl.Categories> _category;
public ObservableCollection<DBControl.Categories> Category
{
get
{
return _category;
}
set
{
if (_category != value)
{
_category = value;
NotifyPropertyChanged("Category");
}
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
string TestCategory = "Cars";
// Create a new to-do item based on the text box.
DBControl.Categories newCat = new DBControl.Categories { CategoryDesc = TestCategory };
//CategoryDesc
// Add a to-do item to the observable collection.
**Category.Add(newCat);**
// Add a to-do item to the local database.
BoughtItemDB.Category.InsertOnSubmit(newCat);
BoughtItemDB.SubmitChanges();
}
The line of code which is giving me the error is Category.Add(newCat)
As far as I can tell everything looks OK which probably means I've made a daft mistake (again).
Any help is much appreciated.
The table definition is below;
[Table(Name = "Categories")]
public class Categories : INotifyPropertyChanged, INotifyPropertyChanging
{
// Define ID: private field, public property and database column.
private int _categoryId;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int CategoryId
{
get
{
return _categoryId;
}
set
{
if (_categoryId != value)
{
NotifyPropertyChanging("CategoryId");
_categoryId = value;
NotifyPropertyChanged("CategoryId");
}
}
}
// Define item category: private field, public property and database column.
private string _categoryDesc;
[Column]
public string CategoryDesc
{
get
{
return _categoryDesc;
}
set
{
if (_categoryDesc != value)
{
NotifyPropertyChanging("CategoryDesc");
_categoryDesc = value;
NotifyPropertyChanged("CategoryDesc");
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify the page that a data context property changed
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify the data context that a data context property is about to change
private void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}
You need to initialize Category or _category. It's not getting set to any value, so you're trying to Add() to a non-initialized object.
Related
I have an object which contain a list of this object.
when the user click on a button I want to add a new object
to one of the object in the list per an index. for example if the index is equal to 2
I want to add an object to the object.object.object(new object()).
how am I trying to get the correct object inside the inifinte object.
any better method then this one?
private void addToCommand(int indexTreeView, ClassForTest.CommadStructure CS)
{
if (_indexTreeView == 0)
{
_OCommandStructure.Add(CS);
}
else if (_indexTreeView==1)
{
_OCommandStructure[_OCommandStructure.Count - 1].SubCommandStructure.Add(CS);
}
else if (_indexTreeView == 2)
{
_OCommandStructure[_OCommandStructure.Count - 1].SubCommandStructure[_OCommandStructure[_OCommandStructure.Count - 1].SubCommandStructure.Count-1].SubCommandStructure.Add(CS);
}
}
public class CommadStructure : INotifyPropertyChanged
{
private string _Controller;;
private ObservableCollection<CommadStructure> _SubCommandStructure;
public string NameOfCommand
{
get => _NameOfCommand;
set
{
_NameOfCommand = value;
NotifyPropertyChanged();
}
}
public ObservableCollection<CommadStructure> SubCommandStructure
{
get => _SubCommandStructure;
set
{
_SubCommandStructure = value;
// NotifyPropertyChanged();
}
}
public ObservableCollection<object> Items
{
get
{
ObservableCollection<object> childNodes = new ObservableCollection<object>();
foreach (var group in this.SubCommandStructure)
childNodes.Add(group);
return childNodes;
}
}
public CommadStructure()
{
NameOfCommand = "";
}
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));
}
}
}
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
I do have a WPF binding question here.
Following Setup:
I do have a class (ActionService) having a name and a ObservableCollection of subitems (also a class named Step). A Step has a flag that shows if the Step is allready done (IsDone).
I bind a form to the ActionService and display all kind of things.
Everything works as aspected and i have just the essential parts in my snippet.
Now I need one more thing that i can not get work. I want the ActionService to know by binding how many of its Steps are open (IsDone == false). I you open a childform with one of the steps and change the IsDone-State, the mother form should get the new count on the fly.
And I'm to dumb to get a correct solution on the way ;-)
Thanks for your help or a best practise.
public class ActionService : BaseObject
{
public ActionService()
{
}
private String name;
public String Name
{
get { return this.name; }
set
{
this.name = value;
raisePropertyChanged("Name");
}
}
public ObservableCollection<Step> actionsteps;
public ObservableCollection<Step> ActionSteps
{
get { return this.actionsteps; }
set
{
this.actionsteps = value;
raisePropertyChanged("ActionSteps");
}
}
}
public class Step : BaseObject
{
public Step()
{
}
private String description;
public String Description
{
get { return this.description; }
set
{
this.description = value;
raisePropertyChanged("Description");
}
}
private Boolean isdone;
public Boolean IsDone
{
get { return this.isdone; }
set
{
this.isdone = value;
raisePropertyChanged("IsDone");
}
}
}
public class BaseObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void raisePropertyChanged(String parPropertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(parPropertyName));
}
}
}
You can create a new property in your ActionService class:
public bool IsDone
{
get
{
return ActionSteps.Count(x => x.IsDone) == ActionSteps.Count;
}
}
If the count of Steps in the ActionSteps list where the IsDone property is true is equal to the number of Steps in the ActionSteps list, then return true, else, return false.
To subscribe to the Steps property changed event, when you add an item to the collection, you simply need to subscribe to the PropertyChanged event:
//Create the item and subscribe to propertychanged.
Step item = new Step();
item.PropertyChanged += item_PropertyChanged;
//Add the item to the list.
ActionSteps.Add(item);
And your method will look like this:
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsDone")
raisePropertyChanged("IsDone");
}
I don't understand why when I update a object, my bound controls do not update.
The data displays fine initially, but when I want to refresh the data displayed in the UI nothing happens when I update the object. The object updates fine. The ViewModel does use INotifyPropertyChanged on all fields.
However if I update individual items directly, I can update my UI. As commented below.
I guess I've made a school boy error somewhere here?
UPDATE: I've added the model to the question. While I understand the answers, I don't understand how to implement it. Attempted to implement a collection changed event without success. Can I have some pointers please?
public partial class CisArrivalsPanel : UserControl
{
private ApiDataArrivalsDepartures _theArrivalsDepartures;
public CisArrivalsPanel()
{
InitializeComponent();
_theArrivalsDepartures = new ApiDataArrivalsDepartures();
_theArrivalsDepartures = MakeQuery.LiveTrainArrivals("London Kings Cross");
this.DataContext = _theArrivalsDepartures;
ListBoxArr.ItemsSource = _theArrivalsDepartures.StationMovementList;
}
void Reload()
{
//This does not update the UI**
_theArrivalsDepartures = MakeQuery.LiveTrainArrivals("London Paddington");
//However this (when uncommented, and I comment out the above line) does update the UI**
//_theArrivalsDepartures.StationMovementList[0].OriginName = "test";
//_theArrivalsDepartures.StationMovementList[0].Platform = "0";
//_theArrivalsDepartures.StationMovementList[0].BestArrivalEstimateMins = "999";
//_theArrivalsDepartures.StationName = "test";
}
private void StationHeader_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Reload();
Debug.WriteLine(_theArrivalsDepartures.StationName);
foreach (var a in _theArrivalsDepartures.StationMovementList)
{
Debug.WriteLine(a.OriginName);
Debug.WriteLine(a.BestArrivalEstimateMins);
}
}
}
EDIT : Added Model
public class ApiDataArrivalsDepartures : INotifyPropertyChanged
{
private string _stationName;
[JsonProperty(PropertyName = "station_name")]
public string StationName {
get
{
return _stationName;
}
set
{
_stationName = value;
NotifyPropertyChanged("StationName");
}
}
private List<StationListOfMovements> _stationMovementList;
public List<StationListOfMovements> StationMovementList
{
get
{
return _stationMovementList;
}
set
{
_stationMovementList = value;
NotifyPropertyChanged("StationMovementList");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class StationListOfMovements : INotifyPropertyChanged
{
private string _originName;
[JsonProperty(PropertyName = "origin_name")]
public string OriginName {
get
{
return _originName;
}
set
{
_originName = value;
NotifyPropertyChanged("OriginName");
}
}
[JsonProperty(PropertyName = "destination_name")]
public string DestinationName { get; set; }
private string _platform;
[JsonProperty(PropertyName = "Platform")]
public string Platform {
get
{
return _platform;
}
set
{
_platform = value;
NotifyPropertyChanged("Platform");
}
}
private string _bestArrivalEstimateMins;
[JsonProperty(PropertyName = "best_arrival_estimate_mins")]
public string BestArrivalEstimateMins {
get
{
return _bestArrivalEstimateMins;
}
set
{
_bestArrivalEstimateMins = value;
NotifyPropertyChanged("BestArrivalEstimateMins");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
There are two pieces here pertaining to your collection (technically three):
If you want a new collection to propagate, the collection property has to raise PropertyChanged (sounds like it does)
If you want add/remove on the collection to propagate, you need to use a collection that implements INotifyCollectionChanged. ObservableCollection is a good choice.
If you want changes to the items in the container to propagate, then those items need to implement INotifyPropertyChanged and raise the PropertyChanged event.
Make sure all those are covered, and the changes should appear on the UI as you expect.
You should update the DataContext and ItemsSource too.
void Reload()
{
//This does not update the UI**
_theArrivalsDepartures = MakeQuery.LiveTrainArrivals("London Paddington");
DataContext = theArrivalsDepartures;
ListBoxArr.ItemsSource = _theArrivalsDepartures.StationMovementList;
}
Use for the collection ObservableCollection , this class notify the ui when change to the collection occurred
your reload function works because the there is PropertyChanged on all the fields include this one
it notify the ui and reload the correct collection
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);
}
}