绑定到 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);
}
});
}
目前有效。虽然我丢失了一些数据,但这是可以修复的。现在我需要找出一种更新数据而不是添加新数据的方法。
我正在构建一个半自动管理平台来跟踪我的日内交易。
使用 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);
}
});
}
目前有效。虽然我丢失了一些数据,但这是可以修复的。现在我需要找出一种更新数据而不是添加新数据的方法。