Control失去了对UpdateSourceTrigger = PropertyChanged的关注

时间:2017-06-30 07:45:06

标签: c# wpf xaml data-binding

我有一个ListBox,其ItemSource绑定到ObservableCollectionListBox具有以下(最小化)ItemTemplate

<ListBox ItemsSource="{Binding SelectedDirectory.PluginValues}" HorizontalContentAlignment="Stretch">
    <ListBox.ItemTemplate>
         <DataTemplate>
              <Grid HorizontalAlignment="Stretch">                                   
                   <Grid Height="29" Margin="5" HorizontalAlignment="Stretch">
                       <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="1*" />
                            <ColumnDefinition Width="1*" />
                       </Grid.ColumnDefinitions>
                       <TextBox Grid.Column="0" Width="Auto" 
                                Text="{Binding Name, Mode=TwoWay
                                     , UpdateSourceTrigger=PropertyChanged}" />
                       <TextBox Grid.Column="1" Width="Auto" 
                                Text="{Binding Value, Mode=TwoWay
                                     , UpdateSourceTrigger=PropertyChanged}" />
                   </Grid>
              </Grid>
         </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

绑定选项UpdateSourceTrigger=PropertyChanged会导致TextBoxes在每次按键到周围ListBox后失去焦点。当我删除该选项时,焦点不会丢失,但TextBox中的值不会立即保存到属性中。因此,当我输入一个值然后提出一个命令(例如通过保存Button)时,该属性不会更新。只有当我先点击其他地方然后再提高命令时,值才会更新。

修改

简化的ViewModel:

public class ViewModel : INotifyPropertyChanged
{
    private FbiDirectory selectedDirectory;

    public FbiDirectory SelectedDirectory
    {
        get
        {
            return this.selectedDirectory;
        }

        set
        {
            this.selectedDirectory = value;

            this.OnPropertyChanged("SelectedDirectory");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

FbiDirectory类(与联邦调查局无关):

public class FbiDirectory : INotifyPropertyChanged
{
    private ObservableCollection<PluginValue> pluginValues = new ObservableCollection<PluginValue>();

    public ObservableCollection<PluginValue> PluginValues
    {
        get
        {
            return this.pluginValues;
        }

        set
        {
            this.pluginValues = value;

            this.OnPropertyChanged("PluginValues");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

PluginValue类:

public class PluginValue : INotifyPropertyChanged
{
    private string name;
    private string value;

    public string Name
    {
        get => name;

        set
        {
            name = value;
            this.OnPropertyChanged("Name");
        }
    }
    public string Value
    {
        get => value;

        set
        {
            this.value = value;
            this.OnPropertyChanged("Value");
        }
    }

    public PluginValue(string name, string value)
    {
        this.Name = name;
        this.Value = value;
    }

    public PluginValue()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

1 个答案:

答案 0 :(得分:0)

您问题的简化代码可能如下所示:

public class MyViewModel
    {
        public ObservableCollection<ItemViewModel> Items { get; set; }

        public ICommand SaveCommand { get; }

        public MyViewModel()
        {
            SaveCommand = new RelayCommand(OnSaveCommand);
            Items = new ObservableCollection<ItemViewModel>();
            Items.Add(new ItemViewModel{Name = "test1", Value = "test1"});
            Items.Add(new ItemViewModel{Name = "test2", Value = "test2"});
        }

        private void OnSaveCommand()
        {
            var message = Items.Aggregate(new StringBuilder(),
                (builder, item) => builder.AppendLine($"{item.Name} {item.Value}"));
            message.AppendLine("Will be save");


            MessageBox.Show(message.ToString());
        }
    }

    public class ItemViewModel : NotifiableObject
    {
        private string _value;
        private string _name;

        public string Name
        {
            get => _name;
            set
            {
                OnPropertyChanged();
                _name = value;
            }
        }

        public string Value
        {
            get => _value;
            set
            {
                OnPropertyChanged();
                _value = value;
            }
        }
    }

    public class NotifiableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

有了这个观点:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <ListBox ItemsSource="{Binding Items}" Grid.Row="0">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid HorizontalAlignment="Stretch">
                    <Grid Height="29" Margin="5" HorizontalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="1*" />
                            <ColumnDefinition Width="1*" />
                        </Grid.ColumnDefinitions>
                        <TextBox Grid.Column="0" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                        <TextBox Grid.Column="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    </Grid>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <Button Grid.Row="1" Content="Save" Command="{Binding SaveCommand}"></Button>
</Grid>

不确定代码中的错误,但是:

  • 您应该将NotifableObject用于常见的RaisePropertyChanged行为
  • 我真的不明白为什么你使用FbiDirectory而不是直接把pluginValues放到你的ViewModel中?

希望它有所帮助。