2013-04-08 4 views
1

Я продолжаю пытаться сделать это препятствие в WPF, и я думаю, что нашел решение, хотя и уродливое.Свойство привязки привязки к текущему свойству DataContext

Сценарий выглядит следующим образом:

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

Код ...

Родитель Просмотр

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}"> 

    <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" /> 

</UserControl> 

Родитель Class View Model

public class ParentClassViewModel : BaseViewModel 
{ 
    private string _demoTextAlpha = "Some Alpha text"; 

    public string DemoTextAlpha 
    { 
     get 
     { 
      return this._demoTextAlpha; 
     } 
     set 
     { 
      this._demoTextAlpha = value; 
      this.NotifyPropertyChange("DemoTextAlpha"); 
     } 
    } 
} 

Ребенок Вид

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ChildControlLocator}"> 

    <TextBlock Text="{Binding Path=SomeProperty}" /> 

</UserControl> 

Детский код Просмотр За

public partial class Child : UserControl 
{ 
    public Child() 
    { 
     InitializeComponent(); 
    } 

    public static readonly DependencyProperty DemoProperty = 
      DependencyProperty.Register("Demo", 
             typeof(string), 
             typeof(Child), 
             new FrameworkPropertyMetadata() 
             { 
              PropertyChangedCallback = OnDemoChanged, 
              BindsTwoWayByDefault = true 
             }); 

    public string Demo 
    { 
     get { return this.GetValue(DemoProperty).ToString(); } 
     set { this.SetValue(DemoProperty, value); } 
    } 

    private static void OnDemoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var control = (Child)d; 
     var viewModel = (ChildViewModel)control.DataContext; 

     viewModel.SomeProperty = (string)e.NewValue; 
    } 
} 

Child Model View

public class ChildViewModel : BaseViewModel 
{ 
    private string _someProperty; 

    public string SomeProperty 
    { 
     get 
     { 
      return _someProperty; 
     } 
     set 
     { 
      _someProperty = value; 
      this.NotifyPropertyChange("SomeProperty"); 
     } 
    } 
} 

Ok, так что РАБОТЫ. То, что я пытаюсь достичь, является лучшим/более элегантным кодом, особенно в том, что касается этого утверждения.

<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" /> 

Даже то, что я мог бы жить, насколько это элегантность идет, но одна вещь, которая сейчас меня беспокоит то, что когда я печатаю

Path=DataContext.DemoTextAlpha 

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

Итак - существует ли какой-либо другой способ сделать свойства DataContext отображаемыми в intellisense, или есть альтернативный способ добиться того же, что и я сейчас?

Спасибо.

EDIT для Уточнить

Когда я кладу что-то вроде этого, вместо указания относительного источника как в приведенных выше примерах ...

<my:Child Demo="{Binding DemoTextAlpha}"/> 

Я получаю сообщение об ошибке ...

System.Windows.Data Error: 40 : BindingExpression path error: 'DemoTextAlpha' property not found on 'object' ''ChildViewModel' (HashCode=34126977)'. BindingExpression:Path=DemoTextAlpha; DataItem='ChildViewModel' (HashCode=34126977); target element is 'Child' (Name=''); target property is 'Demo' (type 'String') 

ответ

5

DataContext (наряду с множеством других свойств, таких как FontSize) является "Inherited" вдоль визуального дерева. Поэтому это:

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}"> 
    <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" /> 
</UserControl> 

точно так же, как это:

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}"> 
    <my:Child Demo="{Binding DemoTextAlpha}"/> 
</UserControl> 

Что касается поддержки Intellisense, я не знаю, что VS версии вы используете, но я м с помощью VS 2010 Pro с ReSharper 6.1 и добавляет поддержку Intellisense, если указать d:DataContext значение:

<UserControl x:Class="...etc." 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:local="clr-namespace:TheViewModelNamespace" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" d:DataContext="{d:DesignInstance local:ViewModel}"> 

Edit:

Ок .. давайте анализируем то, что вы делаете здесь:

- Привязка UserControl к ParentVM:

ParentVM -> UserControl 

- Использование RelativeSource To Возьмите некоторое свойство из ParentVM и поместите его в Custom DP, который вы создали в Child Control

ParentVM -> UserControl -> Child Control 

- В OnPropertyChanged обычая DP, установив, что такое же значение для ChildVM

ParentVM -> UserControl -> Child Control -> ChildVM 

Вы понимаете, что вы используете (пользовательский элемент управления, управления ребенка) Вид промежуточного поделиться некоторые свойства между 2 Просмотр моделей? Почему бы вам не просто

ParentVM -> ChildVM 

Что было бы проще, чище и действительно MVVM?

Либо поставьте ссылку из ParentVM непосредственно в ChildVM, либо используйте что-то вроде шаблона Messenger, чтобы иметь косвенную связь между ними.

+1

Пожалуйста, проверьте мое редактирование на OP, чтобы увидеть мое разъяснение ... Я попытался поместить его в комментарий, но я не смог его правильно отформатировать. – imdandman

+0

@imdandman см. Мое редактирование. –

+0

Drat! Другое дело, что я забыл. То, что вы сказали, это то, что я делал какое-то время. У меня были ссылки на другие виртуальные машины в конструкторе, и когда мне нужно было заполнять данные, я просто так делал. Однако я столкнулся с этой проблемой, когда мне нужно разместить 2x ChildControl внутри одного и того же родителя. Имеет ли это смысл? – imdandman

1

DataContext наследуется:

<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}"> 

    <my:Child Demo="{Binding DemoTextAlpha}" /> 

</UserControl> 

Если ина другой сценарий, ваш дочерний элемент управления имеет другой DataContext указанный, и вы все еще нужно привязать к свойству ваш родитель элемента управления DataContext, используя ElementName вероятно лучше:

<UserControl x:Name="Parent" DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}"> 

    <my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, ElementName=Parent}" /> 

</UserControl> 
Смежные вопросы