C# WPF ListBox绑定最初可以工作,但之后不能工作

C# WPF ListBox绑定最初可以工作,但之后不能工作,c#,wpf,xaml,mvvm,data-binding,C#,Wpf,Xaml,Mvvm,Data Binding,基本问题=>选择其他组合框值时,我的列表框不会更新 我正在按照MVVM模式制作一个WPF应用程序(或者至少尝试这样做)。我有一个基于当前选择的服务器获取的服务器列表 “申请”。服务器列表放在一个列表框中,当我通过下拉菜单更改“应用程序”时,我希望列表框与新服务器一起更新 “Current:”值将根据所做的选择进行更改(以便绑定至少起作用)。我有一个ServerListViewModel类,它实现了INotifyPropertyChanged。这里有一个片段 ServerListViewMode

基本问题=>选择其他组合框值时,我的列表框不会更新

我正在按照MVVM模式制作一个WPF应用程序(或者至少尝试这样做)。我有一个基于当前选择的服务器获取的服务器列表 “申请”。服务器列表放在一个列表框中,当我通过下拉菜单更改“应用程序”时,我希望列表框与新服务器一起更新

“Current:”值将根据所做的选择进行更改(以便绑定至少起作用)。我有一个ServerListViewModel类,它实现了INotifyPropertyChanged。这里有一个片段

ServerListViewModel(代码片段)

设置viewmodel.cs

下拉菜单位于设置选项卡上,而服务器列表有自己的选项卡。这些选项卡有自己的视图模型

以下是此视图模型的一个片段:

public ComboBoxItem CurrentApplication
{
    get { return _settingsModel.CurrentApplication; }
    set {
        SetCurrentApplication(value);
    }
}       

private void SetCurrentApplication(ComboBoxItem value)
{
    if (ReferenceEquals(_settingsModel.CurrentApplication, value)) return;
    _savedSettings = MySettings.Load();
    if (value.Content == null)
    {
        _settingsModel.CurrentApplication =
            new ComboBoxItem { Content = _savedSettings.CurrentApplication };
    }
    else
    {
        _settingsModel.CurrentApplication = value;
        _savedSettings.CurrentApplication = (string)value.Content;
        _savedSettings.Save();
        ServerListViewModel.Instance.ServerList = ConfigUtility.GetServers();
    }   
    InvokePropertyChanged("CurrentApplication");
}
我还有一个MySettings对象,它将值保存到JSON文件中,以便保存活动的“应用程序”b/w会话

XAML

<TabControl DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" Height="Auto" TabStripPlacement="Bottom">
    <!-- Server List -->
    <TabItem Name="ServerListTab" Header="Server List">
        <TabItem.DataContext>
            <viewModel:ServerListViewModel />
        </TabItem.DataContext>
        <ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
        ItemsSource="{Binding ServerList, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"
        SelectedItem="{Binding SelectedServer}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="FontSize" Value="14"></Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </TabItem>
    <!-- Settings -->
    <TabItem Name="SettingsTab" Header="Settings">
        <TabItem.DataContext>
             <viewModel:SettingsViewModel />
        </TabItem.DataContext>
        <StackPanel>
             <TextBlock FontWeight="Bold">Application</TextBlock>
             <WrapPanel>
                  <TextBlock>Current:</TextBlock>
                  <TextBlock Text="{Binding CurrentApplication.Content}"></TextBlock>
             </WrapPanel>
             <TextBlock>Select</TextBlock>
             <ComboBox Name="TheComboBox" SelectedItem="{Binding CurrentApplication}">
                  <ComboBoxItem>NetWebServer</ComboBoxItem>
                  <ComboBoxItem>NetSearchService</ComboBoxItem>
                  <ComboBoxItem>NetInterface</ComboBoxItem>
             </ComboBox>
             <TextBlock FontWeight="Bold">Service</TextBlock>
             <WrapPanel>
                  <TextBlock>Current:</TextBlock>
                  <TextBlock></TextBlock>
             </WrapPanel>
             <TextBlock>Select</TextBlock>
             <ComboBox></ComboBox>
             <CheckBox>
                  Perfomance mode
             </CheckBox>
             <CheckBox>
                  Show live servers
             </CheckBox>
             <CheckBox>
                  Show test servers
             </CheckBox>
             <CheckBox>
                  Show only parameters with IP's
             </CheckBox>
        </StackPanel>
    </TabItem>
</TabControl>

应用
当前:
挑选
网络服务器
NetSearchService
网络接口
服务
当前:
挑选
性能模式
显示实时服务器
显示测试服务器
仅显示具有IP的参数
我还没有把它们分成不同的视图。问题在于,它们不是不同文件中的独立用户控件,具有自己的视图模型?但是,我已将不同的选项卡项设置为相应的视图模型

结论/问题


如何获得它,以便通过在组合框中选择一个新项目,它也会更新服务器列表?通过调试我的代码,似乎每件事都是一行一行地工作的,所以无论如何列表框不能被通知任何更改?如果我在选择其他“应用程序”的情况下关闭应用程序,然后将其重新打开,则它会正确地使用保存的设置以新值填充列表框。但是,当应用程序打开时,我无法更改列表框。有什么想法吗?

如果我什么都做了,SetCurrentApplication()将需要刷新服务器列表并弹出一个INotifyChanged事件。在XAML中,您创建了一个ServerListViewModel对象,该对象被设置为TabItem的DataContext

但是,在方法SetCurrentApplication中,您将更新另一个ServerListViewModel对象的ServerList属性(该静态属性的初始值设定项创建了该对象)。在UI中看不到任何更改,因为用作DataContext的ServerListViewModel对象实际上未被触及

只需使用静态ServerListViewModel.Instance属性中的ServerListViewModel对象作为DataContext:

<TabItem
    DataContext="{x:Static viewModel:ServerListViewModel.Instance}"
    Name="SettingsTab" Header="Settings"
>
    ...

...
现在,在SetCurrentApplication方法中更新ServerListViewModel.Instance.ServerList应该会导致数据绑定更新


另一个更干净的替代方法是避免静态实例属性,并在视图模型之间使用某种消息传递。SetCurrentApplication方法可能会发送一条消息,要求更新服务器列表。ServerListViewModel类将处理该消息并执行更新服务器列表的实际工作。MVVM库(如MVVM Light和其他)提供了Messenger组件,可以实现这一点。

如果您使用的是WPF,您可能应该使用ObservableCollections而不是BindingList我正在访问SettingsViewModel作为SettingsTab的DataContext,所以我不能像上面所示那样将DataContext设置为ServerListViewModel静态实例,对吗?您是否建议我放弃这个想法,转而采用您提到的消息传递实现?从技术上讲,您可以使用ServerListViewModel的单个静态实例作为设置Stab的DataContext。这对于您的应用程序是否有意义,以及这需要多少代码更改,我不知道。但是,是的,正如我所说的,我倾向于消息传递实现。现在,SettingsViewModel和ServerListViewModel之间有一个硬依赖关系,这是令人头痛的原因。你可能会同意,我们在这里工作的场景仍然相当简单。(续)想象一下,您的软件变得越来越复杂,视图模型之间的硬依赖性可能会带来很多麻烦。使用消息传递将消除这些硬依赖并避免这些头痛:)也就是说,并不是所有虚拟机之间的硬依赖都是有害的。如果viewmodels具有明显的父子关系或类似关系(如一个VM生成/提供/聚合另一个VM),那么硬依赖当然不是问题。。。
<TabControl DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" Height="Auto" TabStripPlacement="Bottom">
    <!-- Server List -->
    <TabItem Name="ServerListTab" Header="Server List">
        <TabItem.DataContext>
            <viewModel:ServerListViewModel />
        </TabItem.DataContext>
        <ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
        ItemsSource="{Binding ServerList, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"
        SelectedItem="{Binding SelectedServer}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="FontSize" Value="14"></Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </TabItem>
    <!-- Settings -->
    <TabItem Name="SettingsTab" Header="Settings">
        <TabItem.DataContext>
             <viewModel:SettingsViewModel />
        </TabItem.DataContext>
        <StackPanel>
             <TextBlock FontWeight="Bold">Application</TextBlock>
             <WrapPanel>
                  <TextBlock>Current:</TextBlock>
                  <TextBlock Text="{Binding CurrentApplication.Content}"></TextBlock>
             </WrapPanel>
             <TextBlock>Select</TextBlock>
             <ComboBox Name="TheComboBox" SelectedItem="{Binding CurrentApplication}">
                  <ComboBoxItem>NetWebServer</ComboBoxItem>
                  <ComboBoxItem>NetSearchService</ComboBoxItem>
                  <ComboBoxItem>NetInterface</ComboBoxItem>
             </ComboBox>
             <TextBlock FontWeight="Bold">Service</TextBlock>
             <WrapPanel>
                  <TextBlock>Current:</TextBlock>
                  <TextBlock></TextBlock>
             </WrapPanel>
             <TextBlock>Select</TextBlock>
             <ComboBox></ComboBox>
             <CheckBox>
                  Perfomance mode
             </CheckBox>
             <CheckBox>
                  Show live servers
             </CheckBox>
             <CheckBox>
                  Show test servers
             </CheckBox>
             <CheckBox>
                  Show only parameters with IP's
             </CheckBox>
        </StackPanel>
    </TabItem>
</TabControl>
<TabItem
    DataContext="{x:Static viewModel:ServerListViewModel.Instance}"
    Name="SettingsTab" Header="Settings"
>
    ...