只写模式下的 WPF 数据绑定控件
Posted
技术标签:
【中文标题】只写模式下的 WPF 数据绑定控件【英文标题】:WPF Databind control in write-only mode 【发布时间】:2016-02-27 03:43:41 【问题描述】:我正在尝试使用 WPF 绑定,所以我创建了一个简单的项目。 代码如下:
public class Person : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public int Age
get return age;
set
age = value;
FirePropertyChanged("Age");
public string Name
get return name;
set
name = value;
FirePropertyChanged("Name");
private void FirePropertyChanged(string v)
if(PropertyChanged !=null)
PropertyChanged(this, new PropertyChangedEventArgs(v));
private int age;
private string name;
我的视图模型包含 Person 的 ObservableCollection,以及用于跟踪所选 Person 的单个 Person。
我已将列表框的 ItemsSource 绑定到 ObservableCollection,并将 SelectedItem 绑定到单个 Person,称为 CurrentPerson。此外,我已将 TextBox 绑定到 CurrentPerson.Name。
代码工作正常,但每当我更改 TextBox 的内容时 - 我的列表框也会更改。而且无论 listbox\selecteditem 上的“OneWay、TwoWay、OneWayToSource”绑定模式是什么组合,我都无法阻止列表框从 CurrentPerson 更新。
如何防止这种行为?我想仅使用 VM 中的 ICommand 接口从 CurrentPerson 更新列表框。
【问题讨论】:
【参考方案1】:Person
对象只有一个副本,它同时用于ListBox.ItemsSource
和TextBox.Text
,因此从一个位置更新该对象自然也会反映另一个位置的更改。
两个简单的解决方案是
将TextBox.Text
上的BindingMode
更改为Explicit
,因此它不会更新Person 对象,直到您告诉它
为TextBox.Text
使用单独的字符串属性,并在命令执行时将其复制到您的SelectedPerson.Name
我个人更喜欢第二个选项,因为我不喜欢不能准确反映 UI 组件背后的数据对象的绑定,它允许用户更改 SelectedItem
而无需重置 TextBox 值.
对于第二个选项的示例,您的 ViewModel 可能如下所示:
public class MyViewModel()
ObservableCollection<Person> People get; set;
Person SelectedPerson get; set;
string NewPersonName get; set;
ICommand UpdatePersonName get;
UpdatePersonName
命令的执行位置
SelectedPerson.Name = NewPersonName;
并且CanExecute
只会在以下情况下返回 true
SelectedPerson != null
&& !NewPersonName.IsNullOrWhiteSpace()
&& NewPersonName != SelectedPerson.Name
【讨论】:
第二种解决方案对我来说听起来很糟糕。为什么要污染包含具有编辑相关属性的人员列表的 ViewModel?几乎违反了 ViewModel 的单一职责原则,并且是代码异味 @Tseng Hrm 也许我应该澄清一下。我更新了我的答案。对我来说,ViewModel 应该为 View 建模,所以如果 View 有一个数据列表框、一个用于选定项目新名称的文本框和一个按钮,那么 ViewModel 应该包含所有这些数据。 这仍然是一个混合问题。你最终会在同一个 ViewModel 中混合浏览、显示和编辑。上面的MyViewModel
应该更名为BrowserPeopleViewModel
(因为它实际上是这样做的),并且取决于您是处于查看模式还是编辑模式,在列表框旁边显示PersonViewModel
或“EditPersonViewModel”。毕竟,模型可能是一个复杂的,由多个编辑视图分隔(更改送货地址、付款信息、婚姻状态)。将它们全部放入一个 ViewModel 将导致您复制数十个属性并创建难以维护的代码
构建 WPF 应用程序的方法不止一种。你所描述的是一种方式,而我所描述的是另一种方式。这完全取决于你想如何构建你的应用程序:)【参考方案2】:
我不确定我是否正确地回答了这个问题。 所以,我们有一个 Person 类
public class Person : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public int Age
get return age;
set
age = value;
FirePropertyChanged("Age");
public string Name
get return name;
set
name = value;
FirePropertyChanged("Name");
private void FirePropertyChanged(string v)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(v));
private int age;
private string name;
我们有一个视图模型
public class ViewModel : INotifyPropertyChanged
public ObservableCollection<Person> List get; set;
Person currentPerson;
public Person CurrentPerson
get return currentPerson;
set currentPerson = value;
FirePropertyChanged("CurrentPerson");
private void FirePropertyChanged(string v)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(v));
public event PropertyChangedEventHandler PropertyChanged;
xaml 是
<ListBox ItemsSource="Binding List" SelectedItem="Binding CurrentPerson">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="Binding Name" Width="100" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我将视图模型绑定到视图通过
ViewModel vm = new ViewModel();
vm.List = new ObservableCollection<Person>();
foreach (var i in Enumerable.Range(1,10))
vm.List.Add(new Person() Name = "Test" + i.ToString(), Age= i );
vm.CurrentPerson = null;
this.DataContext = vm;
每当我更改文本框中的值时,它都会正确更新名称。我试图为列表更改添加一个处理程序,但它并没有被触发。
vm.List.CollectionChanged += List_CollectionChanged;
void List_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
MessageBox.Show(e.Action.ToString());
如果它与您的问题陈述不同,您能否发表评论?
【讨论】:
我认为 OP 的问题是他们在 ListBox 旁边有一个单独的<TextBox Text="Binding CurrentPerson.Name" />
,他们希望能够在不更新 ListBox 数据的情况下更新该 TextBox.Text。【参考方案3】:
如果您想控制保存/更新的时间和内容,您显然需要一个 ViewModel 来编辑您的 Person 模型。
在您的列表框中选择一个人时,您必须将人的 id(避免传递对象本身)传递给绑定到要编辑的属性的PersonEditViewModel
,将人员数据加载到PersonEditViewModel
然后编辑。一旦您点击“保存”按钮,它应该提交更改并更新数据库或您用于持久性的任何内容。
使用事件/消息来回传递值/事件,或使用导航方法(如 Prism 中的INavigationAware
界面)。
【讨论】:
以上是关于只写模式下的 WPF 数据绑定控件的主要内容,如果未能解决你的问题,请参考以下文章