将viewmodel中的变量绑定到另一个viewmodel中的另一个变量

时间:2012-10-18 10:46:02

标签: wpf mvvm binding viewmodel

我正在使用MVVM模式实现WPF应用程序。

该应用程序基本上是一个通信面板(通信面板),其上面有控制小部件(例如拨号盘,内部通信线路等)。控件小部件也已使用MVVM模式实现,因为这允许我们在个人基础上轻松测试它们。

我有这个“拨号盘”控件小部件,它在其视图模型中公开DialedNumber公共字段:

public string DialedNumber
    {
        get { return _dialPadModel.DialedNumber; }
        set
        {
            _dialPadModel.DialedNumber = value;
            RaisePropertyChanged("DialedNumber");
        }
    }

“拨号盘”控件小部件还通过视图中的公共字段公开其视图模型:

public DialPadViewModel DialPadViewModel
    {
        get { return DataContext as DialPadViewModel; }
    }

它还通过它的视图公开它,它只是从视图模型中的公共字段写入/读取:

public string DialedNumber
    {
        get
        {
            return DialPadViewModel.DialedNumber;
        }
        set
        {
            DialPadViewModel.DialedNumber = value;
        }
    }

DialPad放置在一个comms面板中(也使用MVVM实现),该面板在其视图模型中有一个DialedPABXNumber公共字段:

public string DialedPABXNumber
    {
        get { return _dialedPABXNumber; }
        set
        {
            _dialedPABXNumber = value;
            OnPropertyChanged("DialedPABXNumber");
        }
    }

现在,我希望能够将来自DialPad的DialedNumber字段链接到comms面板中的DialedPABXNumber字段。但是,为了做到这一点,我正在努力想出正确的XAML语法。我的方法是这样的:

<PanelControls:DialPad x:Name="MyDialPad2" DialPadViewModel.DialedNumber="{Binding Path=CommsPanelViewModel.DialedPABXNumber, Mode=OneWayToSource}"/>

通过执行此操作,我在加载通信面板XAML时收到运行时异常。更具体地说:

  

无法设置未知成员'{http://schemas.microsoft.com/winfx/2006/xaml/presentation} DialPadModel.DialedNumber'。

如何在XAML中指定我想访问DialPadViewModel.DialedNumber?

编辑:添加有关组件如何组合在一起的背景信息。应用程序的主窗口有两个子窗口:左侧的控制面板和动态加载的适当的通信面板。

<Window x:Class="Comms.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Views="clr-namespace:Comms.View"
    xmlns:ViewModel="clr-namespace:Comms.ViewModel"
    Title="Comms" Height="350" Width="525" Closing="WindowClosing">
<Window.Resources>
    <DataTemplate x:Name="CommsControlPanelViewModel" DataType="{x:Type ViewModel:CommsControlPanelViewModel}">
        <Views:CommsControlPanelView x:Name="CommsControlPanelView"/>
    </DataTemplate>
    <DataTemplate x:Name="CommsPanelViewModel" DataType="{x:Type ViewModel:CommsPanelViewModel}">
        <Views:CommsPanelView x:Name="CommsPanelView"/>
    </DataTemplate>
</Window.Resources>

<StackPanel x:Name="Layout" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal">
    <ContentControl Content="{Binding CommsControlPanelView}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
    <ContentControl Content="{Binding CommsPanelView}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</StackPanel>

comms面板是动态加载的。这是面板的XAML文件:

<Border x:Name="CommsPanelBorder"
    Style="{DynamicResource BorderTemplate}"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:PanelControls="clr-namespace:CommsPanelControlsLib.View;assembly=CommsPanelControlsLib"
    VerticalAlignment="Top">
<StackPanel>
    <!-- PABX Dial Pad -->
    <StackPanel Orientation="Horizontal">
    <PanelControls:DialPad x:Name="MyDialPad2" DialPadViewModel.DialedNumber="{Binding Path=CommsPanelViewModel.DialedPABXNumber, Mode=OneWayToSource}"/>
    </StackPanel>
</StackPanel>

3 个答案:

答案 0 :(得分:1)

我对你要做的事情感到有点困惑,但是听起来你正试图将一个属性从一个对象绑定到另一个对象的属性

首先,有两种类型的属性:常规属性和依赖属性。如果要设置带绑定的属性,则需要为DependencyProperty,因此DialPadViewModel.DialedNumber必须是依赖属性,如果该值将由您拥有的绑定提供。

其次,您的实际绑定是尝试引用CommsPanelViewModel.DialedPABXNumber。这实际上将绑定到MyDialPad2.DataContext.CommsPanelViewModel.DialedPABXNumber,我认为如果我正确理解您的代码,则不是有效的属性。

我认为您要做的是找到CommsPanelViewModel作为CommsPanelView的DataContext,并绑定到其DialedPABXNumber属性。

要执行此操作,首先需要更改绑定源,以通过RelativeSourceElementName绑定查找CommsPanelView,并且需要设置{{1 } Path,因为DataContext.DialedPABXNumber的{​​{1}}是DataContext,并且该视图模型具有属性CommsPanelView

CommsPanelViewModel

那就是说,由于涉及到模板,我不能肯定您能够使用当前的XAML布局找到带有绑定的DialedPABXNumber。尝试一下,但如果不是这样的话,你还有一些选择。

您可以尝试在未使用的依赖项属性(例如<PanelControls:DialPad x:Name="MyDialPad2" DialPadViewModel.DialedNumber="{Binding ElementName=CommsPanelView, Path=DataContext.DialedPABXNumber, Mode=OneWayToSource}"/> 属性)中存储所需的属性,然后可以使用CommsPanelViewTag属性来查找ContentControl并绑定到其RelativeSource属性

ElementName

您还可以使用某种消息系统,如MVVM Light的Tag或Prism的<ContentControl Content="{Binding CommsControlPanelView}" Tag="{Binding CommsPanelView.DialedPABXNumber}" /> 来传递值。 (我在an article on my blog

中对这些内容进行了简要总结

或者您可以链接ViewModel图层中的两个属性,因此Messenger包含一个存储EventAggregator的属性,并且该属性会在需要时从ViewModel图层本身进行更新。

答案 1 :(得分:0)

要绑定到属性,此属性必须是DependencyProperty。您可以在View上创建Dummy-Property,通过PropertyChangedCallback操作ViewModel:

public static DependencyProperty TestProperty = DependencyProperty.Register("Test", typeof(string), typeof(View), new PropertyMetadata("default", new PropertyChangedCallback(TestPropertyChanged)));
public string Test
{
    get { return GetValue(TestProperty).ToString(); }
    set { SetValue(TestProperty, value); }
}
public static void TestPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    ViewModel vm= ((View)obj).DataContext;
    if (vm.Test != e.NewValue.ToString())
    {
        vm.Test = e.NewValue.ToString();
    }
} 

现在,您可以在视图中绑定此属性。

但我认为这违反了MVVM模式,不是吗?

答案 2 :(得分:0)

经过与Blachshma的长时间聊天(感谢Blachshma!),我通过删除其模型并将viewmodel中的代码移动到其后面的代码来修改DialPad代码。

我在面板的XAML文件中使用的绑定如下:

<PanelControls:DialPad x:Name="MyDialPad2" DialedNumber="{Binding Path=CommsPanelViewModel.DialedPABXNumber, Mode=OneWayToSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"/>

我可以确认点击拨号盘中的按钮有效地触发了CommsPanelViewModel内DialedPABXNumber字段的更改,这正是我想要的。但是,为了做到这一点,我在DialPad的代码中进行了一些重大修改,现在我无法对它进行单元测试。我会将问题标记为已回答,我会打开一个新问题以处理这个新问题。

感谢大家的意见!