c# - 将模型绑定(bind)到 View ,但按钮单击工作

标签 c# mvvm icommand

全部,
绑定(bind)的完整模型,值显示在控件中,但无法让按钮单击工作......有什么建议吗?我错过了什么或做错了什么?谢谢

<Window x:Class="test" Title="test" Height="350" Width="525">
    <StackPanel Name="abc" Orientation="Vertical" DataContext="{Binding Path=EMP, Mode=TwoWay}" Margin="4" Height="153">
        <Label Content="Last Name:" Margin="0,0,4,0"/>
        <TextBox Width="250" Text="{Binding Path=LastName}" Height="20"/>
        <Button Grid.Row="2" Margin="0,0,4,0" Height="40" Width="40" 
                 Command="{Binding Path=SaveCommand}" />
    </StackPanel>
</Window>

class EmployeeVM: ViewModelBase
{
    private bool _Execute = true;
    public EmployeeVM()
    {
        emp = new Model.Employee { FirstName = "abc", LastName = "xyz" };
    }
    private string sFirstName;
    private string sLastName;
    private Model.Employee emp;

    public Model.Employee EMP
    {
        get{return emp;}
        set{emp = value;
            OnPropertyChanged("EMP");}
    }
    public string LastName
    {
        get { return sLastName; }
        set 
        { 
            sLastName = value;
            OnPropertyChanged("LastName");
        }
    }

    #region Commands
    private ICommand _SaveCommand;
    public ICommand SaveCommand
    {
        get
        {
            return _SaveCommand = new CommandHandler(Save, _Execute);
        }
    }
    #endregion

    private void Save(object param)
    {
        ObservableCollection<Model.Employee> newIM = new ObservableCollection<Model.Employee>();
        foreach(Model.Employee e in newIM)
        {
            string a = e.FirstName;
            string b = e.LastName;
        }
    }
}


public class CommandHandler : ICommand
{
    Action<object> _act;
    bool _canExecute;

    public CommandHandler(Action<object> act, bool canExecute)
    {
        _act = act;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _act(parameter);
    }
}

最佳答案

您可以编写自己的命令。

这是我用于命令的基类。

它有一些非常基本的东西,让生活更轻松。

  • Execute方法接受一个对象,因此您将能够传递数组
  • 可以很容易地传入一个 View 模型,即您将在命令中使用的 View 模型(大多数情况下,如果您不需要它,请将其滑出)
  • 更改后的处理程序利用 CommandManager。这真的很有帮助

  • 也许你想改变一些事情。我添加的所有内容都在那里,因为它非常有帮助。 (尤其是 View 模型)
    public abstract class CommandBase : ICommand
    {
        public abstract bool CanExecute(object o);
        public abstract void Execute(object o);
    
        public PropertyChangedBase ViewModel { get; set; }
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }
    

    你会有像这样的实现
    public class ExampleCommand : CommandBase
    {
        public ExampleCommand (PropertyChangedBase viewModel)
        {
            this.ViewModel = viewModel;
        }
    
        public override void Execute(object o)
        {
            // something like
            var settings = UnityContainer.Resolve<ISettings>();
            settings.MagicValue = (this.ViewModel as ConcreteViewModel).MagicValue;
        }
    
        public override bool CanExecute(object o)
        {
            return true;
        }
    }
    

    在您的 ViewModel 中,您通过拥有一个属性将命令公开给 View :
    public class ExampleViewModel : PropertyChangedBase
    {
        public ExampleViewModel ()
        {
            this.DoThisAndThatCommand = new ExampleCommand(this);
        }
    
        public CommandBase DoThisAndThatCommand { get; set; }
    }
    
    
    // and in XAML, you can use it like
    <Button x:Name="Ok"
                Command="{Binding DoThisAndThatCommand }" />
    

    (假设您通过设置 ViewModelView 正确连接了 DataContextView)

    现在,每当单击按钮时,都会调用命令的执行方法。

    您的 ViewModel 位于 Command ,因此您可以轻松地使用它。

    在命令内或 ViewModel 内有按钮是非常不寻常的。 .
    MVVM 的诀窍是将View 分开。来自 ViewModel并且没有UIElementsViewModel .

    如果您没有 PropertyChangedBase(这个是 Caliburn.Micro 附带的),那么我建议您使用一些简单的 INotifyPropertyChanged 实现。

    I found this one here, should be german though

    公共(public)抽象类 NotifyPropertyChangedBase : INotifyPropertyChanged
    {
    #region < INotifyPropertyChanged > 成员
        /// <summary>
        /// Is connected to a method which handle changes to a property (located in the WPF Data Binding Engine)
        /// </summary>
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        /// <summary>
        /// Raise the [PropertyChanged] event
        /// </summary>
        /// <param name="propertyName">The name of the property</param>
    
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
    
        #endregion
    
        private Dictionary<string, object> propertyValueStorage;
    
    
        #region Constructor
    
        public NotifyPropertyChangedBase()
        {
            this.propertyValueStorage = new Dictionary<string, object>();
        }
    
    
        #endregion
    
    
    
        /// <summary>
        /// Set the value of the property and raise the [PropertyChanged] event
        /// (only if the saved value and the new value are not equal)
        /// </summary>
    
        /// <typeparam name="T">The property type</typeparam>
        /// <param name="property">The property as a lambda expression</param>
        /// <param name="value">The new value of the property</param>
    
        protected void SetValue<T>(Expression<Func<T>> property, T value)
        {
            LambdaExpression lambdaExpression = property as LambdaExpression;
    
            if (lambdaExpression == null)
            {
                throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
            }
    
            string propertyName = this.getPropertyName(lambdaExpression);
    
            T storedValue = this.getValue<T>(propertyName);
    
            if (!object.Equals(storedValue, value))
            {
                this.propertyValueStorage[propertyName] = value;
    
                this.OnPropertyChanged(propertyName);
            }
        }
    
    
        /// <summary> Get the value of the property </summary>
        /// <typeparam name="T">The property type</typeparam>
        /// <param name="property">The property as a lambda expression</param>
        /// <returns>The value of the given property (or the default value)</returns>
        protected T GetValue<T>(Expression<Func<T>> property)
        {
            LambdaExpression lambdaExpression = property as LambdaExpression;
    
            if (lambdaExpression == null)
            {
                throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
            }
    
            string propertyName = this.getPropertyName(lambdaExpression);
            return getValue<T>(propertyName);
        }
    
        /// <summary>
        /// Try to get the value from the internal dictionary of the given property name
        /// </summary>
        /// <typeparam name="T">The property type</typeparam>
        /// <param name="propertyName">The name of the property</param>
        /// <returns>Retrieve the value from the internal dictionary</returns>
    
        private T getValue<T>(string propertyName)
        {
            object value;
    
            if (propertyValueStorage.TryGetValue(propertyName, out value))
            {
                return (T)value;
            }
            else
            {
                return default(T);
            }
        }
    
    
        /// <summary>
        /// Extract the property name from a lambda expression
        /// </summary>
        /// <param name="lambdaExpression">The lambda expression with the property</param>
        /// <returns>The extracted property name</returns>
        private string getPropertyName(LambdaExpression lambdaExpression)
        {
            MemberExpression memberExpression;
    
            if (lambdaExpression.Body is UnaryExpression)
            {
                var unaryExpression = lambdaExpression.Body as UnaryExpression;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }
            else
            {
                memberExpression = lambdaExpression.Body as MemberExpression;
            }
    
            return memberExpression.Member.Name;
        }
    } 
    

    这是非常容易使用!

    在您的 ViewModel 中,您必须为 Binding 提供公共(public)属性(这很重要)并发出更改通知。

    这是一个如何使用 INPC (INotifyPropertyChanged) 的基本实现的示例
    public class LoginViewModel : NotifyPropertyChangedBase
    {
        public string UserName { get;set; }
    }
    

    这个 INPC 实现会为您调用 NotifyOfPropertyChange,您不必关心它!但是您必须检查最适合您的情况。

    在您的问题中,您已经有一个 ViewModelBase。也许您想使用这个而不是上面的。

    关于c# - 将模型绑定(bind)到 View ,但按钮单击工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16654652/

    相关文章:

    c# - WPF 和 MVVM Light - 在 UserControl 中动态显示用户控件

    c# - 你必须使用命令来遵循 MVVM 模式吗?

    c# - 如何在ListView WPF中将命令绑定(bind)到复选框?

    WPF/MVVM - 我们应该为每个 ViewModel 创建一个不同的类吗?

    xaml - Xamarin 可重复使用的 xaml 用户控件和自定义命令

    c# - 使用 Json.net 将非数组 JSON 对象解析为数组

    c# - 我们可以有一个多页的 JPEG 图像吗?

    c# - 将 linq 与 PropertyInfo 结合使用

    c# - asp.net core依赖注入(inject)配置

    jquery - 为什么这个点击绑定(bind)没有被绑定(bind)?