我正在构建一个Silverlight应用程序,我想在后台进行长时间运行时禁用按钮。 我正在使用MVVM,所以在ViewModel中我有一个名为SearchInProgress的属性。 现在我想在SearchInProgress为true时禁用搜索按钮。 在WPF中,我只想写一个DataTrigger,它将搜索按钮上的IsEnabled设置为false。
可悲的是,DataTriggers在Silverlight中不可用,所以我正在寻找另一种解决方案。我已经尝试过使用VisualStateManager,但我还没有到达任何地方。对于我想要实现的这个简单的事情,VSM看起来有点过分了。
感谢任何帮助。
答案 0 :(得分:7)
为什么不创建SearchInProgress=true
属性并绑定到IsEnabled=false
属性,而不是跳过箍来设置CanSearch
PropertyChanged
。该属性可以是只读的(或具有私有的setter),而另一个属性可以代表它发起SearchInProgress
事件。
最终,视图模型的重点是从视图中删除逻辑。让视图绑定到IsEnabled
(因此,使用转换器为CanSearch
否定它)意味着视图理解 它应该或不应该能够搜索。但是,绑定到{{1}}属性意味着视图模型可以完全控制何时启用搜索并且视图可以保持愚蠢。
或者,您可以使用与Blend一起安装的Blend behavior API,因为它们具有与数据触发器类似的功能。
答案 1 :(得分:1)
可以说更好的方法是使用Prism2的DelegateCommand将它附加到搜索按钮并在ViewModel中实现其CanExecute方法,以便它返回!SearchInProgress。
然后,当ViewModel启动搜索操作时,它会将SearchInProgress更改为true(以便CanExecute返回false),然后在命令上调用RaiseCanExecuteChanged(这将导致按钮被禁用)一旦搜索操作通过ViewModel将SearchInProgress更改为false(以便CanExecute返回true)然后再次调用RaiseCanExecuteChanged(这将导致按钮被启用)
答案 2 :(得分:0)
为您的数据模型使用DependencyProperty或INotifyPropertyChanged接口,然后绑定到公共属性SerachInProgress。我想你可能还要制作一个转换器,转换成你的布尔值的反面。
答案 3 :(得分:0)
绑定到SearchInProgress属性,但是通过转换器运行它以反转布尔值。
在Binding中,类似
IsEnabled="{Binding Path=SearchInProgress,Converter={StaticResource YOURCONVERTERHERE}}"
在转换器转换功能中,我认为它将是
return !(value as bool)
使用任何健全性检查,你想要为空项目等等。
答案 4 :(得分:0)
我现在能想到的唯一明智的解决方案是在视图订阅的ViewModel中有一个事件“SearchCompleted”,然后在事件触发时相应地更改视图。
答案 5 :(得分:0)
绝对没有理由为此项目添加PRISM dll。您只需要10行代码和2个单元测试。
你仍然想要使用命令模式。添加一个带有“ICommand”的“Command”附加属性,当你设置属性时:
关于PRISM的说明:图书馆很糟糕!但Composite Application Guidance Book是编写MVVM应用程序的任何人的必读内容,并提供了有关命令模式的更多信息。
答案 6 :(得分:0)
我找到了一个我很满意的解决方案。 首先,我在LayoutRoot网格'SearchInProgress'和'Normal'上定义了两个VSM状态。
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Standard">
<VisualState x:Name="SearchInProgress">
<Storyboard>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="(Control.IsEnabled)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>False</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="SearchCancelButton" Storyboard.TargetProperty="(Control.IsEnabled)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>True</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
相当简单,我可能会对它们进行调整但是有效。
要在状态之间切换,我使用here中的DataStateBehavior,它允许我绑定到datacontext(viewmodel)上的属性,并相应地在两个状态之间切换:
<interactivity:Interaction.Behaviors>
<exprsamples:DataStateBehavior Binding="{Binding Path=SearchIsInProgress, Mode=TwoWay}"
Value="True"
TrueState="SearchInProgress"
FalseState="Normal">
</exprsamples:DataStateBehavior>
</interactivity:Interaction.Behaviors>
我认为我现在能够利用VSM的强大功能,Blend的可设计性以及'DataTrigger'机制的灵活性来充分发挥作用。
答案 7 :(得分:0)
您需要创建一个命令并将其绑定到该按钮。您正在寻找的所有内容都将在您的运行时中融入其中。像MVVM Light和Prism这样的框架可以很容易地创建新命令,但你可以这样做:
创建一个实现ICommand的类。给它一个名为SearchInProgress的私有bool。搜索开始时,将SearchInProgress设置为true。搜索完成时(成功或因为超时,中止等)将SearchInProgress设置为false。有ICommand.CanExecute的执行返回!SearchInProgress。在视图模型上公开Search ICommand,然后将按钮的command属性绑定到视图模型上的命令。
伪代码:
public class MySearchCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private bool _searching;
private bool SearchInProgress
{
get { return _searching; }
set
{
if (_searching == value) return;
_searching = value;
if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty);
}
public bool CanExecute(object param)
{ return !SearchInProgress }
public void Execute(object param)
{
try
{
SearchInProgress = true;
// search code here including callback to OnSearchCompleted method
}
catch(Exception ex)
{
SearchInProgress = false;
}
}
private void OnSearchCompleted(SomeCallbackResult result)
{
SearchInProgress = false;
}
}
public class ViewModel : INotifyPropertyChange
{
public ICommand SearchCommand { get; private set; }
public ViewModel()
{
SearchCommand = new MySearchCommand();
}
}
XAML:
<UserControl ....>
<UserControl.DataContext>
<ViewModel />
</UserControl.DataContext>
<Grid>
<Button Command={Binding SearchCommand} />
</Grid>
</UserControl>