Silverlight с MVVM: как получить доступ к событию модели представления из представления? - PullRequest
2 голосов
/ 27 августа 2011

У меня есть приложение MVVM, и где-то в приложении наша компания использует Стороннюю, которая не может использовать {Binding}. Это компонент, который рисует фигуры и т. Д. Что мне нужно, когда ViewModel загружает из постоянного хранилища все фигуры, чтобы уведомить View для их рисования. В идеальном мире я просто взял бы Стороннее и связал бы его с коллекцией ViewModel Shapes, но я не могу.

Оттуда моя идея состояла в том, чтобы я мог получить из View ViewModel (через DataContext) и перехватить событие PropertyChanged. Проблема в том, что DataContext еще не инициализирован в конструкторе, поэтому он имеет значение NULL, и я не могу перехватить событие. Вот пример кода:

        public CanvasView()
        {
            InitializeComponent();
            ((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged); //Exception Throw here because DataContext is null
        }

        void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Shapes")
            {
                DrawShapes(); 
            }
        }

Как в таком случае я могу получить информацию из моей ViewModel в View?

Ответы [ 4 ]

4 голосов
/ 27 августа 2011

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

C #

public partial class MyWrappedControl : UserControl
{
  public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register("Shapes", typeof(ObservableCollection<IShape>), typeof(MyWrappedControl),
      new PropertyMetadata(null, MyWrappedControl.OnShapesPropertyChanged);

  public ObservableCollection<IShape> Shapes
  {
    get { return (ObservableCollection<IShape>)GetValue(ShapesProperty); }
    set { SetValue(ShapesProperty, value); }
  }

  private static void OnShapesPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
  {
    ((MyWrappedControl)o).OnShapesPropertyChanged(e);
  }

  private void OnShapesPropertyChanged(DependencyPropertyChangedEventArgs e)
  {
    // Do stuff, e.g. shapeDrawer.DrawShapes(); 
  }
}

1010 * XAML *

<UserControl 
    Name="MyWrappedControl"
    x:Class="MyWrappedControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <!-- your control -->
    <shapeDrawerControl x:Name="shapeDrawer" />
</UserControl>
2 голосов
/ 27 августа 2011

вы также можете прикрепить ваш обработчик в событие Loaded.

public CanvasView()
{
   InitializeComponent();
   this.Loaded += this.ViewLoaded;
}

void ViewLoaded(object sender, PropertyChangedEventArgs e)
{
   ((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged);    
}

void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
   if (e.PropertyName == "Shapes")
   {
      DrawShapes(); 
   }
}
0 голосов
/ 27 августа 2011

Хочу прокомментировать ответ Денниса Роша.Действительно, в этом случае мы можем использовать подход с переносом, потому что нам нужно перерисовать представление, когда коллекция Shapes изменилась.Но логика модели представления может быть слишком сложной, и, например, вместо перерисовки в PropertyChanged мы должны перерисовать какое-то пользовательское событие (например, ModelReloadEvent).В этом случае перенос не помогает, но подписка на это событие помогает, как в решении Muad'Dib - модель представления использует связь на основе событий с представлением, но это событие должно быть специфичным для представления.

Использование кодаЗадняя с View специфическая логика не нарушает MVVM.Да, этот код может быть украшен поведением / действием, но использование кода позади - просто простое решение.

Кроме того, взгляните на это представление MVVM .Согласно структуре, ViewModel знает об абстрактном IView.Если вы работали с платформами Caliburn / Caliburn.Micro MVVM, вы помните класс ViewAware и IViewAware, который позволяет получить представление в модели представления.

Итак, более гибкое решение, я думаюследующее:

Просмотр:

public class CanvasView() : ICanvasView
{
        public CanvasView()
        {
            InitializeComponent();
        }

        public void DrawShapes()
        {
          // implementation
        }
}

ICanvasView:

public interface ICanvasView
{
    void DrawShapes();
}

CanvasViewModel:

public class CanvasViewModel : ViewAware
{    
    private ObservableCollection<IShape> _shapes;
    public ObservableCollection<IShape> Shapes
    {
        get
        {
           return _shapes;
        }
        set
        {
           _shapes = value;
           NotifyOfPropertyChange(() => Shapes);
           RedrawView();
        }
    }

    private void RedrawView()
    {
        ICanvasView abstractView = (ICanvasView)GetView();
        abstractView.DrawShapes();
    }
}
0 голосов
/ 27 августа 2011

Использование события DataContextChanged в представлении (Window или UserControl)

    public CanvasView()
    {
        InitializeComponent();
        Action wireDataContext += new Action ( () => {
            if (DataContext!=null) 
                      ((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged);
            });
        this.DataContextChanged += (_,__) => wireDataContext();
        wireDataContext();
    }

    void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Shapes")
        {
            DrawShapes(); 
        }
    }

обновление: вот документированный способ получить DataContextChanged в Silverlight 3 и 4 http://www.lhotka.net/weblog/AddingDataContextChangedInSilverlight.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...