简体   繁体   English

WPF从不同类中调用的方法更新ListBox项

[英]WPF updating ListBox items from method called in different class

I have been banging my head against WPF ListBox item binding for hours and am hoping to get some advice. 几个小时以来,我一直在努力反对WPF ListBox项绑定,希望能得到一些建议。 My application has three main elements: 我的应用程序包含三个主要元素:

  1. A Player class that sends and receives data over a TcpClient . 通过TcpClient发送和接收数据的Player类。
  2. A MainWindow that handles the GUI and exposes methods that Player can call to provide data for updating the UI based on network messages. 一个MainWindow ,用于处理GUI并公开Player可以调用的方法,以基于网络消息提供用于更新UI的数据。
  3. A UserControl called HostLobby that contains 1) a ListBox called gamesListBox for displaying new games as they are added by clients via Player and 2) UI elements for adding a new game to be broadcast to all clients. 一个名为HostLobbyUserControl ,其中包含1)一个名为gamesListBoxListBox用于显示客户端通过Player添加的新游戏,以及2)UI元素,用于添加要向所有客户端广播的新游戏。

I have confirmed that the "upstream" piece works. 我已经确认“上游”作品是可行的。 You can enter new game information on HostLobby , submit, and it propagates to clients as expected. 您可以在HostLobby上输入新游戏信息, HostLobby提交,并按预期传播给客户端。 In addition, clients respond properly to server messages telling them a new game has been added. 此外,客户端可以正确响应服务器消息,告知服务器已添加新游戏。

The problem is, I cannot get gameListBox to update. 问题是,我无法更新gameListBox I rigged up test buttons on both the HostLobby control and MainWindow to verify that binding is working properly - which it is. 我在HostLobby控件和MainWindow上都装配了测试按钮,以验证绑定是否正常工作。 I just can't seem to update by passing data from Player . 我似乎无法通过从Player传递数据来进行更新。 Any ideas what I'm doing wrong? 有什么想法我做错了吗?

Relevant code: 相关代码:

Player.cs Player.cs

public void AddGameToLobby(string name, int mp)
{
    // name and mp are provided by the network message handler and work fine
    WriteToLog("Attempting to add game to host lobby", true);        
    mainWindow.AddGameToLobby(name, mp);    
}

MainWindow.cs MainWindow.cs

public void AddGameToLobby(string n, int mp)
{
    hostLobby.AddGameToList(n, mp);
}

HostLobby.cs HostLobby.cs

private MainWindow parent; // used to call an AddGame event when client adds a game
public ObservableCollection<Game> games;
public class Game
{
    public string Name { get; set; }
}
public HostLobby(MainWindow mw)
{
    InitializeComponent();
    parent = mw;
    games = new ObservableCollection<Game>();

    // add some test items - this works. the ListBox updates properly
    games.Add(new Game() { Name = "game1" });
    games.Add(new Game() { Name = "game2" });
    games.Add(new Game() { Name = "game3" });

    gamesListBox.ItemsSource = games;
}
public void AddGameToList(string n, int maxp)
{
    // called to announce a new game added by another client
    // call stack is Player.AddGameToLobby -> MainWindow.AddGameToLobby -> this.AddGameToList
    string msg = String.Format("{0} (0/{1})", n, maxp.ToString());
    games.Add(new Game() { Name = msg });
}

HostLobby.xaml HostLobby.xaml

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <StackPanel Grid.Column="0" Margin="50" Orientation="Vertical" VerticalAlignment="Center">
        <StackPanel Orientation="Horizontal">
            <Label HorizontalContentAlignment="Right" Width="80">Game Name:</Label>
            <TextBox Name="gameNameTextBox" VerticalContentAlignment="Center" Width="200"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
            <Label HorizontalContentAlignment="Right" Width="80">Max Players:</Label>
            <TextBox Name="maxPlayersTextBox" VerticalContentAlignment="Center" Width="200"/>
        </StackPanel>
        <Button Name="addGameButton" Click="addGameButton_Click" Margin="0,20,0,0" Width="200" Height="30">Add Game</Button>
    </StackPanel>

    <StackPanel Orientation="Vertical" VerticalAlignment="Center" Margin="50" Grid.Column="1">
        <Label>Current Games</Label>
        <ListBox Name="gamesListBox" MinHeight="200">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <TextBlock Text="{Binding Name}"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Grid>

Your ListBox is not being updated because you're modifying the ObservableCollection on a worker thread, which means that the collection's CollectionChanged event is being raised on the worker thread as well. 由于正在修改工作线程上的ObservableCollection ,因此未更新ListBox ,这意味着在工作线程上也引发了集合的CollectionChanged事件。 To remedy this, you need to modify your list on the UI thread. 为了解决这个问题,您需要在UI线程上修改列表。 To achieve this, you have a few options, but the first ones that come to mind are: 为此,您有几种选择,但是首先想到的是:

Using Dispatcher.BeginInvoke 使用Dispatcher.BeginInvoke

In AddGameToList in HostLobby.cs , put the statement that modifies the list inside a Dispatcher.BeginInvoke : AddGameToListHostLobby.cs ,将修改列表的语句放在Dispatcher.BeginInvoke

Application.Current.Dispatcher.BeginInvoke((MethodInvoker)(() => games.Add(new Game() { Name = msg })));

Using BindingOperations.EnableCollectionSynchronization (.NET 4.5 or later) 使用BindingOperations.EnableCollectionSynchronization(.NET 4.5或更高版本)

First, create a lock object as a private member of your HostLobby class. 首先,创建一个锁对象作为HostLobby类的私有成员。 Then, after initializing your ObservableCollection , call the following: 然后,在初始化ObservableCollection ,调用以下命令:

BindingOperations.EnableCollectionSynchronization(games, _yourLockObj);

Then, use the lock whenever you modify the list within HostLobby . 然后,只要在HostLobby修改列表,就使用该锁。 So in this instance, you'd want to change the list modification in AddGameToList such that it uses the lock: 因此,在这种情况下,您需要更改AddGameToList的列表修改,以使其使用锁定:

lock (_yourLockObj)
{
    games.Add(new Game() { Name = msg });
}

The latter seems like a better choice in my opinion, but it is only available if you're using .NET 4.5 or later. 在我看来,后者似乎是一个更好的选择,但是只有在使用.NET 4.5或更高版本时,才可以使用后者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM