遗憾的是,在Silverlight中无法使用DataTriggers,因此我正在寻找另一种解决方案。我尝试使用VisualStateManager,但没有取得任何进展。VSM对于我想要实现的这个简单事情来说似乎过于复杂了。
感谢任何帮助。
SearchInProgress=true
并将IsEnabled=false
而做出艰难的努力,为什么不创建一个CanSearch
属性,并绑定到它呢?该属性可以是只读的(或者具有私有 setter),而另一个属性则可以代表它触发PropertyChanged
事件。SearchInProgress
(从而使用转换器来对其进行取反以获得IsEnabled
)意味着视图了解何时能够进行搜索或无法进行搜索。然而,绑定到CanSearch
属性意味着视图模型完全控制何时启用搜索,而视图可以保持简单。绑定到SearchInProgress属性,但通过转换器运行以反转布尔值。
在绑定中,类似于
IsEnabled="{Binding Path=SearchInProgress,Converter={StaticResource YOURCONVERTERHERE}}"
return !(value as bool)
无论您想在空项目等方面放置什么样的健全性检查。
我找到了一个相当满意的解决方案。首先在我的LayoutRoot网格上定义了两个VSM状态:“SearchInProgress”和“Normal”。
<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>
非常简单,我可能会进行调整,但它有效。
为了在两种状态之间切换,我使用这里的DataStateBehavior,它让我绑定到数据上下文(视图模型)上的属性,并相应地切换两种状态:
<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”机制的灵活性。
您需要创建一个命令并将其绑定到按钮。您要寻找的所有内容都已集成在运行时中。像MVVM Light和Prism这样的框架使创建新命令变得很容易,但您也可以像这样自己完成:
创建一个实现ICommand的类。给它一个名为SearchInProgress的私有布尔值。当搜索开始时,请将SearchInProgress设置为true。当搜索完成(成功完成或因超时、被中止等而完成)时,请将SearchInProgress设置为false。让ICommand.CanExecute的实现返回!SearchInProgress。在您的视图模型上公开Search ICommand,然后将按钮的命令属性绑定到视图模型上的命令。
伪代码:
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>
完全没有必要为此将PRISM dll添加到您的项目中。 您只需要10行代码和2个单元测试。
不过,您仍然想使用命令模式。 添加一个“Command”附加属性,该属性接受“ICommand”,当设置属性时,您需要:
关于PRISM的说明:该库很烂! 但是Composite Application Guidance Book对于任何编写MVVM应用程序的人来说都是必读的,并提供有关命令模式的更多信息。