ListBox ObservableCollection属性未更新

时间:2014-08-07 02:10:17

标签: c# wpf mvvm listbox observablecollection

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

我正在尝试从BuildRepository模拟他的Workspaces以包含ServerConfigs。但是,我不希望“AllServerConfigs”成为该工作区中的选项卡,而是希望它成为左侧的列表框,用户可以打开配置,关闭选项卡。列表框正在更新以进行添加/删除,但属性未更新(例如,如果您保存新名称)。

这是MainWindow.XAML; datacontext设置为“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的示例)。但是,viewmodel属性在直接放入MainWindow时不会更新,ObservableCollection属性也不会直接在MainWindow中更新。如果我在ListBox上直接强制ListBox.items.refresh()包含OberservableCollection,那么当选择更改属性时会更新。

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文章完全相同(不能适合帖子正文中的所有文字)。< / p>

更新: 这是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();
        }
    }
}

1 个答案:

答案 0 :(得分:0)

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

base.OnPropertyChanged("DisplayName");

后面的ServerConfigViewModel中的Name属性的setter

base.OnPropertyChanged("Name");

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