WPF ListBox绑定首先起作用,但之后不起作用

时间:2015-08-14 21:25:27

标签: c# wpf xaml mvvm data-binding

基本问题 =>当选择了不同的ComboBox值时,我的ListBox不会更新

我正在制作一个遵循MVVM模式的WPF应用程序(或者至少尝试过)。我有一个基于当前选择的服务器列表 “应用”。服务器列表放入ListBox,当我通过下拉菜单更改“应用程序”时,我希望ListBox能够使用新服务器进行更新。

View snippet

“当前:”值将根据所做的选择而改变(因此绑定至少有效)。我有一个ServerListViewModel类,它实现了INotifyPropertyChanged。这是一个片段

ServerListViewModel(摘录)

public ServerListViewModel()
{
    _serverListModel = new ServerListModel
    {
        ServerList = ConfigUtility.GetServers()
    };
}

...

public BindingList<string> ServerList
{
    get { return _serverListModel.ServerList; }
    set
    {
        if (ReferenceEquals(_serverListModel.ServerList, value)) return;
        _serverListModel.ServerList = value;
        InvokePropertyChanged("ServerList");
    }
}

构造函数正常工作,ListBox更新以反映ServerList属性。 'ConfigUtility.GetServers'在JSON文件中使用活动“Application”的保存值。

在这个类中我设置了这样的静态属性,以便我可以尝试从另一个视图模型访问该类

public static ServerListViewModel Instance { get; } = new ServerListViewModel();

SettingsViewModel.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文件,以便我可以保存活动的“应用程序”黑白会话。

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>

我还没有把它们分成单独的视图。问题是它们不是单独的用户控件在不同的文件中有自己的视图模型吗?不过,我已将不同的标签项设置为相应的视图模型。

结论/问题

如何通过在ComboBox中选择新项目来获取它,它还会更新服务器列表?通过调试我的代码,似乎一切都是逐行工作的,所以不知道ListBox不会被通知更改?如果我选择了不同的“应用程序”关闭应用程序,然后将其打开,它会正确使用保存的设置用新值填充ListBox。但是当应用程序打开时,我无法更改ListBox。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

如果我遵循所有内容,SetCurrentApplication()将需要刷新服务器列表并弹出INotifyChanged事件。它看起来并不像那样。

答案 1 :(得分:1)

在您的XAML中,您创建一个ServerListViewModel对象,该对象被设置为TabItem的DataContext。

    

但是,在方法 SetCurrentApplication 中,您更新另一个 ServerListViewModel对象的ServerList属性(此静态属性的初始化程序创建它)。您没有在UI中看到任何更改,因为实际上没有触及用作DataContext的ServerListViewModel对象。

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

std::vector <std::string> json_str_rows;
std::vector <float> json_float_ret;

// get values from rows
try
{
    for (size_t i = 0; i < json_str_rows.size(); i++)
    {
        if (document.Parse((json_str_rows.at(i)).c_str()).HasParseError())
        {
            std::cerr << "system_rapidjson::read_json_str >> document.Parse >> ERROR at(" << i << ") = " << json_str_rows.at(i) << std::endl;
            throw -3;
        }

        json_float_ret.push_back (document["Val"].GetDouble());    // get value from row
    }
}
catch (const std::exception &e)
{
    std::cerr << "system_rapidjson::read_json_str >> get values from rows >> ERROR = " << e.what() << std::endl;
    throw -2;
}

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

另一个更清晰的替代方法是避开静态 Instance 属性,并在视图模型之间使用某种消息传递。 SetCurrentApplication 方法可以发送应更新服务器列表的消息。 ServerListViewModel类将处理该消息并执行更新服务器列表的实际工作。 MVVM Light等MVVM库提供了便于实现的Messenger组件。