将ViewModel绑定到多个窗口

时间:2016-01-27 20:02:38

标签: c# wpf xaml mvvm binding

我正在重新编写我的Windows表单项目,该项目为羊剪切事件(不要问,这是新西兰的一项大型运动)从vbnet到wpf c#进行评分并且遇到了一个问题我似乎无法克服。

我有两个窗户。一个是您输入内容的源窗口(如当前事件名称),另一个窗口将以闪存方式显示此信息,以便投影到屏幕上(因此将在第二个监视器上)以及其他一些数据在via XML over the network。我将它设置为MVVM,将ViewModel和Model作为单独的项目。

在我的主窗口上,我可以很好地绑定控件,如果我输入一个文本框,它会立即出现在另一个文本框中,如果它绑定到同一个东西。 但是,在第二个窗口中,我将控件绑定到同一个东西,并且它没有更新。

我已经围绕这个问题进行了一周的讨论,网上的每个例子都显示了如何在一个窗口上进行操作,我已经工作正常,但是缺少两个窗口示例。 / p>

这是我的......

这是我的ViewModel项目

namespace SheepViewModel
{
public class SheepViewModel : INotifyPropertyChanged


{
    private string _CurrentEventName;
    static SheepViewModel _details;

    public string CurrentEventName
    {
        get { return _CurrentEventName; }
        set
        {
            _CurrentEventName = value;
            OnPropertyChanged("CurrentEventName");
        }
    }

    public static SheepViewModel GetDetails()
    {
        if (_details == null)
            _details = new SheepViewModel();
        return _details;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string prop)
    {
        if (PropertyChanged != null)
             PropertyChanged(this, new PropertyChangedEventArgs(prop));
            Console.WriteLine("Test");
            }     
    }
}

然后我有一个主窗口,除了一行打开第二个窗口后面没有真正的代码,我们将会... ...

 public MainWindow()
    {
        ScoreScreen SW = new ScoreScreen();
        SW.Show();
        InitializeComponent();
    }

然后是XAML

<Window x:Class="Sheep_Score_3._1.MainWindow"
    xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:SheepViewModel;assembly=SheepViewModel"
    mc:Ignorable="d"
    Title="MainWindow" Height="433.689" Width="941.194">
<Window.DataContext>
    <vm:SheepViewModel/>
</Window.DataContext>
<Window.Resources>
<Grid Margin="0,0,0,0">
<TextBox x:Name="CurrentEventName" Height="23" Margin="131.01,163.013,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="327.151" Text="{Binding CurrentEventName, Mode=TwoWay}"/>
    <TextBox Text="{Binding CurrentEventName, Mode=TwoWay}" Margin="39.605,0,0,108.567" Height="49.111" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="399" />
</Grid>

上面的代码都可以正常工作,如果我在第一个文本框中输入文本,它会出现在第二个文本框中。如果我在通知部分中放置了console.writeline,那么我可以看到它正在点击并更新。

现在我添加第二个窗口,设置完全相同......

<Window x:Class="Sheep_Score_3._1.ScoreScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:SheepViewModel;assembly=SheepViewModel"
    mc:Ignorable="d"
    Title="ScoreScreen" Height="300" Width="300">
<Window.DataContext>
    <vm:SheepViewModel/>
</Window.DataContext>
<Grid>
    <TextBox x:Name="textBlock" HorizontalAlignment="Left" Margin="79.374,116.672,0,0" TextWrapping="Wrap" Text="{Binding CurrentEventName, Mode=TwoWay}" VerticalAlignment="Top"/>
</Grid>

同样,这里没有真正的代码。

奇怪的是,如果我以两种方式进行此控制并输入,我可以看到它击中相同的通知部分,但它没有更新另一个窗口。

我不确定我在这里缺少什么,所以任何帮助我指向正确的方向都会非常感激。

4 个答案:

答案 0 :(得分:11)

那是因为两个窗口必须共享ViewModel的完全相同的实例

您的所有属性都是实例属性,例如

public string CurrentEventName { get { // snip

因此所有值都与每个实例不同。您正在创建两个实例,每个窗口一个。

<Window x:Class="Sheep_Score_3._1.MainWindow"
    xmlns:blah="http://inurxamlskippinurschemas.org">
    <Window.DataContext>
        <vm:SheepViewModel/>
    </Window.DataContext>

这是一个例子,这是另一个

<Window x:Class="Sheep_Score_3._1.ScoreScreen"
        xmlns:blah="http://yaddayaddawhocares.derp">
    <Window.DataContext>
        <vm:SheepViewModel/>
    </Window.DataContext>

请记住,xaml只是反序列化到对象图中的标记。您有两个不同的标记文件,它们包含其中描述的所有内容的不同实例。

这没有任何问题,并且使用具有实例属性的视图模型没有任何问题。事实上,这是使用静态和静态绑定的首选方式。

答案很简单。您需要提供两个窗口视图模型的相同实例

首先,从两个窗口中删除所有<Window.DataContext>废话。那不适合你。现在,只需将构造函数更改为

即可
public MainWindow()
{
    var viewModel = new SheepViewModel();
    ScoreScreen SW = new ScoreScreen();
    SW.DataContext = viewModel;
    SW.Show();
    InitializeComponent();
    //NOTICE!  After Init is called!
    DataContext = viewModel;
}

你已经完成了。

答案 1 :(得分:1)

我怀疑每个窗口都在创建自己的ViewModel实例。您可以尝试以下方法:

public MainWindow()
{
    InitializeComponent();

    SheepViewModel svm = new SheepViewModel();
    this.DataContext = svm;

    ScoreScreen SW = new ScoreScreen();
    SW.DataContext = svm;
    SW.Show();        
}

答案 2 :(得分:0)

您的viewmodel定义了一个获取单个实例的静态方法,但您不使用它来实例化它。目前,您的viewmodel是使用默认构造函数创建的,这意味着两个窗口将具有单独的副本。

在InitializeComponent下面的代码或OnNavigatedToEvent中创建你的viewmodel。

这里有一些代码可以进一步解释:

在两个窗口中定义一个ViewModel属性

property SheepViewModel ViewModel { get; set; }

然后你的构造函数:

public MainWindow()
{
    InitializeComponent();
    ViewModel = SheepViewModel.GetDetails(); // include this line
    ScoreScreen SW = new ScoreScreen();
    SW.Show();
}

也删除了

<vm:SheepViewModel/>

来自xaml,因为它不是必需的。

答案 3 :(得分:0)

我会将ViewModel作为应用程序资源

<Application.Resources>
    <VM:ViewModel x:Key="SharedViewModel" />
    ....
</Application.Resources>

然后在每个窗口中,您可以像这样调用它

DataContext="{StaticResource MainViewModel}"

我仍然对这个概念不熟悉,我不确定这是否是最佳的。它虽然有效!