绑定到 ObsevableCollection 在 WINUI3 应用程序中不起作用

Bind to ObsevableCollection not working in WINUI3 app

我正在构建一个半自动管理平台来跟踪我的日内交易。 使用 python 脚本,我可以从平台获取信息,并通过管道将信息传递给应用程序,应用程序将其传递到 SQLite 数据库。

现在我正在努力将数据从数据库中取出并放入应用程序的 GUI 中。现在,作为管道服务器的 Task 线程上的所有 3 个方法 运行 都在使用无限 while 循环以及从数据库获取数据的方法。由于数据每 2 秒更改一次,因此它也必须更新 GUI 中的数据。

从 xaml 到模型的绑定不起作用,因为该方法 运行 在不同的线程上运行。这就是我卡住的地方。

代码如下,

页面的第一个方法是 ObservableCollection 和启动 Task 方法的初始化 Task.Run(); 以及 PropertyChanged 方法。

        private ObservableCollection<OpenTradesModel> _trades = new ObservableCollection<OpenTradesModel>();
        public ObservableCollection<OpenTradesModel> Trades
        {
            get { return _trades; }
            set { _trades = value; OnPropertyChanged(nameof(Trades)); Debug.Print("Property Changed!"); }
        }
        
        public OpenTradesView()
        {
            InitializeComponent();
            List<Task> tasks = new()
            {
                Task.Run(() => { startServerAndUseData(); }),
                Task.Run(() => { runScript(); }),
                Task.Run(() => { DataBaseQueryToGUI(); }),
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

下面的方法从SQLite数据库中获取数据并放入_trades.

public Task DataBaseQueryToGUI()
        {
            while (true)
            {
                try
                {
                    using var db = new DatabaseContext();
                    var rowData = db.OpenTrades.OrderBy(x => x.Id).ToList();

                    foreach (var row in rowData)
                    {
                        var query = new OpenTradesModel()
                        {
                            Ticket = row.Ticket,
                            Time = row.Time,
                            Type = row.Type,
                            Magic = row.Magic,
                            Identifier = row.Identifier,
                            Reason = row.Reason,
                            Volume = row.Volume,
                            PriceOpen = row.PriceOpen,
                            StopLoss = row.StopLoss,
                            TakeProfit = row.TakeProfit,
                            CurrentPrice = row.CurrentPrice,
                            Swap = row.Swap,
                            Profit = row.Profit,
                            Symbol = row.Symbol,
                        };
                        _trades.Add(query); //This is where the exception is thrown.
                        Debug.Print("Added to Model!");
                    }
                }
                catch (Exception e)
                {
                    Debug.Print(e.ToString());
                    break;
                }
               Thread.Sleep(2000);
            }
            return Task.CompletedTask;
        }

xaml如下

<ListView x:Name="ListData" MaxWidth="1496" Width="1496" Height="457" MaxHeight="457"
                          ItemsSource="{x:Bind Trades, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

                    <ListViewHeaderItem Margin="-11,0,0,0">
                        ...
                    </ListViewHeaderItem>

                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <Expander>
                                <Expander.Header>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            ...
                                        </Grid.ColumnDefinitions>

                                        <TextBlock Grid.Column="0"  Text="{Binding Trades}"/>
                                        <TextBlock Grid.Column="1"  Text="{Binding Time}" Margin="-17,0,0,0"/>
                                        <TextBlock Grid.Column="2"  Text="{Binding Type}"/>
                                        <TextBlock Grid.Column="3"  Text="{Binding Magic}"/>
                                        <TextBlock Grid.Column="4"  Text="{Binding Identifier}"/>
                                        <TextBlock Grid.Column="5"  Text="{Binding Reason}"/>
                                        <TextBlock Grid.Column="6"  Text="{Binding Volume}"/>
                                        <TextBlock Grid.Column="7"  Text="{Binding PriceOpen}"/>
                                        <TextBlock Grid.Column="8"  Text="{Binding StopLoss}"/>
                                        <TextBlock Grid.Column="9"  Text="{Binding TakeProfit}"/>
                                        <TextBlock Grid.Column="10" Text="{Binding CurrentPrice}"/>
                                        <TextBlock Grid.Column="11" Text="{Binding Swap}" Margin="-15,0,0,0"/>
                                        <TextBlock Grid.Column="12" Text="{Binding Profit}" Margin="15,0,0,0"/>
                                        <TextBlock Grid.Column="13" Text="{Binding Symbol}"/>
                                    </Grid>
                                </Expander.Header>

绑定似乎有效,但未显示放入 _trades 的数据。由于以下异常显示它与线程有关,我只是不知道如何将数据获取到 UI 线程。我在这里找到的每个答案都不适合我。

System.Runtime.InteropServices.COMException (0x8001010E): The application called an interface that was marshalled for a different thread. (0x8001010E (RPC_E_WRONG_THREAD))
   at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|20_0(Int32 hr)
   at WinRT.ExceptionHelpers.ThrowExceptionForHR(Int32 hr)
   at ABI.Microsoft.UI.Xaml.Interop.WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory.CreateInstanceWithAllParameters(NotifyCollectionChangedAction action, IList newItems, IList oldItems, Int32 newIndex, Int32 oldIndex)
   at ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs.CreateMarshaler2(NotifyCollectionChangedEventArgs value)
   at ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.NativeDelegateWrapper.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
   at System.Collections.ObjectModel.Collection`1.Add(T item)
   at TradeAdministration.Views.OpenTradesView.DataBaseQueryToGUI() in C:\Users\Nick\source\repos\TradeAdministration\TradeAdministration\Views\OpenTradesView.xaml.cs:line 96

希望你们中的一位能让我走上正确的思路。提前致谢!

我通过如下更改 DataBaseQueryToGUI() 函数来修复它:

private async void DataBaseQueryToGUI() // Made it an async method.
        {
            await Task.Run(() => // Added
            { 
                while (true)
                {
                    try
                    {
                        using var db = new DatabaseContext();
                        var rowData = db.OpenTrades.OrderBy(x => x.Id).ToList();

                        foreach (var row in rowData)
                        {
                            var query = new OpenTradesModel()
                            {
                                Ticket = row.Ticket,
                                Time = row.Time,
                                Type = row.Type,
                                Magic = row.Magic,
                                Identifier = row.Identifier,
                                Reason = row.Reason,
                                Volume = row.Volume,
                                PriceOpen = row.PriceOpen,
                                StopLoss = row.StopLoss,
                                TakeProfit = row.TakeProfit,
                                CurrentPrice = row.CurrentPrice,
                                Swap = row.Swap,
                                Profit = row.Profit,
                                Symbol = row.Symbol,
                            };
                            this.DispatcherQueue.TryEnqueue(() => // Pushed these to the Dispatcher.
                            {
                                _trades.Add(query);
                                Debug.Print("Added to Model!");
                            });
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.Print(e.ToString());
                        break;
                    }
                    Thread.Sleep(2000);
                }
            });
        }

目前有效。虽然我丢失了一些数据,但这是可以修复的。现在我需要找出一种更新数据而不是添加新数据的方法。