Подписка на PropertyChanged прокси-класса DataContract службы WCF - PullRequest
4 голосов
/ 01 февраля 2010

Мне нравится идея расширения на стороне клиента классов, которые являются контрактами данных служб WCF с использованием частичных классов. Но я столкнулся с проблемой, которая значительно портит вечеринку.

Представьте, что на стороне сервера у меня есть класс:

[DataContract]
public class SolidObject
{
    [DataMember]
    public Point Position { get; set; }
    [DataMember]
    public Size Size { get; set; }
}

На стороне клиента у меня сгенерирован прокси-класс, который используется там на уровне бизнес-логики. В соответствии с потребностями бизнес-логики я расширяю ее следующим образом:

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); }}
}

Теперь я хочу убедиться, что в любое время, когда изменяется позиция или размер, вызывается событие Bounds chage. Это легко сделать по коду:

PropertyChanged += (sender, e) =>
    {
        if ((e.PropertyName == "Position") || (e.PropertyName == "Size")) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Bounds"));
    };

Вопрос в том, где хорошее место для размещения этого кода.

Если бы объекты не были созданы вызовом службы, я бы поместил их в конструктор. Но службы WCF игнорируют конструкторы на стороне клиента, смотрите, как конструктор не отображается в моем клиенте WCF, проблема сериализации? .

Теперь, сразу после ответа службы, моя программа просматривает иерархию контрактов данных, получает нужные объекты и добавляет обработчики событий. Но я не думаю, что это правильно.

Так что мне интересно, где это лучше сделать или, может быть, рассуждать, что весь подход должен быть изменен. Любые идеи приветствуются.

Ответы [ 3 ]

4 голосов
/ 19 февраля 2010

Вы можете использовать атрибут [OnDeserlialized] для вставки метода, который вызывается после десерилизации.

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"));
        };
    }
}
2 голосов
/ 05 февраля 2010

Я бы рекомендовал использовать модель представления, а не частичный класс.

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));
    }
}
0 голосов
/ 06 февраля 2010

Я бы сказал, не используйте сгенерированный класс, если есть бизнес-логика, которая будет реализована. Сохраняйте свою модель чистой, имея реализацию SolidObject на стороне клиента. Когда вы генерируете прокси, вы можете использовать опцию повторного использования классов из этой библиотеки .

Не имеет смысла для объекта присоединяться к своему собственному событию. Реализация будет ..

[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); }}
}
...