文本框和按钮 - 绑定和命令

3
我正在使用MVVM模式。我有一个文本框,其Text属性绑定到ViewModel的Text属性(VM支持INotifyProperyChange)。我还有一个按钮,其命令绑定到VM的ICommand属性类型。
你可以将其视为SearchTextBox和SearchButton。
我遇到的问题是,当我在SearchTextBox中输入文本并单击SearchButton时,只调用了SearchTextBox绑定的set属性实现,但SearchButton点击的Command从未执行(注意:ICommand CanExecute处理程序始终返回True)。
如果我使用TAB键或使用鼠标将焦点从SearchTextBox移开,然后再单击SearchButton,它就可以正常工作。这意味着需要两个单独的操作来分别触发这两个事件。理想情况下,单击SearchButton应导致SearchTextBox失去焦点,从而调用Set属性,并且单击Search按钮会转换为命令执行。
代码如下:
XAML:
<TextBox Text="{Binding Path=SearchText,Mode=TwoWay}"/>

<Button Content="Search" Width="100" Command="{Binding MySearchCommand}"/>

C#:

public String _SearchText;
public String SearchText
{
   get { return _SearchText; }
   set 
   {
     _SearchText = value;
     OnPropertyChanged("SearchText"); 
    }
 }

ICommand实现是标准的实现,没有花哨的代码,CanExecute处理程序始终返回True。


你怎么说当CanExecute运行时,你的命令不执行?你能在这里发布实现吗?(你的Execute方法) - Amsakanna
1
我认为byte的意思是CanExecute没有运行,但被定义为始终返回true。 - Piotr Justyna
1
一定有一些愚蠢的错误,请发布你的代码 :) - Piotr Justyna
如果问题仅在于您的文本框只绑定在失去焦点时,那么您可以在绑定中更改此行为,例如{Binding MyText,UpdateSourceTrigger=PropertyChanged}。 - jeffora
Piotr - 感谢您的澄清。您的理解是正确的。让我在这里复制代码片段。 - byte
jeffora,问题不是你提到的。我仍然需要在失去焦点时更新属性,即文本框的默认行为。 - byte
3个回答

0

我创建了一个示例应用程序来重现这个问题。

我在SearchText - Set属性和MySearchCommandExecute方法中设置了断点并添加了一个Debug.Writeline。

当设置断点时,只有SearchText-Set属性被调用。我观察到,如果我从SearchText-Set属性中删除断点,那么属性和命令都将正确执行。看起来像是VS 2008的一些问题,但我可能错了。

相关的示例代码如下:

class SearchViewModel : ViewModelBase
    {
        public SearchViewModel() 
        {

        }

        public String _SearchText;
        public String SearchText
        {
            get { return _SearchText; }
            set
            {
                System.Diagnostics.Debug.WriteLine("Set Membership called");

                OnPropertyChanged("SearchText");
            }
        }

        #region Commands
        RelayCommand _SearchCommand;

        public ICommand SearchCommand
        {
            get
            {
                if (_SearchCommand == null)
                {
                    _SearchCommand = new RelayCommand(param => this.MySearchCommandExecute(), param => this.MySearchCommandCanExecute);
                }
                return _SearchCommand;
            }
        }

        public void MySearchCommandExecute()
        {
            System.Diagnostics.Debug.WriteLine("MySearchCommandExecute called");

            // Do Search
        }

        public bool MySearchCommandCanExecute
        {
            get
            {
                return true;
            }
        }

        #endregion
    }

SearchView.xaml

<UserControl x:Class="WpfApplication2.SearchView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <StackPanel>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="4">
            <Label Foreground="Black"  FontFamily="Calibri" Width="155" Margin="4,0,4,0" Content="SearchText"/>
            <TextBox Foreground="Black"  FontFamily="Calibri" Width="155" Margin="4,0,4,0" Text="{Binding Path=SearchText}"/>
        </StackPanel>
        <Button HorizontalAlignment="Left" Content="Search" Width="100" Command="{Binding SearchCommand}" Margin="8"/>
    </StackPanel>
</UserControl>

RelayCommand.cs

// Reference: MSDN sample
    class RelayCommand : ICommand
    {
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("relaycommand execute");

            _execute = execute;
            _canExecute = canExecute;
        }

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

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

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

0

Byte,

抱歉回复晚了,但我希望这个答案对你有所帮助。最近我非常忙,所以无法调试你的代码(我会在有更多时间时尝试),但请尝试使用我下面粘贴的示例代码(它对我来说完美运行)。正如你所看到的,它非常简单。我使用了你的XAML,但是用于Window:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = new TempViewModel();
    }
}

public class TempViewModel : INotifyPropertyChanged
{
    private String _searchText;
    private ICommand _searchCommand;

    #region Commands

    protected class Search : ICommand
    {
        private TempViewModel _viewModel;

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

        public event EventHandler CanExecuteChanged
        {
            add { }
            remove { }
        }

        public void Execute(object parameter)
        {
            //MessageBox in VM is just for demonstration
            MessageBox.Show("command executed with search string: " + this._viewModel._searchText);
        }

        public Search(TempViewModel viewModel)
        {
            this._viewModel = viewModel;
        }
    }

    #endregion //Commands

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(String propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion //INotifyPropertyChanged

    #region Public properties

    public String SearchText
    {
        get
        {
            return this._searchText;
        }
        set
        {
            this._searchText = value;
            OnPropertyChanged("SearchText");
        }
    }

    public ICommand SearchCommand
    {
        get
        {
            return this._searchCommand;
        }
        set
        {
            this._searchCommand = value;
            OnPropertyChanged("SearchCommand");
        }
    }

    #endregion //Public properties

    public TempViewModel()
    {
        this.SearchCommand = new Search(this);
        this.SearchText = "Sample string";
    }
}

如果您有任何进一步的问题,请随时提问。

编辑:啊,抱歉,但我已将Command="{Binding SearchCommand}"更改为Command="{Binding Path=SearchCommand}"


0

尝试通过编写一个小的测试项目来隔离问题,如果您能够重现问题,请发布代码。通常,当您在主项目之外重现问题时,问题和解决方案就会变得明显。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接