编辑:添加了具体示例,以阐明我想要实现的目标。
这是应用方案:
为了使代码更简单,我将使用简单的 Messenger 类而不是Prism的事件聚合器。元组包含Id和字符串有效负载。
public static class Messenger
{
public static event EventHandler<Tuple<int, string>> DoWork;
public static void RaiseDoWork(int id, string path)
{
DoWork?.Invoke(null, new Tuple<int, string>(id, path));
}
}
模型实例订阅信使以了解何时开始工作(如果Id正确),并在工作完成时通知视图模型。
public class Model
{
public int id;
public Model(int id)
{
this.id = id;
Messenger.DoWork += (sender, tuple) =>
{
if (tuple.Item1 != this.Id)
{
return;
}
var result = tuple.Item2 + " processed with id " + this.id;
this.OnWorkCompleted(result);
};
}
public event EventHandler<string> WorkCompleted;
private void OnWorkCompleted(string path)
{
this.WorkCompleted?.Invoke(null, path);
}
}
UserControlResult
负责有效负载处理和结果输出。为了使代码更简单,只需跟踪输出而不是将其放在UI上。所以XAML将是默认的。
代码隐藏:
public partial class UserControlResult : UserControl
{
private ResultViewModel viewModel;
public UserControlResult()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ResultViewModel(id);
this.DataContext = this.viewModel;
}
}
查看模型:
public class ResultViewModel
{
private Model model;
public ResultViewModel(int id)
{
this.model = new Model(id);
this.model.WorkCompleted += path =>
{
Trace.WriteLine(path);
};
}
}
UserControlButtons
包含按钮,其中一个应该通过信使开始处理UserControlResult
中的模型。为了使代码更简单,我们省略命令实现并只显示其处理程序。
代码隐藏:
public partial class UserControlButtons : UserControl
{
private ButtonsViewModel viewModel;
public UserControlButtons()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ButtonsViewModel(id);
this.DataContext = this.viewModel;
}
}
查看模型:
public class ButtonsViewModel
{
private int id;
public ButtonsViewModel(int id)
{
this.id = id;
}
// DelegateCommand implementation...
private void StartWorkingCommandHandler()
{
Messenger.RaiseDoWork(this.id, "test path");
}
}
UserControlParent
包含UserControlResult
和UserControlButtons
。他唯一的作用是将Id传递给他们,因此他甚至不需要视图模型。
的Xaml:
<StackPanel>
<uc:UserControlResult x:Name="UserControlResult" />
<uc:UserControlButtons x:Name="UserControlButtons" />
</StackPanel>
代码隐藏:
public partial class UserControlParent : UserControl
{
public UserControlParent()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.UserControlResult.Init(id);
this.UserControlButtons.Init(id);
}
}
最后 MainWindow
包含两个UserControlParent
个实例。它的作用是为它们分配不同的ID。
的Xaml:
<StackPanel>
<uc:UserControlParent x:Name="UserControlParent1" />
<uc:UserControlParent x:Name="UserControlParent2" />
</StackPanel>
代码隐藏:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.UserControlParent1.Init(111);
this.UserControlParent2.Init(222);
}
}
这样可行:UserControlButtons
中的按下按钮将开始在UserControlResult
模式中工作,并且UserControlParent
和Init
将正常工作,感谢Id。
但我相信这一系列调用UserControl
方法违反了MVVM ,因为代码隐藏(MVVM中的 View )应 了解有关Id值的任何信息(相对于MVVM中的 Model )。说到这一点,我确定Id不是视图模型的一部分,因为它在UI中没有任何演示文稿。
如何将顶部窗口中的Id值传递给&#34;最深的&#34;视图模型没有违反MVVM?
这是由3 UserControl3
s:
UserControl2
是UserControl3
内容的一部分。我在开发和使用 Prism 期间保留 MVVM 。
我需要在UserControl1
的 view-model UserControl1
中调用自定义类的方法(就MVVM来说是 model ) 。自定义类不能成为单例的限制。我想以下列方式之一做到这一点:
使用Prism的事件聚合器。 UserControl3
视图模型是发布者,Window
模型是订阅者。为此,我需要在UserControl1
中创建唯一身份证并将其传递给UserControl3
和Window
。
在UserControl1
中创建服务实例,并将其传递给UserControl3
和UserControl1
。然后Window
将调用此实例的方法。
UserControl2
将UserControl1
个实例传递给UserControl1
。 UserControl2
中的视图模型只会调用UserControl3
的方法,该方法将调用DECLARE @Moment AS VARCHAR (6) = '200901';
SELECT CONVERT(VARCHAR(3), CAST(@Moment + '01' AS DATETIME), 100) + '-' +
RIGHT(CONVERT(VARCHAR(8), CAST(@Moment + '01' AS DATETIME), 1), 2);
-- OUTPUT Jan-09
的方法,依此类推。
似乎有2个和3个方法违反了MVVM。你会如何解决这种情况?
答案 0 :(得分:0)
我会使用选项1.我使用MVVM Light发送消息,任何接收该特定消息的人都将触发服务方法。松散耦合。
答案 1 :(得分:0)
我认为我实现了真正的MVVM实现,如下面的简化示例所示。特别感谢Ed Plunkett的comment和Nikita&#39; answer。
首先,我不再需要传递唯一的ID。为了识别不同的ParentViewModel
个实例,我只是传递了不同的Messenger
实例(为了简单起见,它取代了 Prism&#39; EventAggregator ):
internal class Messenger
{
public event EventHandler<string> DoWork;
public void RaiseDoWork(string path)
{
this.DoWork?.Invoke(this, path);
}
}
其次,在我的特定情况下,模型似乎不应该担心Messenger
的{{1}}事件。只要在一个视图模型(DoWork
)中引发此事件,此事件就更适合由另一个视图模型(ButtonsViewModel
)而不是ResultViewModel
本身使用。所以Model
也简化了:
Model
下面展示了所有视图模型&#34;从上到下&#34;。
internal class Model
{
public string Process(string input)
{
return input + " processed!";
}
}
请注意,在internal class MainViewModel
{
private readonly Messenger eventAggregator1 = new Messenger();
private readonly Messenger eventAggregator2 = new Messenger();
public MainViewModel()
{
this.ParentViewModel1 = new ParentViewModel(this.eventAggregator1);
this.ParentViewModel2 = new ParentViewModel(this.eventAggregator2);
}
public ParentViewModel ParentViewModel1 { get; }
public ParentViewModel ParentViewModel2 { get; }
}
internal class ParentViewModel
{
public ParentViewModel(Messenger eventAggregator)
{
this.ButtonsViewModel = new ButtonsViewModel(eventAggregator);
this.ResultViewModel = new ResultViewModel(eventAggregator);
}
public ButtonsViewModel ButtonsViewModel { get; }
public ResultViewModel ResultViewModel { get; }
}
internal class ButtonsViewModel
{
private readonly Messenger eventAggregator;
public ButtonsViewModel(Messenger eventAggregator)
{
this.eventAggregator = eventAggregator;
this.StartCommand = new DelegateCommand(this.StartProcessing);
}
public DelegateCommand StartCommand { get; }
private void StartProcessing()
{
this.eventAggregator.RaiseDoWork("test path");
}
}
internal class ResultViewModel : ViewModelBase
{
private readonly Model model = new Model();
private string textValue;
public ResultViewModel(Messenger eventAggregator)
{
eventAggregator.DoWork += (sender, s) => this.DoWorkHandler(s);
}
public string TextValue
{
get { return this.textValue; }
set { this.SetProperty(ref this.textValue, value); }
}
private void DoWorkHandler(string s)
{
var result = this.model.Process(s);
this.TextValue = result;
}
}
中,我用实际屏幕输出替换了ResultViewModel
(因为现在字符串没有Id,因此跟踪输出相同)。 Trace.WriteLine
只是实施ViewModelBase
。
下面演示了所有观看的内容部分&#34;从上到下&#34;。
INotifyPropertyChanged
最后这两个世界在App.xaml.cs中连接:
<!-- MainWindow.xaml -->
<StackPanel Orientation="Horizontal">
<views:UserControlParent DataContext="{Binding ParentViewModel1}" />
<views:UserControlParent DataContext="{Binding ParentViewModel2}" />
</StackPanel>
<!-- UserControlParent.xaml -->
<StackPanel>
<local:UserControlResult DataContext="{Binding ResultViewModel}" />
<local:UserControlButtons DataContext="{Binding ButtonsViewModel}" />
</StackPanel>
<!-- UserControlButtons.xaml -->
<Grid>
<Button Content="Test" Command="{Binding StartCommand}" />
</Grid>
<!-- UserControlResult.xaml -->
<Grid>
<TextBlock Text="{Binding TextValue}" />
</Grid>
似乎像MVVM,但欢迎任何评论。