I had some problems with serialization of my classes implementing INotifyPropertyChanged events. (Binary Serialization of ViewModel (ObservableCollection))
Solved as recommended in SerializationException when serializing instance of a class which implements INotifyPropertyChanged
Now I have a problem with the deserialization. When doing it like
BinaryFormatter formatter = new BinaryFormatter();
FileStream fs_open = new FileStream(#"C:\Users\vm_user\Documents\Visual Studio 2015\testbin.txt", FileMode.Open);
ViewModels.MainViewModel mvm1 = (ViewModels.MainViewModel)formatter.Deserialize(fs_open);
fs_open.Close();
The deserialized objects do not fire any events anymore.
What do I have to do to get the same functionality as before the serialization? Right now I do it with this:
BinaryFormatter formatter = new BinaryFormatter();
ViewModels.MainViewModel mvm1 = new ViewModels.MainViewModel();
FileStream fs_open = new FileStream(#"C:\Users\vm_user\Documents\Visual Studio 2015\testbin.txt", FileMode.Open);
foreach (ViewModels.WatchedFile file in ( (ViewModels.MainViewModel)formatter.Deserialize(fs_open) ).WatchedFiles)
{
ViewModels.WatchedFile wf = new ViewModels.WatchedFile(file.Name, file.Path, file.Tags, new ObservableCollection<ViewModels.WatchedFile>());
foreach (ViewModels.WatchedFile subs in file.Subs)
{
wf.Subs.Add(refeshLoadedFiles(subs));
}
mvm1.WatchedFiles.Add(wf);
}
fs_open.Close();
and refreshLoadedFiles:
private ViewModels.WatchedFile refeshLoadedFiles( ViewModels.WatchedFile fileSource)
{
ViewModels.WatchedFile wf = new ViewModels.WatchedFile(fileSource.Name, fileSource.Path, fileSource.Tags, new ObservableCollection<ViewModels.WatchedFile>());
foreach (ViewModels.WatchedFile subs in fileSource.Subs)
{
wf.Subs.Add(refeshLoadedFiles(subs));
}
return wf;
}
But this canĀ“t be the goal of serialization, can it? Beacause if I do it this way I can write my information into any textfile with simple text stating the type and then all the rest...
Thanks for your help.
This is the ViewModel (edit):
namespace WatchedFile.ViewModels
{
[Serializable()]
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
[Serializable()]
public class WatchedFile : ViewModelBase
{
#region Name Property
private String _name = default(String);
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged();
}
}
}
#endregion Name Property
#region Path Property
private String _path = default(String);
public String Path
{
get { return _path; }
set
{
setDisplayImage(value);
if (value != _path)
{
_path = value;
OnPropertyChanged();
}
}
}
#endregion Path Property
#region Tags Property
private ObservableCollection<Tag> _tags = new ObservableCollection<Tag>();
public ObservableCollection<Tag> Tags
{
get { return _tags; }
protected set
{
if (value != _tags)
{
_tags = value;
OnPropertyChanged();
}
}
}
#endregion Tags Property
#region Subs Property
private ObservableCollection<WatchedFile> _subs = new ObservableCollection<WatchedFile>();
public ObservableCollection<WatchedFile> Subs
{
get { return _subs; }
protected set
{
setDisplayImage(Path);
if (value != _subs)
{
_subs = value;
_subs.CollectionChanged += _subs_CollectionChanged;
OnPropertyChanged();
}
}
}
private void _subs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Subs = Sort(Subs);
}
#endregion Subs Property
[NonSerialized()]
private BitmapImage _displayImage = default(BitmapImage);
public BitmapImage DisplayImage
{
get { return _displayImage; }
protected set
{
if (value != _displayImage)
{
_displayImage = value;
OnPropertyChanged();
}
}
}
public WatchedFile(): this(string.Empty,string.Empty,new ObservableCollection<Tag>(),new ObservableCollection<WatchedFile>())
{
}
public WatchedFile(String name, String path, ObservableCollection<Tag> tags, ObservableCollection<WatchedFile> subitems)
{
Subs = WatchedFile.Sort(subitems);
Name = name;
Path = path;
Tags = tags;
}
public static ObservableCollection<WatchedFile> Sort(ObservableCollection<WatchedFile> files)
{
if (files == null)
return files;
ObservableCollection<WatchedFile> filesReturn = new ObservableCollection<ViewModels.WatchedFile>();
WatchedFile[] sortedArray = files.ToArray();
WatchedFile temp;
for (int j = 1; j <= sortedArray.Length - 1; j++)
{
for (int i = j; i > 0; i--)
{
if (sortedArray[i].Subs != null && sortedArray[i].Subs.Count > 1)
{
ObservableCollection<WatchedFile> subs = Sort(sortedArray[i].Subs);
sortedArray[i].Subs.Clear();
foreach (WatchedFile f in subs)
sortedArray[i].Subs.Add(f);
}
if (sortedArray[i - 1].Subs != null && sortedArray[i - 1].Subs.Count > 1)
{
ObservableCollection<WatchedFile> subs = Sort(sortedArray[i - 1].Subs);
sortedArray[i - 1].Subs.Clear();
foreach (WatchedFile f in subs)
sortedArray[i - 1].Subs.Add(f);
}
if (( sortedArray[i].Name ).CompareTo(sortedArray[i - 1].Name) == -1)
{
temp = sortedArray[i];
sortedArray[i] = sortedArray[i - 1];
sortedArray[i - 1] = temp;
}
else
break;
}
}
filesReturn.Clear();
foreach (WatchedFile f in sortedArray)
filesReturn.Add(f);
return filesReturn;
}
}
[Serializable()]
public class Tag
{
public Tag(String value)
{
Value = value;
}
public String Value { get; private set; }
}
[Serializable()]
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
}
#region WatchedFiles Property
private ObservableCollection<WatchedFile> _watchedFiles = new ObservableCollection<WatchedFile>();
public ObservableCollection<WatchedFile> WatchedFiles
{
get { return _watchedFiles; }
protected set
{
if (value != _watchedFiles)
{
_watchedFiles =WatchedFile.Sort(value);// value;
_watchedFiles.CollectionChanged += _watchedFiles_CollectionChanged;
OnPropertyChanged();
}
}
}
private void _watchedFiles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
WatchedFiles = WatchedFile.Sort(WatchedFiles);
}
#endregion WatchedFiles Property
}
}
They don't fire because nothing is subscribed to them. In WPF, the framework will subscribe during binding in the background when the view or control is loaded. Since you're doing this at a different stage in the pipeline, you're going to have to figure out a better way to load the model at the right stage so that the subscriptions occur again. Hard to say without seeing more, but this is very smelly. Is this a control or the full view's datacontext?
At the very least, force a rebind of the model. Otherwise, maybe serialize a dto instead and inject into the viewmodel and use it to restore state before loading the view, or give the main view model a method that takes the dto and rebinds it to an observable property. Like I said, it's hard to say without seeing what you're really doing.
Related
How would I be able to change the data in my ObservableCollection without adding or using add to my collection?
var items = await service.GetTrucksAsync();
foreach (var item in items)
{
MyCollection.Add(new TruckItems
{
TruckId = item.TruckId,
TruckQuoteId = item.QuoteId,
TruckPhaseId = item.CurrentPhaseId,
TruckChassisManufacturer = item.ChassisManufacturer,
TruckChassisModel = item.ChassisModel,
TruckStatus = item.Status,
TruckJobNumber = item.JobNumbers,
TruckAddedBy = item.AddedBy,
TruckClientName = item.ClientName,
TruckClientSurname = item.ClientSurname,
TruckClientDetail = item.ClientDetail,
TruckCurrentPhase = item.CurrentPhase
});
}
dgViewProjects.ItemsSource = MyCollection;
I do not want to clear the collection and the add data again, as it causes my datagrid's UI to 'flicker' (clear and load new data again). I need it to be smooth as hell. :)
EDIT: INotifyPropertyChanged is implemented in my class
public class TruckItems : INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You TruckItems class needs to implement the INotifyPropertyChanged interface.
For example:
public class TruckItems : INotifyPropertyChanged
{
private int _truckQuoteId;
public int TruckQuoteId
{
get { return _truckQuoteId; }
set
{
if(value != _truckQuoteId)
{
value = _truckQuoteId;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TruckQuoteId));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
If you're unable to alter the TruckItems class (DAL), you need to create a new class and copy the properties. (for example using a Mapper)
update:
The problem you are facing is, that you're creating a new instance of TruckItems.
var items = await service.GetTrucksAsync();
foreach (var item in items)
{
var truckItem = MyCollection.FirstOrDefault(i => i.TruckId == item.TruckId);
bool isNew = false;
if(truckItem == null)
{
truckItem = new TruckItems();
isNew = true;
}
truckItem.TruckId = item.TruckId;
truckItem.TruckQuoteId = item.QuoteId;
truckItem.TruckPhaseId = item.CurrentPhaseId;
truckItem.TruckChassisManufacturer = item.ChassisManufacturer;
truckItem.TruckChassisModel = item.ChassisModel;
truckItem.TruckStatus = item.Status;
truckItem.TruckJobNumber = item.JobNumbers;
truckItem.TruckAddedBy = item.AddedBy;
truckItem.TruckClientName = item.ClientName;
truckItem.TruckClientSurname = item.ClientSurname;
truckItem.TruckClientDetail = item.ClientDetail;
truckItem.TruckCurrentPhase = item.CurrentPhase;
if(isNew )
MyCollection.Add(truckItem);
}
I am trying to create a SwitchCell with a list of Elements.
Eventhough I found out how to do that with a plain string-List thanks to stackoverflow I can't find out what I'm doing wrong when I try to bind the Cell-Properties to a self-made struct.
This is my code:
public class RestaurantFilter
{
public List<FilterElement> Types;
public RestaurantFilter(List<string> types)
{
Types = new List<FilterElement>();
foreach (string type in types)
Types.Add(new FilterElement { Name = type, Enabled = false });
}
}
public struct FilterElement
{
public string Name;
public bool Enabled;
}
public FilterPage()
{
List<string> l = new List<string>(new string[] { "greek", "italian", "bavarian" });
RestaurantFilter filter = new RestaurantFilter(l);
ListView types = new ListView();
types.ItemTemplate = new DataTemplate(() =>
{
var cell = new SwitchCell();
cell.SetBinding(SwitchCell.TextProperty, "Name");
cell.SetBinding(SwitchCell.IsEnabledProperty, "Enabled");
return cell;
});
types.ItemsSource = filter.Types;
Content = types;
}
But the SwitchCell's in the Application do not show the Name or the Boolean.
About the IsEnabledProperty - there seem to be a knonw bug with the IsEnabled property that will be fixed in the Xamarin.Forms 2.3.0-pre1 release so that might be related to your case:
https://bugzilla.xamarin.com/show_bug.cgi?id=25662
About the Name property - try changing your FilterElement struct to a class with properties and PropertyChangedEventHandler like this and it will work:
public class FilterElement
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
private bool _enabled;
public bool Enabled
{
get { return _enabled; }
set
{
_enabled = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Enabled"));
}
}
}
}
That way you will be able to update the Types list and it will automatically update the ListView.
By the way, if you want to turn the filter on or off based on your ViewModels (not enable or disable it), you need to use the OnProperty for the binding:
https://developer.xamarin.com/api/field/Xamarin.Forms.SwitchCell.OnProperty/
Say I have a BindingList<Person> where Person has a public string property called Name. Is there a way to effectively (if not directly) bind to the Current property (which is a Person object) and then index into the Name property?
I'm imagining a binding set up like
nameLabel.DataBindings.Add(
new Binding("Text", this.myBindingListSource, "Current.Name", true));
or
nameLabel.DataBindings.Add(
new Binding("Text", this.myBindingListSource.Current, "Name", true));
Both of these approaches produce runtime errors.
Currently, I am simply subscribing to the BindingList's CurrentChanged event and handling updates in there. This works but I would prefer the DataBinding approach if possible.
Using the NestedBindingProxy class provided below, you can do
nameLabel.DataBindings.Add(
new Binding("Text", new NestedBindingProxy(this.myBindingListSource, "Current.Name"), true));
Below is the c# code for NestedBindingProxy. The problem with WinForms data binding is it doesn't detect value changes when you use a navigation path that contains multiple properties. WPF does this though. So I created the class NestedBindingProxy that does the change detection and it exposes a property called "Value" that the windows binding can bind too. Whenever any property changes in the navigation path, the notify property changed event will fire for the "Value" property.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace WindowsFormsApplication4
{
public sealed class NestedBindingProxy : INotifyPropertyChanged
{
class PropertyChangeListener
{
private readonly PropertyDescriptor _prop;
private readonly WeakReference _prevOb = new WeakReference(null);
public event EventHandler ValueChanged;
public PropertyChangeListener(PropertyDescriptor property)
{
_prop = property;
}
public object GetValue(object obj)
{
return _prop.GetValue(obj);
}
public void SubscribeToValueChange(object obj)
{
if (_prop.SupportsChangeEvents)
{
_prop.AddValueChanged(obj, ValueChanged);
_prevOb.Target = obj;
}
}
public void UnsubsctribeToValueChange()
{
var prevObj = _prevOb.Target;
if (prevObj != null)
{
_prop.RemoveValueChanged(prevObj, ValueChanged);
_prevOb.Target = null;
}
}
}
private readonly object _source;
private PropertyChangedEventHandler _subscribers;
private readonly List<PropertyChangeListener> _properties = new List<PropertyChangeListener>();
private readonly SynchronizationContext _synchContext;
public event PropertyChangedEventHandler PropertyChanged
{
add
{
bool hadSubscribers = _subscribers != null;
_subscribers += value;
bool hasSubscribers = _subscribers != null;
if (!hadSubscribers && hasSubscribers)
{
ListenToPropertyChanges(true);
}
}
remove
{
bool hadSubscribers = _subscribers != null;
_subscribers -= value;
bool hasSubscribers = _subscribers != null;
if (hadSubscribers && !hasSubscribers)
{
ListenToPropertyChanges(false);
}
}
}
public NestedBindingProxy(object source, string nestedPropertyPath)
{
_synchContext = SynchronizationContext.Current;
_source = source;
var propNames = nestedPropertyPath.Split('.');
Type type = source.GetType();
foreach (var propName in propNames)
{
var prop = TypeDescriptor.GetProperties(type)[propName];
var propChangeListener = new PropertyChangeListener(prop);
_properties.Add(propChangeListener);
propChangeListener.ValueChanged += (sender, e) => OnNestedPropertyChanged(propChangeListener);
type = prop.PropertyType;
}
}
public object Value
{
get
{
object value = _source;
foreach (var prop in _properties)
{
value = prop.GetValue(value);
if (value == null)
{
return null;
}
}
return value;
}
}
private void ListenToPropertyChanges(bool subscribe)
{
if (subscribe)
{
object value = _source;
foreach (var prop in _properties)
{
prop.SubscribeToValueChange(value);
value = prop.GetValue(value);
if (value == null)
{
return;
}
}
}
else
{
foreach (var prop in _properties)
{
prop.UnsubsctribeToValueChange();
}
}
}
private void OnNestedPropertyChanged(PropertyChangeListener changedProperty)
{
ListenToPropertyChanges(false);
ListenToPropertyChanges(true);
var subscribers = _subscribers;
if (subscribers != null)
{
if (_synchContext != SynchronizationContext.Current)
{
_synchContext.Post(delegate { subscribers(this, new PropertyChangedEventArgs("Value")); }, null);
}
else
{
subscribers(this, new PropertyChangedEventArgs("Value"));
}
}
}
}
}
Try this:
Binding bind = new Binding("Text", myBindingListSource, "Current");
bind.Format += (s,e) => {
e.Value = e.Value == null ? "" : ((Person)e.Value).Name;
};
nameLabel.DataBindings.Add(bind);
I haven't tested it but it should work and I've been waiting for the feedback from you.
I stumbled across this by accident (four years after the original post), and after a quick read, I find that the accepted answer appears to be really over-engineered in respect to the OP's question.
I use this approach, and have posted an answer here to (hopefully) help anyone else who has the same problem.
The following should work (if infact this.myBindingListSource implements IBindingList)
I would just create a binding source to wrap my binding list (in my Form/View)
BindingSource bindingSource = new BindingSource(this.myBindingListSource, string.Empty);
And then just bind to the binding source like this:
nameLabel.DataBindings.Add(new Binding("Text", bindingSource, "Name"));
I think the OP's original code didn't work because there is no member called 'Current' on a BindingList (unless the OP has some sort of specialised type not mentioned).
I am attempting to set the value of a single property for a POCO object, and it's looking like Entity is doing a retrieve of every record in the database table from which the POCO object was originally retrieved. Here's the code:
public void DeleteFile(int fileid)
{
var context = GetContext(myTrans, false);
FILE pocofile = (from f in context.FILES.All()
where f.File_Id == fileId
select f).FirstOrDefault();
// the following line causes a retrieve of 60,000 records
pocofile.State_Id = Global.DeletedStateId // global constant
// additional code that is eventually reached after about 10 minutes
}
We have a FILES table that has a State_Id column that is mapped to a STATE table. So when I attempt to set the State_Id property above, it seems to set the first file ok, but judging by the breakpoints it's hitting in the FILE poco class, it looks like it's doing a retrieve for every file in the db once it sets the State_Id from the outset.
The FILE.cs class is also pasted below for reference. It's basically hitting a lot of the getters and setters in a 60,000 long loop, and if I set a breakpoint on one of them at any given time and check the File_Id in the debugger, it's got a new File_Id every time, which is how I know it's looping through all of them.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace FORTRESSPOCO
{
public partial class FILE
{
#region Primitive Properties
public virtual int File_Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual Nullable<System.DateTime> Expiration_Date
{
get;
set;
}
public virtual bool Is_Directory
{
get;
set;
}
public virtual string Path
{
get;
set;
}
public virtual int Data_Asset_Id
{
get { return _data_Asset_Id; }
set
{
if (_data_Asset_Id != value)
{
if (Data_Asset != null && Data_Asset.Data_Asset_ID != value)
{
Data_Asset = null;
}
_data_Asset_Id = value;
}
}
}
private int _data_Asset_Id;
public virtual int State_Id
{
get { return _state_Id; }
set
{
if (_state_Id != value)
{
if (STATE != null && STATE.State_Id != value)
{
STATE = null;
}
_state_Id = value;
}
}
}
private int _state_Id;
#endregion
#region Navigation Properties
public virtual Data_Asset Data_Asset
{
get { return _data_Asset; }
set
{
if (!ReferenceEquals(_data_Asset, value))
{
var previousValue = _data_Asset;
_data_Asset = value;
FixupData_Asset(previousValue);
}
}
}
private Data_Asset _data_Asset;
public virtual ICollection<File_DateTime_Attribute> File_DateTime_Attribute
{
get
{
if (_file_DateTime_Attribute == null)
{
var newCollection = new FixupCollection<File_DateTime_Attribute>();
newCollection.CollectionChanged += FixupFile_DateTime_Attribute;
_file_DateTime_Attribute = newCollection;
}
return _file_DateTime_Attribute;
}
set
{
if (!ReferenceEquals(_file_DateTime_Attribute, value))
{
var previousValue = _file_DateTime_Attribute as FixupCollection<File_DateTime_Attribute>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupFile_DateTime_Attribute;
}
_file_DateTime_Attribute = value;
var newValue = value as FixupCollection<File_DateTime_Attribute>;
if (newValue != null)
{
newValue.CollectionChanged += FixupFile_DateTime_Attribute;
}
}
}
}
private ICollection<File_DateTime_Attribute> _file_DateTime_Attribute;
public virtual ICollection<File_Int_Attribute> File_Int_Attribute
{
get
{
if (_file_Int_Attribute == null)
{
var newCollection = new FixupCollection<File_Int_Attribute>();
newCollection.CollectionChanged += FixupFile_Int_Attribute;
_file_Int_Attribute = newCollection;
}
return _file_Int_Attribute;
}
set
{
if (!ReferenceEquals(_file_Int_Attribute, value))
{
var previousValue = _file_Int_Attribute as FixupCollection<File_Int_Attribute>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupFile_Int_Attribute;
}
_file_Int_Attribute = value;
var newValue = value as FixupCollection<File_Int_Attribute>;
if (newValue != null)
{
newValue.CollectionChanged += FixupFile_Int_Attribute;
}
}
}
}
private ICollection<File_Int_Attribute> _file_Int_Attribute;
public virtual ICollection<File_String_Attribute> File_String_Attribute
{
get
{
if (_file_String_Attribute == null)
{
var newCollection = new FixupCollection<File_String_Attribute>();
newCollection.CollectionChanged += FixupFile_String_Attribute;
_file_String_Attribute = newCollection;
}
return _file_String_Attribute;
}
set
{
if (!ReferenceEquals(_file_String_Attribute, value))
{
var previousValue = _file_String_Attribute as FixupCollection<File_String_Attribute>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupFile_String_Attribute;
}
_file_String_Attribute = value;
var newValue = value as FixupCollection<File_String_Attribute>;
if (newValue != null)
{
newValue.CollectionChanged += FixupFile_String_Attribute;
}
}
}
}
private ICollection<File_String_Attribute> _file_String_Attribute;
public virtual STATE STATE
{
get { return _sTATE; }
set
{
if (!ReferenceEquals(_sTATE, value))
{
var previousValue = _sTATE;
_sTATE = value;
FixupSTATE(previousValue);
}
}
}
private STATE _sTATE;
#endregion
#region Association Fixup
private void FixupData_Asset(Data_Asset previousValue)
{
if (previousValue != null && previousValue.FILES.Contains(this))
{
previousValue.FILES.Remove(this);
}
if (Data_Asset != null)
{
if (!Data_Asset.FILES.Contains(this))
{
Data_Asset.FILES.Add(this);
}
if (Data_Asset_Id != Data_Asset.Data_Asset_ID)
{
Data_Asset_Id = Data_Asset.Data_Asset_ID;
}
}
}
private void FixupSTATE(STATE previousValue)
{
if (previousValue != null && previousValue.FILES.Contains(this))
{
previousValue.FILES.Remove(this);
}
if (STATE != null)
{
if (!STATE.FILES.Contains(this))
{
STATE.FILES.Add(this);
}
if (State_Id != STATE.State_Id)
{
State_Id = STATE.State_Id;
}
}
}
private void FixupFile_DateTime_Attribute(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (File_DateTime_Attribute item in e.NewItems)
{
item.FILE = this;
}
}
if (e.OldItems != null)
{
foreach (File_DateTime_Attribute item in e.OldItems)
{
if (ReferenceEquals(item.FILE, this))
{
item.FILE = null;
}
}
}
}
private void FixupFile_Int_Attribute(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (File_Int_Attribute item in e.NewItems)
{
item.FILE = this;
}
}
if (e.OldItems != null)
{
foreach (File_Int_Attribute item in e.OldItems)
{
if (ReferenceEquals(item.FILE, this))
{
item.FILE = null;
}
}
}
}
private void FixupFile_String_Attribute(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (File_String_Attribute item in e.NewItems)
{
item.FILE = this;
}
}
if (e.OldItems != null)
{
foreach (File_String_Attribute item in e.OldItems)
{
if (ReferenceEquals(item.FILE, this))
{
item.FILE = null;
}
}
}
}
#endregion
}
}
Is there any logical reason for this behavior? Thanks.
Yes, there is a logical reason for this behaviour. But it's not your fault in my opinion but the EF 4.0 POCO T4 template's fault. The problem is the combination of those autogenerated Fixup... methods together with lazy loading.
The following happens:
You set the FK property:
pocofile.State_Id = Global.DeletedStateId;
It calls the State_Id setter:
if (_state_Id != value)
{
if (STATE != null && STATE.State_Id != value)
{
STATE = null;
}
_state_Id = value;
}
The first if condition is true unless you set the same FK value that the loaded entity has already (no change of FK). The second if condition will be true because lazy loading will load the STATE from the database in the expression STATE != null and because the STATE in the database cannot be null since your relationship is not nullable (State_Id is an int). STATE.State_Id cannot be equal to value if _state_Id is not equal to value, it would violate the FK constraint in the DB. So, STATE = null is executed. In other words the setter of the navigation property STATE is called:
if (!ReferenceEquals(_sTATE, value))
{
var previousValue = _sTATE;
_sTATE = value;
FixupSTATE(previousValue);
}
The if condition is true again because _STATE is not null (it just has been loaded from the DB before) and value is null. So, FixupSTATE will be called with a parameter previousValue that is not null:
if (previousValue != null && previousValue.FILES.Contains(this))
//...
Now, if lazy loading is enabled (and it is by default) accessing the previousValue.FILES collection will cause lazy loading to kick in and issue a database query the loads the whole previousValue.FILES collection from the database - which seem to contain 60000 entities in your case.
The solution for this problem is to disable lazy loading:
var context = GetContext(myTrans, false);
context.ContextOptions.LazyLoadingEnabled = false;
//...
Or, modify the T4 template so that it doesn't create the Fixup code anymore (might be difficult to get it right). Or, maybe there already is a modified T4 template for EF 4.0 out there you can use. Or, upgrade to EF >= 4.1 and DbContext because the POCO template for DbContext doesn't have this additional code. The generated POCO classes are much simpler then.
I can't seem to find anything on how to edit the data editor settings before umbraco 6.2 (Juno). Is there any simple way, it must be possible. If you don't understand what i mean i want to do the same as http://www.nibble.be/?p=96 - just for umbraco 4.5.2.
Thanks :)
You need to make 3 classes Class 1 DataEditor
public class DataEditor : System.Web.UI.UpdatePanel, umbraco.interfaces.IDataEditor
{
public MWCropperDataEditor(umbraco.interfaces.IData Data, string Configuration)
{
_data = Data;
}
public virtual bool TreatAsRichTextEditor
{
get { return false; }
}
public bool ShowLabel
{
get { return true; }
}
public Control Editor { get { return this; } }
public void Save()
{
this._data.Value = "data;
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
imageUpload = new FileUpload();
imageUpload.ID = "imageUpload";
//shows Image
cropImage = new System.Web.UI.WebControls.Image();
cropImage.Width = width;
cropImage.Height = height;
cropImage.ImageUrl = this._data.Value.ToString();
//Shows dropdown
locationDropDown = new DropDownList();
AddItemsToDropDown();
lblInfo = new Label();
lblInfo.Attributes.Add("id", "title" + base.ClientID);
lblCropInfo = new Label();
lblCropInfo.Text = "Crop Location: ";
base.ContentTemplateContainer.Controls.Add(lblInfo);
base.ContentTemplateContainer.Controls.Add(imageUpload);
base.ContentTemplateContainer.Controls.Add(new LiteralControl("<br/>"));
base.ContentTemplateContainer.Controls.Add(new LiteralControl("<br/>"));
base.ContentTemplateContainer.Controls.Add(lblCropInfo);
base.ContentTemplateContainer.Controls.Add(locationDropDown);
base.ContentTemplateContainer.Controls.Add(new LiteralControl("<br/>"));
base.ContentTemplateContainer.Controls.Add(new LiteralControl("<br/>"));
base.ContentTemplateContainer.Controls.Add(cropImage);
}
}
class 2 DataType
public class MWCropperDataType : umbraco.cms.businesslogic.datatype.BaseDataType, umbraco.interfaces.IDataType
{
private umbraco.interfaces.IDataEditor _Editor;
private umbraco.interfaces.IData _baseData;
private MWCropperPrevalueEditor _prevalueeditor;
public override umbraco.interfaces.IDataEditor DataEditor
{
get
{
if (_Editor == null)
_Editor = new MWCropperDataEditor(Data, ((MWCropperPrevalueEditor)PrevalueEditor).Configuration);
return _Editor;
}
}
public override umbraco.interfaces.IData Data
{
get
{
if (_baseData == null)
_baseData = new umbraco.cms.businesslogic.datatype.DefaultData(this);
return _baseData;
}
}
public override Guid Id
{
get { return new Guid("71518B4E-B1A5-11DD-A22C-8AAA56D89593"); }
}
public override string DataTypeName
{
get { return "MWCropper"; }
}
public override umbraco.interfaces.IDataPrevalue PrevalueEditor
{
get
{
if (_prevalueeditor == null)
_prevalueeditor = new MWCropperPrevalueEditor(this);
return _prevalueeditor;
}
}
}
Class 3 PrevalueEditor
public class MWCropperPrevalueEditor : System.Web.UI.WebControls.PlaceHolder, umbraco.interfaces.IDataPrevalue
{
#region IDataPrevalue Members
// referenced datatype
private umbraco.cms.businesslogic.datatype.BaseDataType _datatype;
private TextBox _txtWidth;
private TextBox _txtHeight;
public MWCropperPrevalueEditor(umbraco.cms.businesslogic.datatype.BaseDataType DataType)
{
_datatype = DataType;
setupChildControls();
}
private void setupChildControls()
{
_txtWidth = new TextBox();
_txtWidth.ID = "txtWidth";
_txtWidth.CssClass = "umbEditorTextField";
Controls.Add(_txtWidth);
_txtHeight = new TextBox();
_txtHeight.ID = "txtHeight";
_txtHeight.CssClass = "umbEditorTextField";
Controls.Add(_txtHeight);
}
public Control Editor
{
get
{
return this;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Page.IsPostBack)
{
if (Configuration.Length > 0)
{
string[] value = Configuration.Split(new char[]{';'});
_txtWidth.Text = value[0];
_txtHeight.Text = value[1];
}
else
{
_txtHeight.Text = "100";
_txtWidth.Text = "100";
}
}
}
public void Save()
{
_datatype.DBType = (umbraco.cms.businesslogic.datatype.DBTypes)Enum.Parse(typeof(umbraco.cms.businesslogic.datatype.DBTypes), DBTypes.Ntext.ToString(), true);
string data = _txtWidth.Text+";"+_txtHeight.Text;
SqlHelper.ExecuteNonQuery("delete from cmsDataTypePreValues where datatypenodeid = #dtdefid",
SqlHelper.CreateParameter("#dtdefid", _datatype.DataTypeDefinitionId));
SqlHelper.ExecuteNonQuery("insert into cmsDataTypePreValues (datatypenodeid,[value],sortorder,alias) values (#dtdefid,#value,0,'')",
SqlHelper.CreateParameter("#dtdefid", _datatype.DataTypeDefinitionId), SqlHelper.CreateParameter("#value", data));
}
protected override void Render(HtmlTextWriter writer)
{
writer.WriteLine("<table>");
writer.Write("<tr><th>Width:</th><td>");
_txtWidth.RenderControl(writer);
writer.Write("</td></tr>");
writer.Write("<tr><th>Height:</th><td>");
_txtHeight.RenderControl(writer);
writer.Write("</td></tr>");
writer.Write("</table>");
}
public string Configuration
{
get
{
object conf =
SqlHelper.ExecuteScalar<object>("select value from cmsDataTypePreValues where datatypenodeid = #datatypenodeid",
SqlHelper.CreateParameter("#datatypenodeid", _datatype.DataTypeDefinitionId));
if (conf != null)
return conf.ToString();
else
return "";
}
}
#endregion
public static ISqlHelper SqlHelper
{
get
{
return Application.SqlHelper;
}
}
}
I hope this can help you get started :)
Btw this also works for umbraco 6.2
Settings are called prevalues and you need a PrevalueEditor class that implements IDataPrevalue. Have a look at an example in this blog post:
http://www.eyecatch.no/blog/my-first-umbraco-datatype---part-2-rendering-a-recaptcha-control.aspx
This nibble post concerns doing a similar thing for v4.5 and prior, there is also a link within this for an even older version that I followed a while ago and found very helpful.
http://www.nibble.be/?p=62