wpf MVVM 主窗口控件从子用户控件绑定

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15418671/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 08:05:02  来源:igfitidea点击:

MVVM Main window control bind from child user control

wpfmvvmbindingviewmodel

提问by user2066540

I'm very new to MVVM and getting my first POC done now. However, I have been hitting my head to get one issue resolved for 2 days. Thought of explaining to you guys may help and resolve the issue so quickly. Let me brief my issue now. I have WPF MVVM application with a main View bound to MainViewModel. I have Textblock here to bind some content from the viewmodel while loading the screen which is working awesome. I also have ChildUserControl bound to ChildViewModel. Now, I need to bind different content to the Textblock in the main window from user control up on some action which is taking place in user control level. How it is possible ?

我对 MVVM 非常陌生,现在正在完成我的第一个 POC。但是,我一直在努力解决一个问题 2 天。向你们解释的想法可能会帮助并迅速解决问题。现在让我简单介绍一下我的问题。我有 WPF MVVM 应用程序,主视图绑定到 MainViewModel。我在此处使用 Textblock 来绑定视图模型中的一些内容,同时加载工作正常的屏幕。我也将 ChildUserControl 绑定到 ChildViewModel。现在,我需要将不同的内容绑定到主窗口中的 Textblock,从用户控件开始,在用户控件级别发生的某些操作上。怎么可能?

here is the sample code I have MainWindow.Xaml

这是我有 MainWindow.Xaml 的示例代码

<Window.Resources>
    <viewModel:MainViewModel x:Key="mainWindowViewModel"/></Window.Resources>

<TextBlock Name="txtStatus" Text="{Binding StatusMessage, Mode=OneWay }"/>

ChildUserControl.xaml

子用户控件.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
}

Your helps are much appreciated..!

非常感谢您的帮助..!

回答by failedprogramming

An easy way to achieve this would be to use IoC. When you create your child view model, pass a reference of your main view model to your child viewmodel , and hold it as a private read only variable. You now access all of your main VM publics .

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

An alternative solution would ppossibly be to use the Mediator pattern.

另一种解决方案可能是使用 Mediator 模式。

Knocked up a simple App to demonstrate IoC solution.

敲了一个简单的应用程序来演示 IoC 解决方案。

App.xaml.cs

应用程序.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

主窗口视图模型.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

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

视图模型库.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

委托命令.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

主窗口.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

子窗口.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>

Image before clicking on update status

点击更新状态前的图片

Before

前

Image after clicking on updatestatus

单击 updatestatus 后的图像

enter image description here

在此处输入图片说明

回答by dmalicoat

I may be misunderstanding your needs here, but it sounds like you have a TextBlock in the MainWindow that needs to be updated in response to something that occurs in and with data supplied by the ChildWindow. Assuming that's what you're trying to do then I think there are a few different ways that you could do it. In particular, you could use an attached event.

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

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

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

The technical explanation is a bit lengthy, so I'll leave it to MSDN to do that, but the short explanation is simply that it's just an event that will be raised from your child window and handled by your MainWindow. The bonus here is that it will pass in your ChildWindowViewModel as the DataContext.

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