将 ViewModel 绑定到多个 windows
Binding ViewModel to multiple windows
我正在重写我的 windows 表单项目,该项目从 vbnet 到 wpf c# 为剪羊毛活动(别问,这在新西兰是一项巨大的运动)进行评分,但遇到了问题我似乎无法克服。
我有两个windows。一个是来源 window,您可以在其中输入内容(例如当前事件名称),另一个 window 将以闪光方式显示此信息以投影到屏幕上(因此将在第二个monitor) 以及通过网络 XML 传入的一些其他数据。我已将其设置为 MVVM,并将 ViewModel 和模型作为单独的项目。
在我的 Main window 上,我可以很好地绑定控件,如果我在一个文本框中键入内容,如果它绑定到同一内容,它会立即出现在另一个文本框中。
但是,在第二个 window 上,我将一个控件绑定到同一事物并且它没有更新。
我已经在这个问题上兜兜转转了一个星期,网上的每个例子都展示了如何在一个 window 上做,我已经很好地工作了,但是缺少两个window 个示例。
这是我的...
这是在我的 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");
}
}
}
然后我有一个主要的 window,除了一行打开第二个 window 之外没有真正的代码,我们将到达...
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,设置完全相同...
<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>
同样,这里没有真正的代码。
奇怪的是,如果我以两种方式制作此控件并在其中键入,我可以看到它点击了相同的通知部分,但它没有更新另一个 window。
我不确定我在这里遗漏了什么,因此非常感谢您为我指明正确的方向。
我怀疑每个 window 都在创建自己的 ViewModel 实例。您可以尝试以下方法:
public MainWindow()
{
InitializeComponent();
SheepViewModel svm = new SheepViewModel();
this.DataContext = svm;
ScoreScreen SW = new ScoreScreen();
SW.DataContext = svm;
SW.Show();
}
您的视图模型定义了一个静态方法来获取单个实例,但您没有使用它来实例化它。目前您的视图模型是使用默认构造函数创建的,这意味着两个 windows 将有单独的副本。
在 InitializeComponent 下面或 OnNavigatedToEvent 中的隐藏代码中创建您的视图模型。
这里有一些代码可以进一步解释:
像这样在两个 windows
中定义一个 ViewModel 属性
property SheepViewModel ViewModel { get; set; }
那么你的构造函数:
public MainWindow()
{
InitializeComponent();
ViewModel = SheepViewModel.GetDetails(); // include this line
ScoreScreen SW = new ScoreScreen();
SW.Show();
}
同时删除
<vm:SheepViewModel/>
来自 xaml,因为它不是必需的。
那是因为两者 windows 必须共享完全相同的 ViewModel 实例。
你所有的属性都是实例属性,喜欢
public string CurrentEventName { get { // snip
因此每个实例的所有值都是不同的。您正在创建 两个 个实例,每个实例一个 window。
<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 只是 反序列化 到对象图中的标记。您有两个不同的标记文件,它们包含其中描述的所有内容的不同实例。
这并没有错,拥有一个带有实例属性的视图模型也没有错。事实上,这是优于使用静态和静态绑定的首选方式。
幸好答案很简单。您需要将视图模型的 windows 和 相同实例 都交给。
首先,从你们的 windows 中删除所有 <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;
}
大功告成。
我会将 ViewModel 作为应用程序资源
<Application.Resources>
<VM:ViewModel x:Key="SharedViewModel" />
....
</Application.Resources>
然后在每个window中你这样称呼它
DataContext="{StaticResource MainViewModel}"
我对这个概念还是陌生的,我不确定这是否是最优的。虽然它有效!
我正在重写我的 windows 表单项目,该项目从 vbnet 到 wpf c# 为剪羊毛活动(别问,这在新西兰是一项巨大的运动)进行评分,但遇到了问题我似乎无法克服。
我有两个windows。一个是来源 window,您可以在其中输入内容(例如当前事件名称),另一个 window 将以闪光方式显示此信息以投影到屏幕上(因此将在第二个monitor) 以及通过网络 XML 传入的一些其他数据。我已将其设置为 MVVM,并将 ViewModel 和模型作为单独的项目。
在我的 Main window 上,我可以很好地绑定控件,如果我在一个文本框中键入内容,如果它绑定到同一内容,它会立即出现在另一个文本框中。 但是,在第二个 window 上,我将一个控件绑定到同一事物并且它没有更新。
我已经在这个问题上兜兜转转了一个星期,网上的每个例子都展示了如何在一个 window 上做,我已经很好地工作了,但是缺少两个window 个示例。
这是我的...
这是在我的 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");
}
}
}
然后我有一个主要的 window,除了一行打开第二个 window 之外没有真正的代码,我们将到达...
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,设置完全相同...
<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>
同样,这里没有真正的代码。
奇怪的是,如果我以两种方式制作此控件并在其中键入,我可以看到它点击了相同的通知部分,但它没有更新另一个 window。
我不确定我在这里遗漏了什么,因此非常感谢您为我指明正确的方向。
我怀疑每个 window 都在创建自己的 ViewModel 实例。您可以尝试以下方法:
public MainWindow()
{
InitializeComponent();
SheepViewModel svm = new SheepViewModel();
this.DataContext = svm;
ScoreScreen SW = new ScoreScreen();
SW.DataContext = svm;
SW.Show();
}
您的视图模型定义了一个静态方法来获取单个实例,但您没有使用它来实例化它。目前您的视图模型是使用默认构造函数创建的,这意味着两个 windows 将有单独的副本。
在 InitializeComponent 下面或 OnNavigatedToEvent 中的隐藏代码中创建您的视图模型。
这里有一些代码可以进一步解释:
像这样在两个 windows
中定义一个 ViewModel 属性property SheepViewModel ViewModel { get; set; }
那么你的构造函数:
public MainWindow()
{
InitializeComponent();
ViewModel = SheepViewModel.GetDetails(); // include this line
ScoreScreen SW = new ScoreScreen();
SW.Show();
}
同时删除
<vm:SheepViewModel/>
来自 xaml,因为它不是必需的。
那是因为两者 windows 必须共享完全相同的 ViewModel 实例。
你所有的属性都是实例属性,喜欢
public string CurrentEventName { get { // snip
因此每个实例的所有值都是不同的。您正在创建 两个 个实例,每个实例一个 window。
<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 只是 反序列化 到对象图中的标记。您有两个不同的标记文件,它们包含其中描述的所有内容的不同实例。
这并没有错,拥有一个带有实例属性的视图模型也没有错。事实上,这是优于使用静态和静态绑定的首选方式。
幸好答案很简单。您需要将视图模型的 windows 和 相同实例 都交给。
首先,从你们的 windows 中删除所有 <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;
}
大功告成。
我会将 ViewModel 作为应用程序资源
<Application.Resources>
<VM:ViewModel x:Key="SharedViewModel" />
....
</Application.Resources>
然后在每个window中你这样称呼它
DataContext="{StaticResource MainViewModel}"
我对这个概念还是陌生的,我不确定这是否是最优的。虽然它有效!