我是Wpf的新手,并遵循MVVM模式来设计我的应用程序。我有一个ViewModel和一个View。通过设计,我的View了解我的ViewModel而反之则不然。如果我想在点击按钮时打开另一个窗口,我可以使用命令,当用户点击时,该命令将被路由到我的ViewModel。
但是我如何创建一个拥有自己的视图和ViewModel的窗口。我对此感到困惑。我想从Xaml本身调用新窗口,因为我的ViewModel不知道另一个视图,可能是它的viewmodel。
为了简单起见,
View1 -> (bound to) ViewModel1
View1 -> Button1.Click -> Command(bound to) ViewModel1
---- how to open View2 -> (bound to) ViewModel2------
Show View2.
由于
答案 0 :(得分:2)
我只需从View 1的代码隐藏中调用显示即可打开View 2.不应参与View 1中的ViewModel。
像这样:
private void Button_Click(object sender, RoutedEventArgs e)
{
new View2().Show();
}
你的下一个想法可能是“这不是违反MVVM吗?”不,我不会这么说。如果要将ViewModel视为MVVM的干净实现,则ViewModel不应该直接控制UI元素。如果您希望ViewModel中的某些内容触发Window打开,您可以始终使用事件或第三方库(如Caliburn)轻松实现此功能,而无需担心耦合。
答案 1 :(得分:1)
这就是我建议这样做的原因。因为在另一个视图模型中没有依赖视图。只需要编写一个对话框服务来打开任何视图作为弹出窗口(窗口)。
normaly Show()
方法使用System.Windows
依赖性以便更好地用作服务,然后我们可以对我们的应用程序进行单元测试。
public interface IDialogService
{
BaseViewModel ShowTitleDialog(BaseViewModel viewModel, UserControl view, string windowTitle);
}
public class DialogService : IDialogService
{
public BaseViewModel ShowTitleDialog(BaseViewModel viewModel, UserControl view, string windowTitle)
{
if (view == null && viewModel == null)
{
throw new ArgumentNullException("view is null");
}
RegisterTemplate(viewModel.GetType(), view.GetType());
return ShowTitleDialog(viewModel, windowTitle);
}
private void RegisterTemplate(Type viewModelType, Type viewType)
{
const string xamlTemplate = "<DataTemplate DataType=\"{{x:Type vm:{0}}}\"><v:{1} /></DataTemplate>";
var xaml = String.Format(System.Globalization.CultureInfo.InvariantCulture, xamlTemplate, viewModelType.Name, viewType.Name, viewModelType.Namespace, viewType.Namespace);
var context = new ParserContext();
context.XamlTypeMapper = new XamlTypeMapper(new string[0]);
context.XamlTypeMapper.AddMappingProcessingInstruction("vm", viewModelType.Namespace, viewModelType.Assembly.FullName);
context.XamlTypeMapper.AddMappingProcessingInstruction("v", viewType.Namespace, viewType.Assembly.FullName);
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
context.XmlnsDictionary.Add("vm", "vm");
context.XmlnsDictionary.Add("v", "v");
var template = (DataTemplate)System.Windows.Markup.XamlReader.Parse(xaml, context);
var key = template.DataTemplateKey;
Application.Current.Resources[key] = template;
}
public BaseViewModel ShowTitleDialog(BaseViewModel viewModel, string windowTitle, bool isEnableScrollBars = false)
{
Window window = new Window();
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
ContentControl contentControl = new ContentControl();
contentControl.Content = viewModel;
if (!isEnableScrollBars)
{
window.Content = contentControl;
window.SizeToContent = SizeToContent.WidthAndHeight; window.SizeToContent = SizeToContent.WidthAndHeight;
}
else
{
ScrollViewer scrollViewver = new ScrollViewer();
scrollViewver.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
scrollViewver.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
scrollViewver.Content = contentControl;
window.Height = 600;
window.MaxHeight = 600;
window.Content = scrollViewver;
}
window.Title = windowTitle;
window.WindowStyle = WindowStyle.None;
window.Owner = Application.Current.Windows.OfType<System.Windows.Window>().FirstOrDefault(x => x.IsMouseOver);
window.ShowInTaskbar = false;
window.ShowDialog();
return viewModel;
}
}
用法..
class TestAViewModel
{
public void ClickEvent()
{
TestBViewModel viewModel = new TestBViewModel();
viewModel.ShowThisView();
}
}
class TestBViewModel
{
public void ShowThisView()
{
TestBViewModel viewModel = new TestBViewModel();
TestBView view = new TestBView();
view.DataContext = viewModel;
IDialogService dialogCheckIn = new DialogService();
dialogCheckIn.ShowTitleDialog(viewModel, view, "Title");
}
}