数据绑定不完全更新ListBox

时间:2012-07-29 18:44:57

标签: wpf data-binding mvvm listbox

我正在使用MVVM模式,并且数据绑定列表框没有完全更新。

有一个模型视图,其中包含绑定到列表的Observable机器集合:

<ListBox Name="MachinesList"
                         Height="300" 
                         Width="290" 
                         DataContext="{Binding Path=AllMachines}"
                         SelectionMode="Single"
                         ItemsSource="{Binding}" SelectionChanged="MachinesList_SelectionChanged"
                         HorizontalAlignment="Right"
                         >

集合AllMachines包含一个可观察的MachineModelViews集合,它们又绑定到MachineView,显示机器的名称和位置:

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
            <Label Name="NameLabel" Content="{Binding Path=Name}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Width="50" />
            <Label Content="Location:" Width="120"
                HorizontalAlignment="Right"
                HorizontalContentAlignment="Center"
                VerticalContentAlignment="Center"
                Target="{Binding ElementName=locationLabel}" 
            />
            <Label Content="{Binding Path=Location.Name}" Name="locationLabel" HorizontalContentAlignment="Center" Width="60"/>
</StackPanel>

问题:

当将值添加到集合中时,事情可以更新。删除计算机时,只有绑定到Location.Name的Label更新,其他两个保留在列表框中。我已经确认该集合实际上正在更新并正确删除MachineModelView,但是在重新启动应用程序之前,一些带有它的名称的标签和带有“Location:”的“label label”将继续存在:

之前:

enter image description here

删除后:

enter image description here

重启应用后:

enter image description here

删除按钮调用一个命令,该命令从私有成员中删除该项,该私有成员支持AllMachines属性和存储库(最终通过Entity Framework插入数据库):

    RelayCommand _deleteCommand;

    public ICommand DeleteCommand
    {
        get
        {
            if (_deleteCommand == null)
            {
                _deleteCommand = new RelayCommand(
                    param => this.Delete(),
                    null
                    );
            }
            return _deleteCommand;
        }
    }

    void Delete()
    {
        if (_selectedMachine != null && _machineRepository.GetMachines().
            Where(i => i.Name == _selectedMachine.Name).Count() > 0)
        {
            _machineRepository.RemoveMachine(_machineRepository.GetMachines().
                Where(i => i.Name == _selectedMachine.Name).First());
            _allMachines.Remove(_selectedMachine);
        }
    }

注意:在AllMachines中只能有一个带有名称的项目(这由存储库中的添加逻辑和命令本身处理)所以删除“First”应该没问题。

AllMachines属性:

public ObservableCollection<MachineViewModel> AllMachines
    {
        get
        {
            if(_allMachines == null)
            {
                List<MachineViewModel> all = (from mach in _machineRepository.GetMachines()
                                              select new MachineViewModel(mach, _machineRepository)).ToList();
                _allMachines = new ObservableCollection<MachineViewModel>(all);
            }
            return _allMachines;
        }
        private set
        {
            _allMachines = value;
        }
    }

选择更改了事件处理程序:

private void MachinesList_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count > 0 && e.AddedItems[0] is MachineViewModel)
            ((MachinesViewModel)this.DataContext).SelectedMachine = (MachineViewModel)e.AddedItems[0];
    }

4 个答案:

答案 0 :(得分:0)

如果'AllMachines是你的实际可观察​​集合,我会将itemssource绑定到它而不是将它绑定到你的datacontext,然后将你的viewmodel绑定为你的datacontext。在itemssource绑定之后调用updatesourcetrigger也可能会有所帮助。

 DataContext="{Binding Path=YourViewModel}"
                     SelectionMode="Single"
                     ItemsSource="{Binding AllMachines, UpdateSourceTrigger="PropertyChanged"}"

您可以在某些标签上使用该updatesourcetrigger以及绑定的位置。除此之外,你的xaml看起来还不错,任何人都很难说,直到他们看到一切。

答案 1 :(得分:0)

_machineRepository.RemoveMachine(_machineRepository.GetMachines().
            Where(i => i.Name == _selectedMachine.Name).First());
        AllMachines.Remove(_selectedMachine);

而不是字段_allMachines.Remove直接从属性AllMachines中删除

  

AllMachines.Remove

我希望这会有所帮助。

答案 2 :(得分:0)

如果你按照你在这里说的方式编码,那么一切都应该有效。请发布丢失的代码:

  • 您的AllMaschines Property
  • 你的MachinesList_SelectionChanged事件&lt; - 做MVVM时几乎不需要这个

请调试你的Delete()方法,以确保:_allMachines.Remove(_selectedMachine);被击中,机器真的从您的收藏中删除。

答案 3 :(得分:0)

我发现了我的问题,非常愚蠢:我有一个事件在存储库更新时触发。在我只有一个add命令之前,我正在添加删除此问题的能力。事实证明,MachinesModelView正在处理事件,以更新它的内部变量,该变量是AllMachines属性的源:

void OnMachineAddedToRepository(object sender, MachineAddedOrRemovedEventArgs e)
    {
        var viewModel = new MachineViewModel(e.NewMachine, _machineRepository);
        this.AllMachines.Add(viewModel);
    }

我修改了事件参数以在添加和删除之间切换但忘记更新事件处理程序。现在它确实有效:

void OnMachineAddedOrRemovedFromRepository(object sender, MachineAddedOrRemovedEventArgs e)
    {
        if (e.Added)
        {
            var viewModel = new MachineViewModel(e.NewMachine, _machineRepository);
            this.AllMachines.Add(viewModel);
        }
        else if (AllMachines.Where(i => i.Name == e.NewMachine.Name).Count() > 0)
            AllMachines.Remove(AllMachines.Where(i => i.Name == e.NewMachine.Name).First());
    }

这使得如此难以追踪的原因是额外的项目只能非常短暂地运行,直到删除命令的AllMachines.Remove部分运行。因此,检查预删除计数和删除后计数看起来是正确的,当项目从存储库中删除时,它是中间的内容,该事件触发了一个事件,该事件将项目添加回来并将其置于这种奇怪的状态。奇怪的是,它一直只有MachineModelView的位置部分浮动。我现在只是让事件处理程序添加或删除_allMachines变量中的项目,并且不要在delete命令中显式触摸它(delete命令只是从存储库中删除它并让UI变量在一秒钟内赶上事件触发需要)。谢谢你们的帮助。