绑定按钮.IsEnabled() 属性无法正常工作
本文关键字:常工作 工作 属性 按钮 IsEnabled 绑定 | 更新日期: 2023-09-27 18:15:43
我是MVVM Pattern的新手。
我想知道为什么每次触发我的TextChanged()
事件时,绑定的IsEnabled()
属性都不会更改其状态。TextChanged()
事件正在调用 IsValid()
来检查数据验证。
我有这个简单的ViewModel
课
public class myViewModel : ViewModel
{
public bool IsOk { get; set; }
public RelayCommand OnValidateExecute { get; set; }
public myViewModel()
{
OnValidateExecute = new RelayCommand(p => IsValid(), p => true);
}
public bool IsValid()
{
// other codes
IsOk = MethodName();
NotifyPropertyChanged("IsOk");
}
}
我在IsValid()
上设置了一个断点,代码工作正常。我想知道为什么IsEnabled
属性没有按预期工作。
这是我XAML
代码
<TextBox ...other propeties here....>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding OnValidateExecute}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<Button x:Name="btnSave" IsEnabled="{Binding IsOk, Mode=TwoWay}"
...other propeties here....>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding OnSaveExecute}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
我想要的是,当IsOk
属性为 false 时,按钮应该Disabled
,否则,Enabled
.
我的数据绑定有问题吗?如果以前有人问过这个问题,请帮助我重定向到它。
更新 1
我使用此代码遇到的另一个问题是,在文本框上设置值之前,首先触发IsValid()
函数。举个例子,假设文本框的起始值是0
,当我把它改成,比如说9时,要检查的值是0
的前一个值,而不是9
。知道为什么会这样吗?绑定有问题吗?
这是答案,包括我的 MVVM 框架的重要部分。 除此之外,我还在其中添加了一些额外的功能。我不能把我所有的图书馆都放在这里。但我相信它会有所帮助。
如果您使用命令,您应该注意ICommand
界面中的CanExecuteChanged
。应在属性更改时触发此命令。(我不使用中继命令,它是3.party。
Use my DCommand :) this is the most important part
它易于实现,并具有FirePropertyChanged
方法。此方法触发 ICommand 的CanExecuteChanged
(如果它不为空(。
示例命令
匿名语法
DCommand commandPost=new DCommand(()=>{
//TODO:Command's real execute method Post()
},
()=>
{
return this.TextBoxBoundProperty.IsValid;
}
)
非匿名语法
DCommand commandPost=(Post,Validate);
除此之外,您应该通过视图模型的 ctor 中的以下方法触发 canexecuteChanged。
this.PropertyChanged += (sender, prop) =>
{
//I preffered canExcuteChange for any property changes for my viewmodel class. You could put your own logic. if(prop.PropertyName.equals("thisone"));
//Just works for this class's property changed
this.InvokeOnClassPropertyChange(prop.PropertyName, () =>
{
this.commandPost.FirePropertyChanged();
});
}
InvokeOnClassPropertyChange 在属性是 ViewModel 类的属性时有效。
public static void InvokeOnClassPropertyChange(this object instance,string PropertyName,Action action)
{
Type type = instance.GetType();
var fulllist = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(w => w.DeclaringType == type).ToList();
if (fulllist.Select(p => p.Name).Contains(PropertyName))
{
action.Invoke();
}
}
上面的代码显示了InvokeOnClassPropertyChange
扩展方法。下面的一个显示了我的DCommand
实现 ICommand。
public class DCommand :ICommand
{
public void FirePropertyChanged()
{
if (CanExecuteChanged!=null)
CanExecuteChanged(this, EventArgs.Empty);
}
Func<bool> CanExecuteFunc { get; set; }
Action<object> Action { get; set; }
public DCommand(Action<object> executeMethod)
{
this.Action = executeMethod;
}
public DCommand(Action executeMethod)
{
this.Action = new Action<object>(
(prm) =>
{
executeMethod.Invoke();
}
);
}
public DCommand(Action<object> executeMethod, Func<bool> canExecuteMethod)
: this(executeMethod)
{
this.CanExecuteFunc = canExecuteMethod;
}
public DCommand(Action executeMethod, Func<bool> canExecuteMethod)
: this(executeMethod)
{
this.CanExecuteFunc = canExecuteMethod;
}
public bool CanExecute(object parameter=null)
{
if (CanExecuteFunc == null)
return true;
return CanExecuteFunc.Invoke();
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter=null)
{
if (CanExecuteFunc == null || CanExecute(parameter))
{
Action.Invoke(parameter);
}
}
}
毕竟如果要在文本框的文本立即更改时更改 ViewModel 属性,则应按此方式进行绑定。
Text="{Binding BoundProperty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
下面是我的代码:
<StackPanel>
<TextBox x:Name="TextBox1" Margin="5,0,5,0" Width="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding OnValidateExecute, Mode=OneWay}" CommandParameter="{Binding Text,ElementName=TextBox1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<Button x:Name="btnSave" Width="120" Height="25" Content="click" IsEnabled="{Binding IsOk}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding OnSaveExecute}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
仅添加命令参数。
public class myViewModel : INotifyPropertyChanged
{
public bool IsOk { get; set; }
public string Message { get; set; }
public RelayCommand OnValidateExecute { get; set; }
public myViewModel()
{
OnValidateExecute = new RelayCommand(p =>
{
Message = p as string;
IsValid();
}, p => true);
}
public bool IsValid()
{
bool valid = !string.IsNullOrEmpty(Message);
IsOk = valid;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsOk"));
}
return valid;
}
public event PropertyChangedEventHandler PropertyChanged;
}
效果很好。
希望这有帮助。
要完成Davut Gürbüz的答案:
Button
具有 Command 属性,则使用此属性比i:Interaction.Triggers
更好(更易于阅读(。
就像Davut Gürbüz说RelayCommand有一个CanExecuteFunc参数一样。如果使用此属性,按钮的状态会自动更改。
Xaml 代码 :
<TextBox
Text="{Binding BoundProperty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
...other propeties here.... />
<Button
x:Name="btnSave"
Command="{Binding OnSaveExecute}"
...other propeties here.... />
C#
/// <summary>
/// Gets the OnSaveExecute.
/// </summary>
public RelayCommand OnSaveExecute
{
get
{
return _onSaveExecute
?? (_onSaveExecute = new RelayCommand(
() =>
{
// Save action or call save method
},
() => IsOk));
}
}
private RelayCommand _onSaveExecute;
/// <summary>
/// Sets and gets the IsOk property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public bool IsOk
{
get { return _isOk; }
set
{
if (_isOk == value)
{
return;
}
_isOk = value;
RaisePropertyChanged("IsOk");
OnSaveExecute.RaiseCanExecuteChanged();
}
}
private bool _isOk = false;
/// <summary>
/// Sets and gets the BoundProerty property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string BoundProerty
{
get { return _boundProerty; }
set
{
if (_boundProerty == value)
{
return;
}
_boundProerty = value;
RaisePropertyChanged("BoundProerty");
IsValid();
}
}
private string _boundProerty = false;
public myViewModel()
{
}
public bool IsValid()
{
// other codes
IsOk = MethodName();
}