事件处理程序无法在属性上触发

时间:2018-10-22 08:12:52

标签: wpf mvvm event-handling

我有一个WPF应用程序,并且仅当他们必须在“下载路径”的文本框中指定一个值时,才希望启用“开始”按钮控件。

我的ViewModel包含我的模型“ ConfigurationSettings”的属性和按钮的ICommand实现(CommandImp):

public class MainWindowViewModel : BaseNotifyPropertyChanged
{        
    private ConfigurationSettings _configurationSettings { get; set; }
    public ConfigurationSettings ConfigurationSettings 
    {
        get
        {
            return _configurationSettings;
        }
        set
        {
            if (_configurationSettings != value)
            {
                _configurationSettings = value;
                RaisePropertyChanged("ConfigurationSettings");
            }
        }
    }

    public CommandImp StartCommand { get; set; } // this is an implementation of ICommand

    public MainWindowViewModel()
    {
        StartCommand = new CommandImp(OnStart, CanStart);

        _configurationSettings = new ConfigurationSettings();
        _configurationSettings.PropertyChanged += delegate (object o, 
                PropertyChangedEventArgs args)
        {                
            StartCommand.RaiseCanExecuteChanged(); // break point here is never reached            
        };            
    }

    private bool CanStart()
    {
        if (!String.IsNullOrEmpty(ConfigurationSettings.DownloadPath))
        {
            return true;
        }
        return false;
    }
}

在我的XAML中,我有一个“开始”按钮和with Command =“ {Binding StartCommand}”。

我的ConfigurationSettings类仅具有一个DownloadPath的字符串,该字符串绑定到XAML中的文本框:

public class ConfigurationSettings : BaseNotifyPropertyChanged
{
    private string _downloadPath { get; set; }
    public string DownloadPath
    {
        get { return _downloadPath; }
        set
        {
            if (_downloadPath != value)
            {
                _downloadPath = value;
                RaisePropertyChanged("DownloadPath"); // break point here IS reached
            }
        }
    }
} 

当用户输入DownloadPath时,我希望它会触发PropertyChanged事件并运行在ViewModel构造函数中定义的委托方法。

如果我在ConfigurationSettings类内移动命令按钮,则可以取消事件订阅,而只需在RaisePropertyChanged(“ DownloadPath”);下方使用StartCommand.RaiseCanExecuteChanged()。但是我不希望ICommand成为模型的一部分。

当ConfigurationSettings的属性之一更改时,如何触发CanStart()?

更新: 这是文本框绑定的XAML:

<TextBlock Text="{Binding ConfigurationSettings.DownloadPath, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextWrapping="WrapWithOverflow" />

按钮:

<Button Content="Start" Command="{Binding StartCommand}"></Button>

我应该注意绑定正常工作。更新文本块时,可以在ViewModel中看到ConfigurationSettings.DownloadPath正在正确更新。

BaseNotifyPropertyChanged是INotifyPropertyChanged的实现,如下所示:

public class BaseNotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string property)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
}

我似乎对属性更改事件没有任何问题。我可以在此处放置一个断点,并在更新DownloadPath文本框时将其击中。当我在ViewModel构造函数中订阅此PropertyChanged事件时,我的委托方法没有触发。

1 个答案:

答案 0 :(得分:0)

讨厌回答我自己的问题,但是人们的评论使我开始考虑重组我的问题-这导致我在需要进行其他更新之前找到了答案。

解决方案是将我的事件订阅移至ConfigurationSettings的“设置”功能内:

private ConfigurationSettings _configurationSettings { get; set; }
public ConfigurationSettings ConfigurationSettings
{
    get
    {
        return _configurationSettings;
    }
    set
    {
        if (_configurationSettings != value)
        {
            _configurationSettings = value;

            _configurationSettings = new Model.ConfigurationSettings();
            _configurationSettings.PropertyChanged += (o, args) =>
            {
                StartCommand.RaiseCanExecuteChanged();
            };

            RaisePropertyChanged("ConfigurationSettings");                   
        }
    }
}

问题出在我设置数据上下文的位置,而我最初并不怀疑这是问题所在。我从磁盘上的XML文件加载视图模型。当应用程序关闭时,我用最新的ViewModel覆盖了该文件。

在构造函数中,我正在读取和设置DataContext:

public MainWindowView()
{
    InitializeComponent();

    string appPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
    DataSourcePath = new Uri(System.IO.Path.Combine(appPath, DataFileName));

    if (File.Exists(DataSourcePath.LocalPath))
    {
        XmlReader reader = XmlReader.Create(DataSourcePath.LocalPath);
        DataContext = (MainWindowViewModel)serialize.Deserialize(reader);
        reader.Close();
    }
    else
    {
        WriteDataViewModelToDisk(); // new empty view model written to disk
    }
}

如果这是我第一次运行代码,并且没有预先存在的文件,则我的委托事件处理程序实际上可以工作。问题在于,当此代码加载了预先存在的XML文件时,它覆盖了我的视图模型中的ConfigurationSettings属性,从而破坏了事件订阅。