【问题标题】:ViewModel property not getting updated on changing TextBox textViewModel 属性在更改 TextBox 文本时未更新
【发布时间】:2015-11-11 21:44:48
【问题描述】:

我在 UserControl 中有一个 TextBox,它绑定到 MainWindow 的 ViewModel 中的一个属性。

现在,当我在文本框中键入内容时,它会更新视图模型中的属性,但如果我在后面的代码中更改文本框的文本,则视图模型属性不会更新。

实际上,文本框是从我单击按钮时打开的 FileDialog 获取值,因此文本框是从后面的代码中获取其文本。

用户控件 XAML:

<StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Left">
    <TextBox x:Name="TextBoxFileOrFolder" Text="{Binding FolderOrFileName}" Grid.Row="1" Width="200" Height="100" HorizontalAlignment="Left"></TextBox>
    <Button x:Name="ButtonRun" Content="Run" Click="ButtonRun_OnClick" Width="200" Height="100" Margin="10"></Button>
</StackPanel>

UserControl 后面的代码

private void ButtonRun_OnClick(object sender, RoutedEventArgs e)
{
    TextBoxFileOrFolder.Text = "FileName" + new Random().Next();
}

视图模型:

public class MainViewModel: INotifyPropertyChanged
{
    public MainViewModel()
    { }

    private string folderOrFileName;

    public string FolderOrFileName
    {
        get { return folderOrFileName; }
        set
        {
            if (folderOrFileName!=value)
            {
                folderOrFileName = value;
                RaisePropertyChanged();
            }
        }
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises the property changed.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    # endregion
}

【问题讨论】:

  • 只是好奇:我不知道“[CallerMemberName]”这个短语。那是来自 vs 版本 > 2010 的一些神奇的编译器吗?
  • @Udontknow 它是在 .NET 4.5 中引入的,基本上在编译时传递调用成员的名称。所以它非常适合实施 INPC。见MSDN

标签: c# wpf xaml mvvm


【解决方案1】:

但如果我在后面的代码中更改文本框的文本,则 viewmodel 属性不会更新。

这是因为如果您在代码隐藏中设置文本框的Text 属性,您将覆盖绑定。所以当你更新视图时,你的视图模型的链接消失了,所以没有什么可以更新它。而且,当视图模型更新值时,视图也不会更新。

要解决这个问题,不要在代码隐藏中设置具有绑定的属性。

您应该将按钮命令绑定到您的视图模型并更新视图模型中的FolderOrFileName,而不是在代码隐藏中处理按钮事件并更新视图。

【讨论】:

  • 工作就像一个魅力!谢谢。
【解决方案2】:

如果您绑定到Text 属性,您应该在 ViewModel 中设置属性以更改 TextBox 的值:

public partial class MainWindow : Window
{
    private MainViewModel _vm;

    public MainWindow()
    {
        InitializeComponent();
        _vm = new MainViewModel();
        DataContext = _vm;
    }

    private void ButtonRun_OnClick(object sender, RoutedEventArgs e)
    {
        _vm.FolderOrFileName = "FileName" + new Random().Next();
    }
}

在您的情况下,您应该使用命令来修改数据。

1) 你应该创建继承自ICommand的类:

public class DelegateCommand : ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

    public event EventHandler CanExecuteChanged;

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

    public DelegateCommand(Action<object> execute,
                   Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
        {
            return true;
        }

        return _canExecute(parameter);
    }

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

    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }
    }
}

2) 接下来你应该在 ViewModel 中创建命令:

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        ChangeFileName = new DelegateCommand(OnChangeFileName);
    }

    public ICommand ChangeFileName { get; private set; }

    private void OnChangeFileName(object param)
    {
        FolderOrFileName = "FileName" + new Random().Next();
    }

    private string folderOrFileName;
    ...

3) 最后,您应该在 View 中添加对 Button.Command 属性的绑定:

<Button x:Name="ButtonRun" Content="Run" Command="{Binding ChangeFileName}" Width="200" Height="100" Margin="10"></Button>

【讨论】:

    【解决方案3】:

    确保您的绑定设置为“TwoWay” - UI -> VM 和 VM -> UI

    <TextBox x:Name="TextBoxFileOrFolder" Text="{Binding FolderOrFileName, Mode=TwoWay}" Grid.Row="1" Width="200" Height="100" HorizontalAlignment="Left"></TextBox>
    

    【讨论】:

    • TextBox 的默认绑定模式是 TwoWay。
    • 自从我完成 WPF 以来已经有一段时间了 - 尽管我通常发现指定 Mode 更易读。
    猜你喜欢
    • 1970-01-01
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    相关资源
    最近更新 更多