如何仅在ViewModel的SuccesfullyLoggedIn为true时触发View中的行为?
针对你的需求——仅当ViewModel中的SuccesfullyLoggedIn布尔属性为true时,才让KeyboardFocusBehavior执行,这里有几个实用的实现方案,你可以根据自己的行为特性选择:
方案1:直接绑定行为的IsEnabled属性(最简单)
如果你的KeyboardFocusBehavior实现了IsEnabled属性(大多数WPF行为框架如Microsoft.Xaml.Behaviors都会默认支持),直接将这个属性绑定到SuccesfullyLoggedIn即可。当属性为false时,行为会被禁用,不会触发:
<Grid x:Name="main" Style="{StaticResource GridMain}"> <behaviors:KeyboardFocusBehavior On="{Binding ElementName=somecontrol}" IsEnabled="{Binding SuccesfullyLoggedIn}" /> </Grid>
方案2:用DataTrigger动态控制行为的触发
如果行为没有IsEnabled属性,或者你需要更精细的控制,可以使用DataTrigger来在SuccesfullyLoggedIn变为true时才触发行为:
<Grid x:Name="main" Style="{StaticResource GridMain}"> <i:Interaction.Triggers> <!-- 当SuccesfullyLoggedIn为true时,触发焦点行为 --> <i:DataTrigger Binding="{Binding SuccesfullyLoggedIn}" Value="True"> <behaviors:KeyboardFocusBehavior On="{Binding ElementName=somecontrol}" /> </i:DataTrigger> </i:Interaction.Triggers> </Grid>
注意:部分行为框架可能需要用InvokeCommandAction间接调用行为的触发方法,你可以根据实际情况调整。
方案3:通过MultiBinding+转换器组合触发条件
如果KeyboardFocusBehavior的On属性需要接收目标控件,但你想仅在SuccesfullyLoggedIn为true时才传递这个控件,可以创建一个多值转换器来组合两个条件:
第一步:创建转换器类
using System; using System.Globalization; using System.Windows.Data; public class FocusTriggerConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { // 第一个值是SuccesfullyLoggedIn,第二个是目标控件 if (values.Length != 2 || !(values[0] is bool isLoggedIn)) return null; return isLoggedIn ? values[1] : null; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException("反向转换不需要实现"); } }
第二步:在XAML中使用转换器
<Window.Resources> <local:FocusTriggerConverter x:Key="FocusTriggerConverter" /> </Window.Resources> <Grid x:Name="main" Style="{StaticResource GridMain}"> <behaviors:KeyboardFocusBehavior> <behaviors:KeyboardFocusBehavior.On> <MultiBinding Converter="{StaticResource FocusTriggerConverter}"> <Binding Path="SuccesfullyLoggedIn" /> <Binding ElementName="somecontrol" /> </MultiBinding> </behaviors:KeyboardFocusBehavior.On> </behaviors:KeyboardFocusBehavior> </Grid>
这个方案的逻辑是:当SuccesfullyLoggedIn为true时,转换器返回somecontrol,行为正常触发;否则返回null,行为不会执行。
方案4:在ViewModel中手动触发(最灵活)
如果前面的方案都不符合你的需求,你可以在ViewModel中监听SuccesfullyLoggedIn的变化,当它变为true时,通过命令或事件通知视图执行焦点设置:
ViewModel代码
using System.ComponentModel; using System.Windows.Input; using Prism.Commands; // 也可以用RelayCommand等其他ICommand实现 public class YourViewModel : INotifyPropertyChanged { private bool _succesfullyLoggedIn; public bool SuccesfullyLoggedIn { get => _succesfullyLoggedIn; set { if (_succesfullyLoggedIn != value) { _succesfullyLoggedIn = value; OnPropertyChanged(nameof(SuccesfullyLoggedIn)); if (value) { SetFocusCommand?.Execute(null); } } } } public ICommand SetFocusCommand { get; } public YourViewModel() { SetFocusCommand = new DelegateCommand(ExecuteSetFocus); } private void ExecuteSetFocus() { // 通过自定义事件通知视图设置焦点 FocusRequested?.Invoke(this, EventArgs.Empty); } public event EventHandler FocusRequested; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
视图代码
<Grid x:Name="main" Style="{StaticResource GridMain}" Loaded="MainGrid_Loaded"> <!-- 需要获得焦点的目标控件 --> <TextBox x:Name="somecontrol" /> </Grid>
视图后台代码
private void MainGrid_Loaded(object sender, RoutedEventArgs e) { if (DataContext is YourViewModel vm) { vm.FocusRequested += (s, args) => somecontrol.Focus(); } }
这个方案完全绕开了行为,直接在ViewModel控制逻辑,适合需要更复杂业务判断的场景。
内容的提问来源于stack exchange,提问作者user1702369

