我是MVVM的新手,现在已经完成了我的第一个POC。但是,我一直在努力让一个问题解决2天。想解释给你们的人可能会帮助并迅速解决问题。我现在简要介绍一下我的问题。 我有WPF MVVM应用程序,主视图绑定到MainViewModel。我在这里有Textblock来绑定视图模型中的一些内容,同时加载屏幕工作真棒。 我还将ChildUserControl绑定到ChildViewModel。现在,我需要将不同的内容绑定到主窗口中的Textblock,以防止用户控制在用户控件级别发生的某些操作。怎么可能?
这是我的示例代码 MainWindow.Xaml
<Window.Resources>
<viewModel:MainViewModel x:Key="mainWindowViewModel"/></Window.Resources>
<TextBlock Name="txtStatus" Text="{Binding StatusMessage, Mode=OneWay }"/>
ChildUserControl.xaml
<UserControl.Resources>
<viewModel:ChildModelView x:Key="ChildModelView"/> </UserControl.Resources>
public class ChildModelView : BaseViewModel
{
// Some child level logic..
// then need to update the txtStatus text block from parent
}
非常感谢您的帮助..!
答案 0 :(得分:5)
实现这一目标的简单方法是使用IoC。创建子视图模型时,将主视图模型的引用传递给子视图模型,并将其作为私有只读变量保存。您现在可以访问所有主要VM公共。
另一种解决方案可能是使用Mediator模式。
敲了一个简单的App来演示IoC解决方案。
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var window = new MainWindow() {DataContext = new MainWindowViewModel() };
window.Show();
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : ViewModelBase
{
private string _statusMessage;
public string StatusMessage
{
get { return _statusMessage; }
set { _statusMessage = value; this.OnPropertyChanged("StatusMessage"); }
}
public ICommand OpenChildCommand { get; private set; }
public MainWindowViewModel()
{
this.StatusMessage = "No status";
this.OpenChildCommand = new DelegateCommand((o) => this.OpenChild());
}
private void OpenChild()
{
var window = new ChildWindow {DataContext = new ChildWindowViewModel(this)};
window.Show();
}
}
ChildWindowViewModel.cs
public class ChildWindowViewModel : ViewModelBase
{
private readonly MainWindowViewModel _mainvm;
public ChildWindowViewModel(MainWindowViewModel mainvm)
{
_mainvm = mainvm;
this.UpdateStatusCommand = new DelegateCommand((o) => this.UpdateStatus());
}
public ICommand UpdateStatusCommand { get; private set; }
private void UpdateStatus()
{
this._mainvm.StatusMessage = "New Status";
}
}
ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
DelegateCommand.cs
public class DelegateCommand : ICommand
{
/// <summary>
/// Action to be performed when this command is executed
/// </summary>
private Action<object> executionAction;
/// <summary>
/// Predicate to determine if the command is valid for execution
/// </summary>
private Predicate<object> canExecutePredicate;
/// <summary>
/// Initializes a new instance of the DelegateCommand class.
/// The command will always be valid for execution.
/// </summary>
/// <param name="execute">The delegate to call on execution</param>
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Initializes a new instance of the DelegateCommand class.
/// </summary>
/// <param name="execute">The delegate to call on execution</param>
/// <param name="canExecute">The predicate to determine if command is valid for execution</param>
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.executionAction = execute;
this.canExecutePredicate = canExecute;
}
/// <summary>
/// Raised when CanExecute is changed
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the delegate backing this DelegateCommand
/// </summary>
/// <param name="parameter">parameter to pass to predicate</param>
/// <returns>True if command is valid for execution</returns>
public bool CanExecute(object parameter)
{
return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter);
}
/// <summary>
/// Executes the delegate backing this DelegateCommand
/// </summary>
/// <param name="parameter">parameter to pass to delegate</param>
/// <exception cref="InvalidOperationException">Thrown if CanExecute returns false</exception>
public void Execute(object parameter)
{
if (!this.CanExecute(parameter))
{
throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
}
this.executionAction(parameter);
}
}
MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBlock Text="{Binding StatusMessage, Mode=OneWay}" />
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Open Child Window"
Command="{Binding Path=OpenChildCommand}"/>
</StackPanel>
ChildWindow.xaml
<Window x:Class="WpfApplication2.ChildWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChildWindow" Height="300" Width="300">
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="UpdateStatus"
Command="{Binding Path=UpdateStatusCommand}" />
</Grid>
点击更新状态前的图片
点击updatestatus后的图片
答案 1 :(得分:0)
我可能在这里误解了你的需求,但听起来你在MainWindow中有一个TextBlock,需要更新以响应ChildWindow提供的数据。假设这是你想要做的事情,那么我认为有几种不同的方法可以做到。特别是,您可以使用附加事件。
http://msdn.microsoft.com/en-us/library/bb613550.aspx
技术说明有点冗长,所以我会把它留给MSDN去做,但简短的解释就是它只是一个从你的子窗口引出并由你的MainWindow处理的事件。这里的好处是它将传递你的ChildWindowViewModel作为DataContext。