Подтвердить что ты не робот

Twoway-bind view DependencyProperty для свойства viewmodel?

Несколько источников в сети сообщают нам, что в MVVM связь/синхронизация между представлениями и режимами просмотра должна происходить через свойства зависимостей. Если я правильно понимаю это, свойство зависимостей представления должно быть привязано к свойству viewmodel с использованием двусторонней привязки. Теперь аналогичные вопросы задавали раньше, но без достаточного ответа.

Прежде чем приступить к анализу этой довольно сложной проблемы, вот мой вопрос:

Как синхронизировать пользовательский вид DependencyProperty с свойством viewmodel?

В идеальном мире вы просто привязываете его как это:

<UserControl x:Class="MyModule.MyView" MyProperty="{Binding MyProperty}">

Это не работает, поскольку MyProperty не является членом UserControl. Doh! Я пробовал разные подходы, но никто не добился успеха.

Одним из решений является определение базового класса UserControlEx с необходимыми свойствами зависимостей, чтобы заставить выше работать. Однако это скоро становится крайне беспорядочным. Не достаточно!

4b9b3361

Ответ 1

Я использую Caliburn.Micro для разделения ViewModel из представления. Тем не менее, это может работать в MVVM одинаково. Я полагаю, MVVM задает свойство view DataContext экземпляру ViewModel.

VIEW

// in the class of the view: MyView
public string ViewModelString // the property which stays in sync with VM property
{
    get { return (string)GetValue(ViewModelStringProperty); }
    set
    {
        var oldValue = (string) GetValue(ViewModelStringProperty);
        if (oldValue != value) SetValue(ViewModelStringProperty, value);
    }
}

public static readonly DependencyProperty ViewModelStringProperty =
    DependencyProperty.Register(
        "ViewModelString",
        typeof(string),
        typeof(MyView),
        new PropertyMetadata(OnStringValueChanged)
        );

private static void OnStringValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    // do some custom stuff, if needed
    // if not, just pass null instead of a delegate
}    

public MyView()
{
    InitializeComponent();
    // This is the binding, which binds the property of the VM
    // to your dep. property.
    // My convention is give my property wrapper in the view the same
    // name as the property in the VM has.
    var nameOfPropertyInVm = "ViewModelString"
    var binding = new Binding(nameOfPropertyInVm) { Mode = BindingMode.TwoWay };
    this.SetBinding(SearchStringProperty, binding);
}

VM

// in the class of the ViewModel: MyViewModel
public string ViewModelStringProperty { get; set; }

Обратите внимание, что этот вид реализации полностью не реализует интерфейс INotifyPropertyChanged. Вам нужно будет правильно обновить этот код.

Ответ 2

Если вы хотите сделать это в XAML, вы можете попробовать использовать стили для достижения этого.

Вот пример:

<UserControl x:Class="MyModule.MyView"
             xmlns:local="clr-namespace:MyModule">
    <UserControl.Resources>
        <Style TargetType="local:MyView">
            <Setter Property="MyViewProperty" Value="{Binding MyViewModelProperty, Mode=TwoWay}"/>
        </Style>
    </UserControl.Resources>
    <!-- content -->
</UserControl>

В вашем случае оба MyViewProperty и MyViewModelProperty будут называться MyProperty, но я использовал разные имена, чтобы быть ясно, что именно.

Ответ 3

Допустим, вы определили свой DependencyProperty "DepProp" в представлении и хотите использовать точно такое же значение в вашей ViewModel (которое реализует INotifyPropertyChanged, но не DependencyObject). Вы должны иметь возможность сделать следующее в своем XAML:

<UserControl x:Class="MyModule.MyView"
         xmlns:local="clr-namespace:MyModule"
             x:Name="Parent">
    <Grid>
        <Grid.DataContext>
            <local:MyViewModel DepProp="{Binding ElementName=Parent, Path=DepProp}"/>
        </Grid.DataContext>
    ...
    </Grid>
</UserControl>