我正在重新编写我的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>
同样,这里没有真正的代码。
奇怪的是,如果我以两种方式进行此控制并输入,我可以看到它击中相同的通知部分,但它没有更新另一个窗口。
我不确定我在这里缺少什么,所以任何帮助我指向正确的方向都会非常感激。
答案 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}"
我仍然对这个概念不熟悉,我不确定这是否是最佳的。它虽然有效!