简体   繁体   English

只写模式下的WPF Databind控件

[英]WPF Databind control in write-only mode

I'm trying to fiddle a little with WPF bindings, so I created a simple project. 我试图对WPF绑定进行一些摆弄,所以我创建了一个简单的项目。 Here's the code: 这是代码:

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;
}

My viewmodel contains ObservableCollection of Person, and single Person to track selected Person. 我的视图模型包含Person的ObservableCollection和用于跟踪选定Person的单个Person。

I've bound listbox's ItemsSource to ObservableCollection, and SelectedItem to single Person, called CurrentPerson. 我已将列表框的ItemsSource绑定到ObservableCollection,并将SelectedItem绑定到称为CurrentPerson的单个Person。 Also, I've bound TextBox to CurrentPerson.Name. 另外,我将TextBox绑定到CurrentPerson.Name。

Code works fine, but whenever I change content of TextBox - my listbox also changes. 代码工作正常,但是每当我更改TextBox的内容时,我的列表框也会更改。 And no matter what combination of "OneWay, TwoWay, OneWayToSource" binding modes on listbox\\selecteditem I cannot prevent listbox from updating from CurrentPerson. 而且无论listbox \\ selecteditem上“ OneWay,TwoWay,OneWayToSource”绑定模式的哪种组合,我都不能阻止列表框从CurrentPerson更新。

How can I prevent this behavior? 如何防止这种行为? I'd like to update listbox from CurrentPerson only by using ICommand interface from VM. 我只想通过使用VM中的ICommand接口来更新CurrentPerson中的列表框。

There is only one copy of the Person object which is being used in both ListBox.ItemsSource and TextBox.Text , so naturally updating that object from one location will reflect the change in the other as well. ListBox.ItemsSourceTextBox.Text都只使用一个Person对象的副本,因此自然地从一个位置更新该对象也将反映另一个位置的更改。

Two easy solutions would be 两个简单的解决方案是

  • Change the BindingMode on TextBox.Text to Explicit , so it doesn't update the Person object until you tell it to 变化BindingModeTextBox.TextExplicit ,所以直到你告诉它它不更新Person对象

  • Use a separate string property for TextBox.Text and copy it over to your SelectedPerson.Name whenever the command executes 在命令执行时,为TextBox.Text使用单独的字符串属性,并将其复制到SelectedPerson.Name

Personally I prefer the second option because I'm not a big fan of bindings that don't accurately reflect the data object behind the UI component, and it would allow the user to change the SelectedItem without resetting the TextBox value. 就我个人而言,我更喜欢第二种选择,因为我不是绑定的忠实拥护者,因为它们不能准确反映UI组件后面的数据对象,并且它将允许用户更改SelectedItem而不重置TextBox值。


For an example of the second option, your ViewModel might look like this : 对于第二个选项的示例,您的ViewModel可能看起来像这样:

public class MyViewModel()
{
    ObservableCollection<Person> People { get; set; }
    Person SelectedPerson { get; set; }
    string NewPersonName { get; set; }
    ICommand UpdatePersonName { get; }
}

where the UpdatePersonName command would execute UpdatePersonName命令将执行的位置

SelectedPerson.Name = NewPersonName;

and the CanExecute would only return true if 并且CanExecute仅在CanExecute情况下返回true:

SelectedPerson != null 
&& !NewPersonName.IsNullOrWhiteSpace() 
&& NewPersonName != SelectedPerson.Name

I'm not sure if I've followed the question properly. 我不确定我是否正确遵循了这个问题。 So, we have a class Person as 因此,我们有一个类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;
    }

And we have a view model as 我们有一个视图模型

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;

    }

The xaml is XAML是

<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding CurrentPerson}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Name}" Width="100" />
        </DataTemplate>
    </ListBox.ItemTemplate>       
</ListBox>

And I bind the view model to the view via 我通过将视图模型绑定到视图

    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;

Whenever I change the value at textbox, it updates the name properly. 每当我在文本框中更改值时,它都会正确更新名称。 I tried to add a handler for list changed, but it doesn't happen to get triggered. 我试图为列表更改添加处理程序,但是它并没有被触发。

vm.List.CollectionChanged += List_CollectionChanged;
void List_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            MessageBox.Show(e.Action.ToString());
        }

Can you comment if it isn't the same as your problem statement? 如果与您的问题陈述不同,您可以发表评论吗?

If you want to control when and what is saved/updated, you obviously need is a ViewModel for editing your Person model. 如果要控制何时保存/更新什么以及什么,显然需要一个ViewModel来编辑Person模型。

When selecting a person in your Listbox, you have to pass the person's id (avoid passing the object itself) to the PersonEditViewModel which is bound to the properties that shall be edited, load the persons data into the PersonEditViewModel and then edit. 在列表PersonEditViewModel选择一个人时,您必须将该人的ID(避免传递对象本身)传递PersonEditViewModel要编辑的属性绑定的PersonEditViewModel ,将人数据加载到PersonEditViewModel ,然后进行编辑。 Once you hit the "Save" button, it should commit the change and update the database or whatever you are using for persistence. 按下“保存”按钮后,它将提交更改并更新数据库或您用于持久性的任何内容。

Use either events/messages to pass values/events back and forth, or use a navigation approach (like INavigationAware interface in Prism). 使用事件/消息来回传递值/事件,或使用导航方法(例如Prism中的INavigationAware接口)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM