将usercontrol中包含的WPF按钮与其自己的viewmodel绑定到主窗口视图模型中的函数

时间:2017-06-09 19:34:04

标签: c# wpf mvvm data-binding

我有一个简单的窗口,里面有一个UserControl“NewCardView”。取消按钮需要调用MainWindow视图模型中包含的函数,以便切换当前显示的UserControl。当我按下按钮时它什么也没做,没有什么可追查的,就像它没有任何约束力一样。我已经尝试绑定到该窗口的datacontext但我没有成功。

 <UserControl x:Class="MapProject.View.NewCardView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:MapProject.MainView.View"
                 xmlns:mainwin="clr-namespace:MapProject"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid>
            <Label x:Name="labeltitle" Content="New Card" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0"/>
            <ComboBox x:Name="comboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="133,43,0,0"/>
            <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="133,74,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="120"/>
            <TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="133,105,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="120"/>
            <DatePicker HorizontalAlignment="Left" VerticalAlignment="Top" Margin="133,136,0,0" Width="120"/>
            <Label x:Name="label" Content="District" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="62,43,0,0"/>
            <Label x:Name="label1" Content="Map #" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="62,74,0,0"/>
            <Label x:Name="label2" Content="Parcel #" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="62,105,0,0"/>
            <Label x:Name="label3" Content="Date From" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="62,136,0,0"/>
            <Button x:Name="button" Content="OK" HorizontalAlignment="Left" Margin="78,184,0,0" VerticalAlignment="Top" Width="75"/>
            <Button x:Name="button_Copy" Content="Cancel" CommandParameter="Blank" Command="{Binding DataContext.ChangePageCommand, BindsDirectlyToSource=True, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type mainwin:MainWindow}}}" HorizontalAlignment="Left" Margin="178,184,0,0" VerticalAlignment="Top" Width="75"/>

        </Grid>

    public ICommand ChangePageCommand
    {
        get
        {
            if (_changePageCommand == null)
            {
                _changePageCommand = new RelayCommand(p => ChangeViewModel(p.ToString()), p => p is string);
            }

            return _changePageCommand;
        }

}

MainWindow包含此

<DataTemplate DataType="{x:Type mainviewviewmodel:NewCardViewModel}">
    <mainview:NewCardView/>
</DataTemplate>
using System;
using System.Diagnostics;
using System.Windows.Input;

    namespace MapProject
    {
        /// <summary>
        /// A command whose sole purpose is to 
        /// relay its functionality to other
        /// objects by invoking delegates. The
        /// default return value for the CanExecute
        /// method is 'true'.
        /// </summary>
        public class RelayCommand : ICommand
        {
            #region Fields

            readonly Action<object> _execute;
            readonly Predicate<object> _canExecute;

            #endregion // Fields

            #region Constructors

            /// <summary>
            /// Creates a new command that can always execute.
            /// </summary>
            /// <param name="execute">The execution logic.</param>
            public RelayCommand(Action<object> execute)
                : this(execute, null)
            {
            }

            /// <summary>
            /// Creates a new command.
            /// </summary>
            /// <param name="execute">The execution logic.</param>
            /// <param name="canExecute">The execution status logic.</param>
            public RelayCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                    throw new ArgumentNullException("execute");

                _execute = execute;
                _canExecute = canExecute;
            }

            #endregion // Constructors

            #region ICommand Members

            [DebuggerStepThrough]
            public bool CanExecute(object parameters)
            {
                return _canExecute == null ? true : _canExecute(parameters);
            }

            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }

            public void Execute(object parameters)
            {
                _execute(parameters);
            }

            #endregion // ICommand Members
        }
    }

1 个答案:

答案 0 :(得分:0)

正如Clemens正确建议的那样,最好的解决方案是将 ChangePageCommand 命令保留在 NewCardViewModel 级别。创建NewCardViewModel时,初始化ChangePageCommand命令,以便从MainViewModel 调用方法:

public class MainViewModel {
    public void CreateNewCard() {
        NewCardViewModel newCardModel = new NewCardViewModel();
        newCardModel.ChangePageCommand = new RelyCommand(Cancel);
        //...
    }
    void Cancel() {

    }
}

public class NewCardViewModel {
    public ICommand ChangePageCommand {
        get;
        set;
    }
}

在这种情况下,以下简单绑定应该正常工作:

<Button Command="{Binding ChangePageCommand}" ... />

如果您不希望以这种方式在MainViewModel中初始化NewCardViewModel,则可以使用free DevExpress MVVM Framework制作子视图模型aware of their parents

使用相同MVVM框架的另一种解决方案是使用Messenger在不同的视图模型之间进行通信。使用这种方法,您可以在MainViewModel级别注册一个操作,并从NewCardViewModel调用它,而不会创建任何强依赖项甚至接口。