提高 属性 改变触发 "set" 在不同的 属性

RaisePropertyChanged Triggering "set" On Different Property

当我在 MVVM Light 的 RaisePropertyChanged 中遇到一个我似乎无法弄清楚的问题时,我正在做一个项目。当我尝试对我的列表提出更改时,列表确实会更新,但上面选定的索引值也会更新。传递到我选择的索引的值似乎受按下哪个键触发事件的影响(即,如果我按 "BACKSPACE",传递到 setter 的值是“-1”,而如果我输入一个字母,传递的值是“0”)

我重新创建了一个纯粹演示该问题的项目。以下是 MainVeiwModel 中的主要逻辑部分:

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        _testItems = new List<TestItem>()
        {
            new TestItem() { Name = "Test1" },
            new TestItem() { Name = "Test2" }
        };
    }

    public int SelectedIndex
    {
        get { return _selectedIndex; }
        set
        {
            _selectedIndex = value;
            RaisePropertyChanged("SelectedIndex");
            RaisePropertyChanged("SelectedText");
            RaisePropertyChanged("TestList");
        }
    }

    public string SelectedText
    {
        get
        {
            return _testItems[_selectedIndex].Name;
        }
        set
        {
            _testItems[_selectedIndex].Name = value;
            RaisePropertyChanged("TextList");
        }
    }

    public List<string> TextList
    {
        get
        {
            _textList = new List<string>();
            if (_testItems != null && _testItems.Count > 0)
            {
                foreach (TestItem item in _testItems)
                    _textList.Add(item.Name);
            }
            return _textList;
        }
        set { _textList = value; }
    }

    private int _selectedIndex;
    private List<string> _textList;
    private List<TestItem> _testItems;
}

我的XAML:

<Window x:Class="RaisePropertyBug.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:RaisePropertyBug"
    mc:Ignorable="d"
    DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <ComboBox ItemsSource="{Binding TextList, UpdateSourceTrigger=PropertyChanged}"
              SelectedIndex="{Binding SelectedIndex, UpdateSourceTrigger=PropertyChanged}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>

    <TextBox Grid.Row="1" Text="{Binding SelectedText, UpdateSourceTrigger=PropertyChanged}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

对于上下文:我有一个 ComboBox,它列出了我拥有的项目集合中的名称。有一个编辑 window,用户可以在其中更改这些项目的名称和其他属性。我的目标是在用户编辑值时更新 ComboBox 列表。在我的实际程序中,您可以使用索引 0 处的项目执行此操作,但只要按下某个键并到达 RaisePropertyChanged() 区域,任何其他索引都会自动更改为 0。

检查下面的代码是否按您的要求工作。

使用 ComboBoxSelectedItem 属性 并将所选项目绑定到编辑 screen/textbox。我在这里绑定 SelectedTestItem.Name 属性。

查看-

<Window x:Class="Whosebug.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:Whosebug"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <ComboBox ItemsSource="{Binding TestItems, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Name" SelectedValuePath="Name"
          SelectedItem="{Binding SelectedTestItem, UpdateSourceTrigger=PropertyChanged}"
    HorizontalAlignment="Center" VerticalAlignment="Center"/>

    <TextBox Grid.Row="1" Text="{Binding SelectedTestItem.Name, UpdateSourceTrigger=PropertyChanged}" Width="200"
    HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

View.cs -

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private TestItem selectedTestItem;

    public TestItem SelectedTestItem
    {
        get { return selectedTestItem; }
        set
        {
            selectedTestItem = value;
            RaisePropertyChanged("SelectedTestItem");
        }
    }



    public List<TestItem> TestItems
    {
        get;
        set;

    }

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;

        var items = new List<TestItem>()
                {
                    new TestItem() { Name = "Test1" },
                    new TestItem() { Name = "Test2" }
                };

        TestItems = items;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string prop)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
}

对于此示例,您甚至不需要 INotifyPropertyChanged。我不完全确定您要实现的目标,但这段代码将实现我从您的 post.

中收集到的内容
<Window x:Class="RaisePropertyChangedExample.BindingExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Correct View" Width="150" Height="80">
   <StackPanel Orientation="Vertical">
      <ComboBox ItemsSource="{Binding Items}"
                x:Name="ItemViews"
                HorizontalAlignment="Stretch" VerticalAlignment="Center" DisplayMemberPath="Name"/>
      <TextBox DataContext="{Binding SelectedItem, ElementName=ItemViews}" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
               HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
   </StackPanel>
</Window>

和支持代码

using System.Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace RaisePropertyChangedExample
{
   public partial class BindingExample : Window
   {
      public BindingExample()
      {
         InitializeComponent();
         DataContext = new BindingExampleViewModel();
      }
   }

   public class BindingExampleViewModel
   {
      public ObservableCollection<TestItemViewModel> Items { get; set; }
         = new ObservableCollection<TestItemViewModel>(new List<TestItemViewModel>
         {
            new TestItemViewModel {Name = "Test1"},
            new TestItemViewModel {Name = "Test2"}
         });
   }

   public class TestItemViewModel
   {
      public string Name { get; set; }
   }
}

除非所选 Itemindex 有某些需要,否则没有真正的理由反对将每个项目简单地公开为 TestItemViewModel 视图模型并绑定其他控件直接到所选项目本身。但是,如果其他控件绑定到 TestItemViewModel 的成员,那么您仍然不一定要在该视图模型上实现 INotifyPropertyChanged。

以下示例在连接现有 ViewModel 时仍会显示正确的信息:

<Window x:Class="RaisePropertyChangedExample.BindingExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Correct View" Width="150" Height="100">
   <StackPanel Orientation="Vertical">
      <ComboBox ItemsSource="{Binding Items}"
                x:Name="Combo"
                HorizontalAlignment="Stretch" VerticalAlignment="Center" DisplayMemberPath="Name"/>
      <Grid DataContext="{Binding SelectedItem, ElementName=Combo}">
         <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
         </Grid.RowDefinitions>
         <TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
         <Label Grid.Row="1" HorizontalAlignment="Stretch" Height="20" Content="{Binding Name}" />
      </Grid>
   </StackPanel>
</Window>

正常

Updating after every keystroke can diminish performance and it denies the user the usual opportunity to backspace and fix typing errors before committing to the new value. see MS reference

但是,只有在更新源时发生其他处理时才会出现此问题。如果您担心涉及的处理量,您可以通过简单地省略“UpdateSourceTrigger”声明来切换到 LostFocus 的默认行为。