【问题标题】:MVVM Main window control bind from child user controlMVVM 主窗口控件从子用户控件绑定
【发布时间】:2013-03-14 19:38:41
【问题描述】:

我对 MVVM 非常陌生,现在我已经完成了我的第一个 POC。但是,我一直在努力解决一个问题 2 天。考虑向你们解释可能会帮助并很快解决问题。现在让我简要介绍一下我的问题。 我有一个绑定到 MainViewModel 的主视图的 WPF MVVM 应用程序。我在这里有 Textblock 可以在加载屏幕时绑定视图模型中的一些内容,这非常棒。 我也将 ChildUserControl 绑定到 ChildViewModel。现在,我需要将不同的内容绑定到主窗口中的文本块,从用户控制开始,在用户控制级别发生的一些操作上。怎么可能?

这是我的示例代码 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
}

非常感谢您的帮助..!

【问题讨论】:

    标签: wpf mvvm binding viewmodel


    【解决方案1】:

    实现此目的的一种简单方法是使用 IoC。创建子视图模型时,将主视图模型的引用传递给子视图模型,并将其作为私有只读变量保存。您现在可以访问所有主要的 VM publics 。

    另一种解决方案可能是使用中介者模式。

    开发了一个简单的 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后的图片

    【讨论】:

    • 我在启动 ChildVM 时确实通过了主视图模型并设置了 MainVM 属性一些值。但它并没有反映在页面上..不知道为什么。
    • 您是否在 mainvm 上实现 INPC 并在 mainvm 属性上调用 propertychanged?
    • 这是我在 MainMV 中做的事情 public string StatusMessage { get { return statusMsg; } 设置 { statusMsg = 值; OnPropertyChanged("状态消息"); } } 然后在初始化子虚拟机时我正在做 ChildVM(mainVM vm) { _main = vm; _main.StatusMessage = "新状态";方法对吗?
    • 是的,这是正确的。您是否还将mainvm分配给主窗口的DataContext?在您的 App.xaml.cs 中,您应该覆盖 OnStartup 并创建您的 mainvm,然后创建您的 mainwindow 并将 mainvm 分配给 datacontext。即 var window = new MainWindow { DataContext = new MainViewModel() };窗口.Show();
    • 我可以看到您的资源中有您的主视图模型,但我不认为它会以这种方式工作。我得去上班了,但大约一个小时后会回来检查您是否需要更多帮助
    【解决方案2】:

    我可能在这里误解了您的需求,但听起来您在 MainWindow 中有一个 TextBlock,需要更新它以响应发生在 ChildWindow 中的数据以及由 ChildWindow 提供的数据。假设这就是你想要做的,那么我认为你可以通过几种不同的方式来做到这一点。特别是,您可以使用附加事件。

    http://msdn.microsoft.com/en-us/library/bb613550.aspx

    技术解释有点冗长,所以我将把它留给 MSDN 来做,但简短的解释很简单,它只是一个将从您的子窗口引发并由您的 MainWindow 处理的事件。这里的好处是它将作为 DataContext 传入您的 ChildWindowViewModel。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-17
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多