我有一个简单的按钮:
<Button Content="Print" Command="{Binding PrintCommand}"/>
...用命令:
private RelayCommand _printCommand;
public ICommand PrintCommand
{
get
{
if (_printCommand == null)
{
_printCommand = new RelayCommand(param => Print(),
() => (Files != null && Files.Count > 0));
}
return _printCommand;
}
}
仅当Files
集合不为null或其中包含某些项目时才会启用它。这是集合:
private ObservableCollection<RecordModel> _files;
public ObservableCollection<RecordModel> Files
{
get { return _files; }
set
{
if (_files == value)
{
return;
}
_files = value;
OnPropertyChanged("Files");
}
}
该集合绑定到窗口中的ListView
。到目前为止,没有什么特别的......而且这里有奇怪的行为......
如果我在集合中有足够的项目要显示ListView
ScrollBar
,那么我的按钮会显示为Enabled
,这很好。如果我没有项目,那么它是Disabled
,这也很好。但是,如果我有足够的项目来填充可见ListView
的一部分,而不会触发ScrollBar
的外观,那么我的按钮会显示为Disabled
。如果我专注于任何控件,包括按钮本身,则弹出为Enabled
。我不知道发生了什么。起初,我认为它可能是我正在使用的按钮模板,所以我摆脱它并保持按钮的默认设置,但奇怪的行为仍然存在。
任何想法会发生什么?
这是RelayCommand
课程。我不确定问题是否存在,但这是我一直在使用的问题:
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Func<bool> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Func<bool> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
编辑:
这是我如何填充我的收藏品。
public FileManagerViewModel()
{
LoadCollection();
}
private void LoadCollection()
{
Task task = new Task(() =>
{
Files = DbWorker.GetFiles();
});
task.Start();
}
以下是我如何将集合绑定到ListView
:
<Window.DataContext>
<vm:FileManagerViewModel/>
</Window.DataContext>
<Window.Resources>
<CollectionViewSource Source="{Binding Files}" x:Key="GroupedFiles">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="RepNum"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<ListView ItemsSource="{Binding Source={StaticResource GroupedFiles}}">
...
</ListView
修改
嗯,我不知道这是不是它或它是如何引起它的(特别是在整个ScrollBar
情况下),但是当我不使用{{1}时更新我的收藏,我没有经历过这种行为。当然,由于漫长的操作,我不得不处理悬挂问题。考虑到我不想阻止UI线程,我不知道如何解决这个问题。我甚至试过这个并没有改变任何东西:
Task
但我没有看到有关未更新的属性的问题。我已经检查过,所有属性都会根据需要更新。它只是var temp = new ObservableCollection<RecordModel>();
Task task = new Task(() =>
{
temp = DbWorker.GetFiles();
});
task.ContinueWith((result) =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
Files = temp;
}));
});
task.Start();
状态更新中的一小部分内容,只有焦点更改才会更新(在此方案中)。
根据我所知,似乎线程和命令之间存在问题...嗯。每次我通过点击手动给UI元素一个焦点,命令更新(或者它出现)。如果ScrollBar出现或消失,也会发生这种情况。但是其他UI元素不会做任何事情,例如文本。
答案 0 :(得分:3)
好吧,正如我所看到的那样,你的命令并不知道它与Files
集合有关。所以Files
被更改,它的setter被调用,你的ListView
被更新,因为它绑定到该集合并对PropertyChanged作出反应。但命令并不直接与它相关联,所以它只是坐在那里不受干扰,喝咖啡或其他什么。只有当UI开始更改系统时,才会调用CanExecute并开始工作。所以我认为这里的简单解决方案可以是将Files
绑定到命令。
你可以直接用它来设置setter(因为你知道命令依赖于集合):
public ObservableCollection<RecordModel> Files
{
get { return _files; }
set
{
if (_files == value) return;
_files = value;
OnPropertyChanged("Files");
CommandManager.InvalidateRequerySuggested();
}
}
或者您可以在加载集合时正确执行此操作(例如,如果您知道这是影响集合大小的唯一操作):
task.ContinueWith((result) =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
Files = temp;
CommandManager.InvalidateRequerySuggested();
}));
});
注意:我使用InvalidateRequerySuggested
作为无效命令的最简单方法,但它有点矫枉过正。其他ICommand
实现使用不同的技术来做到这一点(例如Telerik的DelegateCommand具有自定义InvalidateCanExecute
方法)。所以这部分可以进一步改进。
答案 1 :(得分:0)
我无法评论为什么这个设计不起作用,因为从代码中提供的命令处理启用按钮的方法不清楚。但我这样做的方式如下: 在我的viewmodel类中,我将拥有一个公共属性IsPrintAllowed。
private bool _isPrintAllowed;
public bool IsPrintAllowed{
get{ return _isPrintAllowed;}
set{_isPrintAllowed = value;
RaisePropertyChanged(() => IsPrintAllowed)}
Files集合的CollectionChanged事件将评估IsPrintAllowed属性。
Files.CollectionChanged += EvaluateIsPrintAllowed;
private void EvaluateIsPrintAllowed()
{
IsPrintAllowed = Files != null && Files.Count > 0;
}
在xaml中,我会将按钮的IsEnabled属性绑定到IsPrintAllowed。
<Button Content="Print" Command="{Binding PrintCommand}" IsEnabled = {Binding IsPrintAllowed}/>