我开始在现有的WPF c#应用程序中实现MVVM设计模式。我是全新的,以前从未使用过设计模式或依赖注入。我正在研究已经可用的框架并采用了MVVM灯。我将逻辑从视图移动到viewmodel。我在PopulateTestMenu
中有很多与视图模型中的UI相关的代码。它还调用了事件处理程序。我该如何处理这个?
在 XAML 中我有:
<Window DataContext="{Binding Main, Source={StaticResource Locator}}">
<Menu>
<MenuItem Header="Load All History..." Command="{Binding LoadAllHistory}">
在 MainViewModel 类中,我有:
public ICommand LoadAllHistory { get; private set; }
public MainViewModel()
{
LoadAllHistory = new RelayCommand(() => LoadHistoryExecute(), () => true);
}
我从视图移至 viewmodel 的代码:
private void LoadHistoryExecute()
{
try
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Test History File (*.xml)|*.xml";
ofd.Title = "Open Test History";
ofd.Multiselect = true;
if (ofd.ShowDialog() == true)
{
ThreadPool.QueueUserWorkItem(LoadTestHistoryCallback, ofd.FileNames);
}
}
catch
{
//some code
}
}
private void LoadTestHistoryCallback(object state)
{
try
{
string[] fileNames = (string[])state;
foreach (string fileName in fileNames)
{
bool success = MyApp.Instance.ParseTestHistory(fileName);
string status = success
? String.Format("'{0}' loaded successfully.",
System.IO.Path.GetFileName(fileName))
: String.Format("Failed to load history from '{0}'.",
System.IO.Path.GetFileName(fileName));
Dispatcher.CurrentDispatcher.DynamicInvoke(delegate()
{
Status = status;
});
PopulateTestMenu(new SortedList<int, int>());
}
}
catch
{
//some code
}
}
private void PopulateTestMenu(SortedList<int, int> indexes)
{
try
{
_testMenuMutex.WaitOne();
//Populate the Tests menu with the list of tests.
Dispatcher.CurrentDispatcher.DynamicInvoke(delegate()
{
menuTests.Items.Clear();
var checkEventHandler = new RoutedEventHandler(testMenuItem_Checked);
bool added = false;
if (MyApp.Instance.TestHistory != null &&
MyApp.Instance.TestHistory.Count > 0)
{
List<ushort> subIds = new
List<ushort>MyApp.Instance.TestHistory.Keys);
foreach (ushort subId in subIds)
{
MenuItem menuItem = null;
menuItem = new MenuItem();
menuItem.Header = subId.ToString().PadLeft(5, '0');**
MenuItem none = new MenuItem();
none.Header = "None";
none.IsCheckable = true;
none.IsChecked = true;
none.Checked += checkEventHandler;
none.Unchecked += checkEventHandler;
menuItem.Items.Add(none);
if (MyApp.Instance.TestHistory != null &&
MyApp.Instance.TestHistory.ContainsKey(subId))
{
var tests = MyApp.Instance.TestHistory[subId];
if (tests != null)
{
foreach (Test t in tests)
{
MenuItem item = new MenuItem();
item.IsCheckable = true;
string description = t.Description.Replace("\n",
"\n".PadRight(34, ' '));
string header = abc;
item.Header = header;
item.DataContext = t;
item.Checked += checkEventHandler;
item.Unchecked += checkEventHandler;
menuItem.Items.Add(item);
}
if (tests.Count > 0)
{
menuTests.Items.Add(menuItem);
added = true;
}
}
}
// Carry over the previous selection.
if (indexes.ContainsKey(subId) && indexes[subId] > -1)
{ ((MenuItem)menuItem.Items[indexes[subId]]).IsChecked =
true;
}
}
}
答案 0 :(得分:2)
我仍然想弄清楚你在问什么=)......
但是你混淆了一些东西......记住MVVM的一个核心概念是使viewmodel可测试并从viewmodel中删除所有与视图相关的代码。所以根本不依赖于WPF。因此,MenuItem
看起来像WPF MenuItem
,不应该在您的ViewModel
中。
相反,你可以考虑制作一个MenuItemViewModel
,它绑定到视图中的MenuItem
。而且我可以看到ObservableCollection<MenuItemViewModel> TestMenu
而不是您的排序列表。
在您的方法LoadTestHistoryCallback
中,您可以MenuItemViewModel
实现(可以通过DI完成)并将其添加到TestMenu集合中。 MenuItemViewModel
可能具有status
属性,可以从外部或内部分配。 (它还可以有一些额外的逻辑,嘿它是一个视图模型)。
然后,您可以在View
中将其绑定到一个列表,其中的模板通过DataBinding表示MenuItem
。
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" />
因此,请记住ViewModel
也可以包含ViewModels
或collections
viewmodel
。
使用WPF的rich databinding api。
使用ObservebaleCollections
或Properties
等可绑定属性,这些属性通过PropertyChanged
通知进行了扩展。
HTH
PS:然后,您可以在ICommand
中点击MenuItemViewModel
并执行操作,或者更好地使用EventAggregator
或Messenger
来通知其他ViewModels
。 ..(但那是另一个问题的故事=)......)
答案 1 :(得分:0)
您已经通过将代码移动到ViewModel来正确应用MVVM理论,但请记住,View应该只提供显示的“结构”。
显示的内容由ViewModel中的模型提供。
考虑到这一点,将ViewModel方法中的菜单部分分开并将它们放在View中,但保留Test对象创建部分(将ViewModel对象绑定到View结构就是它的内容)。
在PopulateTestMenu方法中,需要在View中指定菜单和菜单结构,而填充它们的数据需要在ViewModel中创建和格式化。
在视图中,您将相应的对象部分绑定到菜单结构,当模型绑定到视图时,ViewModel将自动用模型对象填充它。
查看代码,看来Test对象是ViewModel,需要在View中创建Menu和MenuItem结构,然后指定Test对象的特定属性与特定结构部分的绑定视图中的菜单。