我有一个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事件时,我的委托方法没有触发。
答案 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属性,从而破坏了事件订阅。