ListBox ObservableCollection属性未更新

本文关键字:更新 属性 ObservableCollection ListBox | 更新日期: 2023-09-27 18:17:03

在获取包含ObservableCollection的ListBox以在其中项的属性发生更改时进行更新时遇到一些问题。项所在的ObjectType正在使用INotifyPropertyChanged和PropertyChanged((。(对我来说(有趣的是,Listbox不会按照我想要/需要的方式工作,但我可以用另一种方式让它工作。我的MVVM基于以下文章/代码:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx。

我正在尝试模仿他的工作区,以包含BuildRepository中的ServerConfigs。然而,与其让"AllServerConfigs"成为工作区中的一个选项卡,我希望它成为左手边的一个列表框,用户可以在其中打开他们关闭选项卡的配置。列表框正在更新以添加/删除,但属性不会更新(例如,如果保存新名称(。

这是MainWindow.XAML;数据上下文设置为"RepositoryViewModel"。

<Window x:Class="Server_Build_Config_Tool.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:Server_Build_Config_Tool.ViewModel"
    Height="800" Width="600"
    MinWidth="600"
    MinHeight="800"
    Title="{Binding Path=DisplayName}"
    WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Window.Resources>
    <ResourceDictionary Source="pack://application:,,,/Resources/MainWindowResources.xaml" />
</Window.Resources>
<DockPanel>
    <Grid Margin="4">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="4" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" Width="170" Style="{StaticResource MainBorderStyle}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="20" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Label Grid.Row="0" Grid.Column="0" 
              Content="{Binding Path=DisplayName}" 
              HorizontalAlignment="Right"
            />
                <ListBox x:Name="lstConfigs" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Path=AllServerConfigs}" DisplayMemberPath="DisplayName"/>
                <Button Grid.Row="2" Grid.Column="0" Content="Open" CommandTarget="{Binding ElementName=lstConfigs}" Command="{Binding Path=ShowSelectedServerConfigCommand}"
                        CommandParameter="{Binding ElementName=lstConfigs,Path=SelectedItem}"/>
                <!-- A test to see if putting the view in would work -->
                <ContentControl Grid.Row="4" Content="{Binding Path=All}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
            </Grid>
        </Border>
        <Border Grid.Column="2" Style="{StaticResource MainBorderStyle}">
            <ContentPresenter Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource WorkspacesTemplate}" />
        </Border>
    </Grid>
</DockPanel>

这是RepositoryViewModel。如果AllConfigsViewModelworkspaces集合中的工作空间,则列表框会在该工作空间选项卡控件上进行精细更新(根据Josh的示例(。但是,当直接放置到MainWindow中时,视图模型属性不会更新,ObservableCollection属性也不会直接在MainWindow中更新。如果我在直接包含ObservableCollection的ListBox上强制使用ListBox.items.refresh(),则当选择发生更改时,属性会更新。

public class RepositoryViewModel : WorkspaceViewModel
{
    readonly BuildRepository _buildRepository;
    ObservableCollection<WorkspaceViewModel> _workspaces;
    public RepositoryViewModel(string buildName)
    {
        base.DisplayName = buildName + " Properties";
        _buildRepository = new BuildRepository(buildName);
        _buildRepository.ServerConfigAdded += this.OnServerConfigAddedToRepository;
        this.CreateAllServerConfigs();
    }
    void CreateAllServerConfigs()
    {
        List<ServerConfigViewModel> all =
            (from config in _buildRepository.GetServerConfigs()
             select new ServerConfigViewModel(config, _buildRepository)).ToList();
        foreach (ServerConfigViewModel scvm in all)
            scvm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
        this.AllServerConfigs = new ObservableCollection<ServerConfigViewModel>(all);
        this.AllServerConfigs.CollectionChanged += this.OnCollectionChanged;
    }
    public ObservableCollection<ServerConfigViewModel> AllServerConfigs
    {
        get;
        private set;
    }
    public ObservableCollection<WorkspaceViewModel> Workspaces
    {
        get
        {
            if (_workspaces == null)
            {
                _workspaces = new ObservableCollection<WorkspaceViewModel>();
                _workspaces.CollectionChanged += this.OnWorkspacesChanged;
            }
            return _workspaces;
        }
    }
    #region Private Helpers
    void AddNewServerConfig()
    {
        ServerConfig newServerConfig = new ServerConfig();
        ServerConfigViewModel workspace = new ServerConfigViewModel(newServerConfig, _buildRepository);
        this.Workspaces.Add(workspace);
        this.SetActiveWorkspace(workspace);
    }
    void ShowAllConfigs()
    {
        AllConfigsViewModel workspace =
           this.Workspaces.FirstOrDefault(vm => vm is AllConfigsViewModel)
           as AllConfigsViewModel;
        if (workspace == null)
        {
            workspace = new AllConfigsViewModel(_buildRepository);
            this.Workspaces.Add(workspace);
        }
        _all = workspace;
        base.OnPropertyChanged("All");
        this.SetActiveWorkspace(workspace);
    }
    AllConfigsViewModel _all;
    public AllConfigsViewModel All
    {
        get
        {
            return _all;
        }
    }
    void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null && e.NewItems.Count != 0)
            foreach (ServerConfigViewModel configVm in e.NewItems)
                configVm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
        if (e.OldItems != null && e.OldItems.Count != 0)
            foreach (ServerConfigViewModel configVm in e.OldItems)
                configVm.PropertyChanged -= this.OnServerConfigViewModelPropertyChanged;
    }
    void OnServerConfigViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        string DisplayName = "DisplayName";
        (sender as ServerConfigViewModel).VerifyPropertyName(DisplayName);
        if (e.PropertyName == DisplayName)
        {
            this.OnPropertyChanged("AllServerConfigs");
        }
    }
    void OnServerConfigAddedToRepository(object sender, ServerConfigAddedEventArgs e)
    {
        var viewModel = new ServerConfigViewModel(e.NewServerConfig, _buildRepository);
        this.AllServerConfigs.Add(viewModel);
        //this.OnPropertyChanged("AllServerConfigs");
    }
    #endregion
}

这是ServerConfigViewModel

    public class ServerConfigViewModel : WorkspaceViewModel, IDataErrorInfo
    {
        readonly ServerConfig _serverConfig;
        readonly BuildRepository _buildRepository;
        #region Server Config Fields
        public string Name
        {
            get
            {
                return _serverConfig.Name;
            }
            set
            {
                if (value == _serverConfig.Name)
                {
                    return;
                }
                _serverConfig.Name = value;
                base.OnPropertyChanged("Name");
            }
        }

        #region Presentation Properties
        public override string DisplayName
        {
            get
            {
                if (_serverConfig.Name == null)
                {
                    return "New Server";
                }
                return _serverConfig.Name;
            }
        }

        #region Constructors
        public ServerConfigViewModel(ServerConfig serverConfig, BuildRepository buildRepository)
        {
            if (serverConfig == null)
            {
                throw new ArgumentNullException("serverConfig");
            }
            if (buildRepository == null)
            {
                throw new ArgumentNullException("buildRepository");
            }
            _serverConfig = serverConfig;
            _buildRepository = buildRepository;
        }
        #endregion
        #region Public Methods
        public void Save()
        {
            if (!_serverConfig.IsValid)
            {
                throw new InvalidOperationException("Server Config is not valid, cannot be saved");
            }
            if (this.IsNewServerConfig)
            {
                _buildRepository.AddServerConfig(_serverConfig);
            }
            base.OnPropertyChanged("DisplayName");
        }
        #endregion
    }

这是AllConfigsViewModel

    public class AllConfigsViewModel : WorkspaceViewModel
    {
        #region Properties
        readonly BuildRepository _buildRepository;
        #endregion
        #region Constructor
        public AllConfigsViewModel(BuildRepository buildRepository)
        {
            base.DisplayName = "All Configs";
            _buildRepository = buildRepository;
            _buildRepository.ServerConfigAdded += this.OnServerConfigAddedToRepository;
            this.CreateAllServerConfigs();
        }
        void CreateAllServerConfigs()
        {
            List<ServerConfigViewModel> all =
                (from config in _buildRepository.GetServerConfigs()
                 select new ServerConfigViewModel(config, _buildRepository)).ToList();
            foreach (ServerConfigViewModel scvm in all)
                scvm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
            this.AllServerConfigs = new ObservableCollection<ServerConfigViewModel>(all);
            this.AllServerConfigs.CollectionChanged += this.OnCollectionChanged;
        }

        #endregion
        #region Public Interface
        public ObservableCollection<ServerConfigViewModel> AllServerConfigs
        {
            get;
            private set;
        }
        #endregion
        #region Event Handling
        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null && e.NewItems.Count != 0)
                foreach (ServerConfigViewModel configVm in e.NewItems)
                    configVm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
            if (e.OldItems != null && e.OldItems.Count != 0)
                foreach (ServerConfigViewModel configVm in e.OldItems)
                    configVm.PropertyChanged -= this.OnServerConfigViewModelPropertyChanged;
        }
        void OnServerConfigViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            string DisplayName = "DisplayName";
            (sender as ServerConfigViewModel).VerifyPropertyName(DisplayName);
            if (e.PropertyName == DisplayName)
            {
                this.OnPropertyChanged("AllServerConfigs");
            }
        }
        void OnServerConfigAddedToRepository(object sender, ServerConfigAddedEventArgs e)
        {
            var viewModel = new ServerConfigViewModel(e.NewServerConfig, _buildRepository);
            this.AllServerConfigs.Add(viewModel);
            //this.OnPropertyChanged("AllServerConfigs");
        }
        #endregion
    }

MainWindowResources.xamlWorkspaceViewModelViewModelBase都与上面链接的Josh Smith的MVVM文章中的相同(无法容纳文章正文中的所有文本(。

更新:这是MainWindow.xaml.xs

namespace Server_Build_Config_Tool
{
    public partial class MainWindow : System.Windows.Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}
namespace Server_Build_Config_Tool
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MainWindow window = new MainWindow();
            // Create the ViewModel to which 
            // the main window binds.
            var viewModel = new RepositoryViewModel("Test");
            // When the ViewModel asks to be closed, 
            // close the window.
            EventHandler handler = null;
            handler = delegate
            {
                viewModel.RequestClose -= handler;
                window.Close();
            };
            viewModel.RequestClose += handler;
            // Allow all controls in the window to 
            // bind to the ViewModel by setting the 
            // DataContext, which propagates down 
            // the element tree.
            window.DataContext = viewModel;
            window.Show();
        }
    }
}

ListBox ObservableCollection属性未更新

由于ListBox的DisplayMemberPath绑定到DisplayName,请添加

base.OnPropertyChanged("DisplayName");

到之后ServerConfigViewModel中Name属性的setter

base.OnPropertyChanged("Name");

这应该告诉它在绑定DisplayName的任何地方刷新视图。