3

几天前我创建了这个线程,因为我无法从另一个线程更新 ObservableCollection。这是来自线程的解决方案:

Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
{
    TheTVDB theTvdb = new TheTVDB();
    foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter"))
    {
        this.Overview.Add(tvSeries);
    }
}),
DispatcherPriority.Background);

但是,这似乎不是真正的解决方案,因为 UI 在执行委托时仍然冻结。我的猜测是,上面并没有真正在另一个线程上运行任何东西,而是将它全部分派给 UI 线程。所以我真正想做的是自己创建一个新线程并进行加载(这发生在theTvdb.SearchSeries())。然后我将遍历结果并将其添加到我的ObservableCollection,这必须发生在 UI 线程上。

这种方法听起来对吗?

我想出了下面我认为会加载结果并将它们添加到 ObervableCollection 并在我的列表视图中显示它们而不会冻结 UI 的方法。

Thread thread = new Thread(new ThreadStart(delegate
{
    TheTVDB theTvdb = new TheTVDB();
    List<TVSeries> dexter = theTvdb.SearchSeries("Dexter");

    foreach (TVSeries tvSeries in dexter)
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
        {
            this.Overview.Add(tvSeries);
        }),
        DispatcherPriority.Normal);
    }
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

以上不会产生任何错误。相反,什么也没有发生。UI 不会冻结,但不会更新。OverviewUI 中没有显示其中的对象,我已经测试了绑定是否正确。如果我不加载它们并将它们添加到ObservableCollection另一个线程上,这些对象将正确显示。

我尝试过的另一个解决方案是使用此答案中的 MTObservableCollection 来解决类似问题。当使用 的那个子类时ObservableCollection,我自己没有发送任何东西。这给了我以下错误:

必须在与 DependencyObject 相同的线程上创建 DependencySource。

谁能告诉我我该怎么做:

  1. 在单独的线程上加载一些东西
  2. 使用步骤 1 的结果更新绑定到列表视图的 ObservableCollection
  3. 在 UI 中显示结果而不冻结 UI

我希望你能进一步帮助我。

更新

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:acb="clr-namespace:AttachedCommandBehavior"
    mc:Ignorable="d"
    x:Class="TVSeriesLibrary.OverviewView"
    x:Name="UserControl"
    d:DesignWidth="512"
    d:DesignHeight="480">

    <UserControl.Resources>
        <DataTemplate x:Key="CoverTemplate">
            <StackPanel Orientation="Horizontal">
                <Image Width="82" Height="85" Stretch="Fill" Source="{Binding Cover}" Margin="10,10,0,10"/>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="#515050">
        <Grid.Resources>
            <ResourceDictionary>
                <Style x:Key="ItemContStyle" TargetType="{x:Type ListViewItem}">
                    <Setter Property="Background" Value="#282828" />
                    <Setter Property="Margin" Value="0,0,0,5" />
                    <Setter Property="Padding" Value="0" />
                </Style>
            </ResourceDictionary>
        </Grid.Resources>

        <ListView Height="112"
                  Width="488"
                  Margin="12,150,12,218"
                  Foreground="#ffffff"
                  Background="#515050"
                  VerticalContentAlignment="Center"
                  BorderThickness="0"
                  ItemTemplate="{StaticResource CoverTemplate}"
                  ItemsSource="{Binding Overview}">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
        </ListView>

        <ListView Height="170"
                  Margin="10,298,10,0"
                  VerticalAlignment="Center"
                  Foreground="#ffffff"
                  Background="#515050"
                  VerticalContentAlignment="Center"
                  BorderThickness="0"
                  Width="488" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                  ItemsSource="{Binding Path=Overview}"
                  SelectedItem="{Binding Path=SelectedTVSeries}"
                  ItemContainerStyle="{StaticResource ItemContStyle}">
            <ListView.Resources>
                <ResourceDictionary>
                    <Style x:Key="hiddenStyle" TargetType="GridViewColumnHeader">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </Style>
                </ResourceDictionary>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Cover" Width="auto" HeaderContainerStyle="{StaticResource hiddenStyle}">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Image Source="{Binding Path=Cover}" Height="50" Margin="-6,0,0,0" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>


                    <GridViewColumn Header="Title" Width="200" HeaderContainerStyle="{StaticResource hiddenStyle}">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=Name}" FontWeight="Bold"></TextBlock>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>

                    <GridViewColumn Header="Year" Width="100" HeaderContainerStyle="{StaticResource hiddenStyle}">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=DisplayYear}"></TextBlock>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>

                    <GridViewColumn Header="Button" Width="135" HeaderContainerStyle="{StaticResource hiddenStyle}">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Button Content="Details" Width="100" Height="20" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>

                </GridView>
            </ListView.View>
        </ListView>
    </Grid>

</UserControl>
4

3 回答 3

1

在您希望保持响应的应用程序中的任何“繁重”工作中的多线程方法是正确的思考方式,因此您走在正确的轨道上。

然而,当您在这里创建和使用其他线程时,您仍然过于依赖 Dispatcher。考虑一下,这里使用多线程,您的过程应该如下所示:

  1. 在单独的线程上进行繁重的工作。
  2. 完成后,要求 Dispatcher 根据需要更新 UI。

这可以减轻 Dispatcher 上的负载。

您是否考虑过使用任务?从“干净代码”的角度来看,它们很棒,但在这里也适用,因为使用任务继续,一旦繁重的工作在其线程上完成,您可以将任务链接在一起以调用 UI 上的相关代码。

看看这里的答案是一个好的开始。

如果您之后需要它,我会很乐意提供更详细的示例。

编辑:正如另一个答案中提到的,BackgroundWorker 在这里同样有效......从线程的角度来看,最终结果完全相同。我只是喜欢任务语法!

编辑:只是想我会提供一些代码。为了简单起见,我暂时避免继续。考虑以下可以完成繁重工作的方法:

    public void HeavyLifting(Action<List<Items>> callback)
    {
        Task<List<Items>> task = Task.Factory.StartNew(
            () =>
                {
                    var myResults = new List<Items>();

                    // do the heavy stuff.

                    return myResults;
                });

        callback.Invoke(task.Result);
    }

然后对于您的 UI(例如在您的 ViewModel 中),您可以调用和处理回调。需要时,调用“繁重”并传入您的回调:

HeavyLifting(this.HandleHeavyLiftingCompleted);

然后,您传递的方法是在任务完成时执行回调。请注意,这是我要求 Dispatcher 完成工作的地方:

private void HandleHeavyLiftingCompleted(List<Items> results)
{
    this._uiDispatcher.BeginInvoke(
        new Action(() => { this.MyItems = new ObservableCollection<Items>(results); }));
}

请注意,在这种情况下,所涉及的 UI 工作是更新我从视图绑定到的 ObvservableCollection。对于这里的示例,我使用了一个随机的“项目”对象,它可以是你喜欢的任何东西!

我正在使用 Cinch,因此依靠服务来获取相关的 Dispatcher(您在这里看到的 this._uiDispatcher)。在您的情况下,您可以使用此处其他问题中提到的方法获得对它的参考。

此外,如果您有时间阅读,这里有一些关于 WPF 线程模型的重要信息

于 2012-11-05T10:54:13.230 回答
0

您的方法很危险,在很短的时间内将大量作业推送到调度程序上可能会使您的应用程序停止或冻结。虽然您的一般方法很好,但您可能需要考虑使用批量添加元素到您的列表中。

您也不能使用Dispatcher.CurrentDispatcher因为您现在正在使用当前线程的调度程序。因此,您要求您的线程处理同一线程中的添加,而不是 ui 线程。您需要从 ui 线程中获取调度程序。例如,您可以使用 Application 对象。

根据我的经验,我还建议您使用BackgroundWorker,它在 WPF 中比普通线程更好。

于 2012-11-05T10:39:59.353 回答
0

你可以简单地做:

Task.Factory.StartNew(() => 
{
    var theTvdb = new TheTVDB();
    var dexterSeries = theTvdb.SearchSeries("Dexter");
    Application.Current.Dispatcher.Invoke(new Action(() => 
    {    
        foreach (var tvSeries in dexterSeries)
        {
            this.Overview.Add(tvSeries);
        }
    }));
});
于 2012-11-05T12:46:12.070 回答