所以,我对MVVM很新,并且已经支持自己进入一个有趣的角落,我不知道如何使用行为或命令。我有一个用户控件,其中包含需要实现各种行为的项目列表框,例如删除或删除给定项目。像这样:
<UserControl> // DataContext is a viewmodel
// Borders, grids, various nesting controls...
<ListBox x:Name="ListBox_Items" ItemSource="{Binding ItemsList}">
<ListBox.ItemTemplate>
<DataTemplate> // From here on the individual item has its own data context of type Item in ItemsList
<StackPanel Orientation="Horizontal">
<TextBox Name="EditItemStuffOnLoseFocus" Text="{Binding ItemStuff}"/>
<Button Name="DeleteItemStuff"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
示例已经过简化,但基本思路是文本框在失去焦点时应编辑其关联的列表框项,按钮应在按下时删除关联的列表框项。起初我为此实现了命令并且两者都有效,直到我意识到我忘记了标准“你确定吗?”信息。我在命令中添加了这个,但由于它没有实际对象的概念,我想不出如何告诉它放置对话窗口的位置。该命令在创建时接受视图模型(_ViewModel)并接受Item模型(文本框/按钮的DataContext)作为参数。使用基本消息框对话框,Execute()方法看起来像这样(简化):
public void Execute(object parameter)
{
if (MessageBox.Show("Really delete the item?", "Delete Item", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
ItemService service = new ItemService();
service.RemoveItem(((Item)parameter).ItemID);
if (_ViewModel.ReloadItemListCommand.CanExecute(_ViewModel.ItemInfo))
_ViewModel.ReloadItemListCommand(_ViewModel.ItemInfo);
}
}
当然,这个消息框不是以应用程序为中心,这个小而烦人。同事建议我用一个行为替换命令,以便我有一个关联的对象用于居中消息框。问题是,我无法找到任何关于将参数传递给行为的信息,或者如何从关联对象追溯到其父级的多个级别,以便我可以获得重新加载步骤的视图模型以及单个项目的模型(关联对象的DataContext)。
总之,有没有办法在保持MVVM友好的同时将MessageBox集中在命令中的应用程序上,或者使用行为传递参数/检索特定的父对象或其资源?
____________更新____________
下面的答案很有用,但我选择了另一条路线,以便在MessageBox中使用DataContext变量。我设法通过将视图模型添加到控件的标记来保留对调用控件和视图模型的DataContext的访问:
<UserControl> // DataContext is a viewmodel
// Borders, grids, various nesting controls...
<ListBox x:Name="ListBox_Items" ItemSource="{Binding ItemsList}">
<ListBox.ItemTemplate>
<DataTemplate> // From here on the individual item has its own data context of type Item in ItemsList
<StackPanel Orientation="Horizontal">
<TextBox Name="EditItemStuffOnLoseFocus" Text="{Binding ItemStuff}" Tag={Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext}"/>
<Button Name="DeleteItemStuff" Tag={Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
我不完全确定这是使用Tag的最佳方式,但它确实可以获取我需要的所有信息,同时允许我将MessageBox居中。除了添加一些用于提取模型和视图模型的行外,该行为与命令类似。保持最初的缩短示例,它看起来像这样:
ExampleViewModel viewModel = (ExampleViewModel)AssociatedObject.Tag;
Item parameter = (Item)AssociatedObject.DataContext;
if (MessageBox.Show(Window.GetWindow(AssociatedObject), "Really delete the item?", "Delete Item", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
ItemService service = new ItemService();
service.RemoveItem(((Item)parameter).ItemID);
if (viewModel.ReloadItemListCommand.CanExecute(viewModel.ItemInfo))
viewModel.ReloadItemListCommand(viewModel.ItemInfo);
}
}
谢谢大家的帮助。
答案 0 :(得分:0)
要使窗口上的消息框居中,您需要实现自己的窗口并执行ShowDialog,您可以在其中指定位置。或者,您可以从此CodeProject Solultion中完成的Forms控件继承。
然而,对于问题的第一部分,很可能更容易在按钮上实现单击处理程序并将删除绑定到用户控件作为依赖项属性。这将允许您访问发件人并将UI强制保留在控件内。
XAML
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Name}" />
<Button Click="Button_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
背后的代码
public ICommand DeleteItem
{
get { return (ICommand)GetValue(DeleteItemProperty); }
set { SetValue(DeleteItemProperty, value); }
}
public static readonly DependencyProperty DeleteItemProperty = DependencyProperty.Register("DeleteItem", typeof(ICommand), typeof(control), new PropertyMetadata(null));
private void Button_Click(object sender, RoutedEventArgs e)
{
if (DeleteItem != null)
{
var result = System.Windows.MessageBox.Show("WOULD YOU LIKE TO DELETE?", "Delete", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
var fe = sender as FrameworkElement;
if (DeleteItem.CanExecute(fe.DataContext))
{
DeleteItem.Execute(fe.DataContext);
}
}
}
让你的删除命令从外部绑定,并在click事件中为你的消息框做逻辑。