在更改项目的属性时,获取包含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
。如果AllConfigsViewModel
是workspaces
集合中的工作空间,则列表框会在该工作空间选项卡控件上更新(根据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.xaml
,WorkspaceViewModel
和ViewModelBase
与上面链接的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();
}
}
}
答案 0 :(得分:0)
由于ListBox的DisplayMemberPath绑定到DisplayName,因此添加
base.OnPropertyChanged("DisplayName");
后面的ServerConfigViewModel中的Name属性的setter
base.OnPropertyChanged("Name");
这应该告诉它在视图中刷新DisplayName的任何地方。