Subscribing to PropertyChanged of WCF service's DataContract proxy classe
I like the idea of extending on the client side classes that are data contracts of WCF services using partial classes. But I encountered a problem that considerably spoils the party.
Imagine on the server side I have a class:
[DataContract]
public class SolidObject
{
[DataMember]
public Point Position { get; set; }
[DataMember]
public Size Size { get; set; }
}
On the client side I have a proxy class generated, which i开发者_如何学Gos used there in the business logic layer. According to the needs of the business logic I extend it this way:
public partial class SolidObject
{
public Rect Bounds { get { return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height); }}
}
Now I want to ensure that anytime either Position or Size changes, then Bounds chage event is invoked. It is easy to do by the code:
PropertyChanged += (sender, e) =>
{
if ((e.PropertyName == "Position") || (e.PropertyName == "Size")) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Bounds"));
};
The question is where the good place to put this code in is.
If objects weren't created by a service call, I'd put it into the constructor. But WCF services ignore constructors on the client side, see constructor not showing up in my WCF client, serialization problem?.
Now, right after service response my program searches through data contract hierarchy, gets desired objects and adds event handlers. But I don't think it's a right thing.
So I'm interesting in where to it is better to do or, maybe, reasoning that the whole approach should be changed. Any ideas appreciated.
You could use the [OnDeserlialized] attribute to insert a method that gets called after deserizlization.
public partial class SolidObject
{
public Rect Bounds
{
get
{
return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height);
}
}
[OnDeserialized]
public void Initialize(StreamingContext streamingContext)
{
PropertyChanged += (sender, e) =>
{
if ((e.PropertyName == "Position") || (e.PropertyName == "Size"))
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Bounds"));
};
}
}
I'd recommend using a view model rather than a partial class.
public class SolidObjectViewModel : INotifyPropertyChanged
{
private SolidObject _solidObject;
public SolidObjectViewModel(SolidObject solidObject)
{
_solidObject = solidObject;
_solidObject.PropertyChanged += (sender, e) =>
{
bool positionChanged = e.PropertyName == "Position";
bool sizeChanged = e.PropertyName == "Size";
if (positionChanged)
FirePropertyChanged("Position");
if (sizeChanged)
FirePropertyChanged("Size");
if (positionChanged || sizeChanged)
FirePropertyChanged("Bounds");
};
}
public Point Position
{
get { return _solidObject.Position; }
set { _solidObject.Position = value; }
}
public Size Size
{
get { return _solidObject.Size; }
set { _solidObject.Size = value; }
}
public Rect Bounds
{
get
{
return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I would say do not use generated class if there is business logic to be implemented.Keep your model clean by having client side implementation of SolidObject. When you are generating proxies you can use option of reuse classes from this dll.
It doesnot make sense for an entity to attach to its own event.The implementation would be..
[DataContract]
public class SolidObject
{
[DataMember]
public Point Position
{
get { return _position; }
set
{
position = value;
FirePropertyChanged("Position");
FirePropertyChanged("Bounds");
}
}
public Size Size
{
//similar to position
}
public Rect Bounds { get { return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height); }}
}
精彩评论