繁体   English   中英

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

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

几个小时以来,我一直在努力反对WPF ListBox项绑定,希望能得到一些建议。 我的应用程序包含三个主要元素:

  1. 通过TcpClient发送和接收数据的Player类。
  2. 一个MainWindow ,用于处理GUI并公开Player可以调用的方法,以基于网络消息提供用于更新UI的数据。
  3. 一个名为HostLobbyUserControl ,其中包含1)一个名为gamesListBoxListBox用于显示客户端通过Player添加的新游戏,以及2)UI元素,用于添加要向所有客户端广播的新游戏。

我已经确认“上游”作品是可行的。 您可以在HostLobby上输入新游戏信息, HostLobby提交,并按预期传播给客户端。 此外,客户端可以正确响应服务器消息,告知服务器已添加新游戏。

问题是,我无法更新gameListBox 我在HostLobby控件和MainWindow上都装配了测试按钮,以验证绑定是否正常工作。 我似乎无法通过从Player传递数据来进行更新。 有什么想法我做错了吗?

相关代码:

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

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

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

<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>

由于正在修改工作线程上的ObservableCollection ,因此未更新ListBox ,这意味着在工作线程上也引发了集合的CollectionChanged事件。 为了解决这个问题,您需要在UI线程上修改列表。 为此,您有几种选择,但是首先想到的是:

使用Dispatcher.BeginInvoke

AddGameToListHostLobby.cs ,将修改列表的语句放在Dispatcher.BeginInvoke

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

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

首先,创建一个锁对象作为HostLobby类的私有成员。 然后,在初始化ObservableCollection ,调用以下命令:

BindingOperations.EnableCollectionSynchronization(games, _yourLockObj);

然后,只要在HostLobby修改列表,就使用该锁。 因此,在这种情况下,您需要更改AddGameToList的列表修改,以使其使用锁定:

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

在我看来,后者似乎是一个更好的选择,但是只有在使用.NET 4.5或更高版本时,才可以使用后者。

暂无
暂无

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

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