我有一个View谁将DataContext设置为Employee。 此外,该视图使用BindingGroup和Validation Rules。 最后,视图有2个按钮:保存并取消
保存:验证用户输入,如果成功,请保存更改 取消:回滚用户输入并恢复原始值。
直到这一点它才能正常工作。
现在最后一个要求和问题:
为了获得更好的用户体验,我想在用户开始更改数据时启用保存按钮。
为此,我将BindingGroup的IsDirty属性绑定到Button的Enabled属性。
不幸的是它不起作用。绑定似乎是正确的,但用户界面无法识别IsDirty的更改。
我可以解决这个问题吗?
我的模特:
public class EmployeeModel:ModelBase
{
private int _nr;
private string _firstname;
private string _lastname;
public int Nr
{
get
{
return _nr;
}
set
{
_nr = value;
OnChanged(nameof(Nr));
}
}
public string Firstname
{
get
{
return _firstname;
}
set
{
_firstname = value;
OnChanged(nameof(Firstname));
}
}
public string Lastname
{
get
{
return _lastname;
}
set
{
_lastname = value;
OnChanged(nameof(Lastname));
}
}
}
模型库:
public class ModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnChanged(string propertyname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
有效性规则:
public class EmployeeValidationRule:ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
BindingGroup bindingGroup = (BindingGroup)value;
if (bindingGroup.Items.Count == 2)
{
EmployeeModel employee = (EmployeeModel)bindingGroup.Items[1];
string firstname = (string)bindingGroup.GetValue(employee, "Firstname");
string lastname = (string)bindingGroup.GetValue(employee, "Lastname");
if (firstname.Length == 0)
return new ValidationResult(false, "Firstname can not be empty.");
if (lastname.Length == 0)
return new ValidationResult(false, "Lastname can not be empty.");
}
return ValidationResult.ValidResult;
}
}
我的ViewModel:
public class EmployeeViewModel
{
private EmployeeModel _employeeModel;
public EmployeeModel Employee
{
get
{
return _employeeModel;
}
set
{
_employeeModel = value;
}
}
public EmployeeViewModel()
{
LoadData();
}
private void LoadData()
{
//Employee = (from e in _context.Employee
// where e.Nr == 158
// select e).FirstOrDefault();
Employee = new EmployeeModel() { Firstname = "Billy", Lastname = "Wilder" };
}
public void Save()
{
//_context.SaveChanges();
}
}
最后是视图:
<Window x:Class="WpfApplication3_Validation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3_Validation"
xmlns:vm="clr-namespace:WpfApplication3_Validation.ViewModel"
xmlns:vr="clr-namespace:WpfApplication3_Validation.ValidationRules"
mc:Ignorable="d"
Title="Employee" Height="250" Width="525"
Validation.ValidationAdornerSite="{Binding ElementName=lbErrors}" Loaded="Window_Loaded">
<Window.DataContext>
<vm:EmployeeViewModel/>
</Window.DataContext>
<Window.BindingGroup>
<BindingGroup x:Name="MyBindingGroup">
<BindingGroup.ValidationRules>
<vr:EmployeeValidationRule/>
</BindingGroup.ValidationRules>
</BindingGroup>
</Window.BindingGroup>
<Grid x:Name="gridMain">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Nr:"/>
<TextBlock Grid.Column="1" Text="{Binding Employee.Nr}"/>
<Label Grid.Row="1" Content="Vorname:" Target="{Binding ElementName=tbFirstname}"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="tbFirstname" Text="{Binding Employee.Firstname}"/>
<Label Grid.Row="2" Content="Nachname:" Target="{Binding ElementName=tbLastname}"/>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="tbLastname" Text="{Binding Employee.Lastname}"/>
<Label Grid.Row="4" Grid.Column="0" x:Name="lbErrors" Content="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)[0].ErrorContent}"
Foreground="Red" FontWeight="Bold"/>
<StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock x:Name="tbIsDirty"/>
<Button x:Name="btn1" Content="IsDirty?" Click="btn1_Click"/>
<Button x:Name="btnSave" Content="Save1" Click="btnSave_Click" />
<Button x:Name="btnSave1" Content="Save2" Click="btnSave_Click" IsEnabled="{Binding ElementName=MyBindingGroup, Path=IsDirty}"/>
<Button x:Name="btnCancel" Content="Cancel" Click="btnCancel_Click"/>
</StackPanel>
</Grid>
代码背后:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.MyBindingGroup.BeginEdit(); // Not really needed?
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
if (this.BindingGroup.CommitEdit())
{
EmployeeViewModel vm = (EmployeeViewModel)this.DataContext;
vm.Save();
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
this.BindingGroup.CancelEdit();
}
private void btn1_Click(object sender, RoutedEventArgs e)
{
tbIsDirty.Text = BindingGroup.IsDirty.ToString();
}
}
答案 0 :(得分:0)
由于BindingGroup.IsDirty没有实现INotifyPropertyChanged,因此它不是这种数据绑定的有用源。
可能的解决方案:
- 在视图中实现INotifyPropertyChanged
- 使用INotifyPropertyChanged在视图中创建自己的IsDirty
- 为KeyUp添加事件处理程序,在BindingGroup.IsDirty的情况下设置我的IsDirty
- 启用绑定到新属性
缺点:如果在视图中实现INotifyPropertyChanged,则需要。
优点:它有效。
ViewBehind of View:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnChanged(string propertyname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
private bool _isDirty;
public bool IsDirty
{
get
{
return _isDirty;
}
set
{
_isDirty = value;
OnChanged(nameof(IsDirty));
}
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.MyBindingGroup.BeginEdit(); // Not really needed?
gridMain.KeyUp += GridMain_KeyUp;
}
private void GridMain_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (this.MyBindingGroup.IsDirty)
{
IsDirty = true;
}
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
if (this.BindingGroup.CommitEdit())
{
EmployeeViewModel vm = (EmployeeViewModel)this.DataContext;
vm.Save();
IsDirty = false;
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
this.BindingGroup.CancelEdit();
IsDirty = false;
}
}
进一步改进:
现在我将IsDirty移动到我的ViewModel,所以我不必在视图中实现INPC。另一个优点是,通过这种方式,命令可以使用属性,最后我不必对启用的属性使用数据绑定,因为我通过命令获取它。