Привязка ContentControl к глубокому пути в WPF - PullRequest
1 голос
/ 31 января 2012

Приложение, которое я сейчас пишу, использует MVVM с шаблоном ViewModel-first. У меня XAML похож на следующее:

<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>

Каждая виртуальная машина является DependencyObject. Каждое свойство DependencyProperty. В зависимости от состояния приложения значение свойства BarViewModel для FooViewModel может изменяться, что приводит к изменению значения свойства View. К сожалению, когда это происходит, новый вид не отображается, а старый остается.

Это очень расстраивает. Я думал, что если какая-то часть выражения пути изменится, привязка обновится, но это не так. Когда я использовал более мелкие выражения пути, такие как FooViewModel.View, и я изменил значение свойства FooViewModel, то имеет обновил ContentControl, к которому он привязан, но не в этом случай.

Если ваше решение состоит в том, чтобы я сначала отказался от ViewModel, это не вариант, хотя я ценю ваш совет. Я должен заставить это работать как есть.

ПОЯСНЕНИЯ

Это вопрос о привязке данных , а , а не о MVVM или о том, как ее реализовать. Вы можете смело игнорировать аспекты MVVM, если это поможет вам подумать о проблеме или если у вас другое представление о том, как следует реализовывать MVVM. Это большой существующий проект, в котором шаблон проектирования MVVM не может быть изменен. (Для этого уже слишком поздно.)

Итак, с учетом сказанного, правильный вопрос, на который нужно ответить, следующий:

Учитывая выражение пути привязки, в котором каждый элемент является DependencyProperty, а конечное свойство является представлением, связанным с ContentControl, почему изменение свойства в середине пути не приводит к обновлению привязки?

Ответы [ 2 ]

1 голос
/ 31 января 2012

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

Во-первых, ваши модели представлений не должны использовать DependencyObject или DependencyProperty, это связывает их с WPF.Вместо этого они должны реализовать INotifyPropertyChanged.Это позволяет использовать ваши модели представлений в других технологиях презентаций, таких как Silverlight.

Во-вторых, ваши модели представлений не должны иметь ссылок на ваши представления, поэтому вам не требуется свойство View в ваших моделях представлений.

Я бы серьезно подумал об использовании инфраструктуры MVVM для компоновки представлений - например, Caliburn.Micro делает первую разработку модели представления чрезвычайно простой и уже предоставляет базовый класс модели представления, который реализует INotifyPropertyChanged и механизм построения композиций вида с условными обозначениями.

Т.е. у вас может быть модель вида проводника, которая имеет свойство ActiveItem, и вы просто помещаете ContentControl в свой вид с тем же именем, что и у свойства:

<ContentControl x:Name="ActiveItem" />

Вы можете использовать метод ActivateItem() для изменения текущего активного элемента.

Caliburn.Micro также обладает множеством других функций, таких как возможность размещения элемента управления Button с x:Name="Save" на вашем экране., и ваш метод Save в модели представления будет автоматически вызываться при нажатии кнопки.

0 голосов
/ 31 января 2012

Каждая виртуальная машина является объектом DependencyObject. Каждая собственность DependencyProperty.

почему? модель представления должна быть простым классом с INotifyPropertyChanged, а свойства должны быть простыми свойствами.

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

 <Window>
  <Window.Resources>
    <DataTemplate DataType="{x:Type local:MyViewModelA}>
     <MyViewA/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:MyViewModelB}>
     <MyViewB/>
    </DataTemplate>
   </Windows.Resources>
   <Grid>
     <ContentControl Content="{Binding MyActualVM}"/>
   </Grid>
  </Window>

РЕДАКТИРОВАТЬ: между прочим, вы всегда привязываетесь к последнему свойству: FooViewModel.BarViewModel.View -> поэтому INotifyPropertyChanged (если поднято) просто работает для .View

EDIT2: другой подход может заключаться в получении BindingExpression вашего элемента управления контентом и вызова.

System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();

EDIT3: и простой mvvm способ - просто используйте INotifyPropertyChanged

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.MyFooVM = new FooVM();
        this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};

        this.DataContext = this;

    }

    public FooVM MyFooVM { get; set; }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
    }
}

public class INPC : INotifyPropertyChanged
{
    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropChanged(string property)
    {
        var handler = PropertyChanged;

        if(handler != null)
            handler(this, new PropertyChangedEventArgs(property));
    }

    #endregion
}

public class FooVM:INPC
{
    private BarVM _myBarVm;
    public BarVM MyBarVM
    {
        get { return _myBarVm; }
        set { _myBarVm = value;OnPropChanged("MyBarVM"); }
    }
}

public class BarVM : INPC
{
    private string _view;
    public string View
    {
        get { return _view; }
        set { _view = value;OnPropChanged("View"); }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...