我对 wpf 数据网格的工作方式有点陌生,我有一个项目,给定机场 IATA 代码,该项目将在 wpf 数据网格中显示该机场当前的到达和出发航类。
航类数据取自网站www.avionio.com ,并且由于该网站是多语言的,因此列标题将根据所选语言而变化。例如,以下是飞往伦敦希思罗机场的抵达航类的英文版本:
这是德语版本:
由于列标题是基于所选语言的动态列标题,因此在最终将数据网格源设置为数据表之前,它们由“DataTable.Columns.Add”方法填充,并通过 DataRow 方法将行添加到数据表中.
这一切都工作正常,并且数据网格会根据选择的语言正确更新。
我现在想要做的不是“航空公司”列以文本形式显示航空公司名称,而是希望它显示航空公司 Logo ,该 Logo 可以从以下网站 https://pics.avs.io/71/25 获取。该网站使用航类号的前两个字符来引用正确的航空公司 Logo ,因此对于上面数据网格中的第一个航类(汉莎航空),完整 URL 将为 https://pics.avs.io/71/25/LH.png .
我的问题是如何根据相应“航类”列中的前两个字符将数据网格上的航空公司列替换为相关航空公司 Logo 。
我使用的语言是 C#,下面是我填充数据网格的现有代码:
使用网页https://www.avionio.com/widget/en/LHR/arrivals (这会以英语显示当前伦敦希思罗机场到达情况。我首先通过以下代码获取网站背后的 HTML 代码:
public static string MakeRequest(string URL)
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
try
{
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
try
{
Stream response = resp.GetResponseStream();
StreamReader readStream = new StreamReader(response, Encoding.UTF8);
return readStream.ReadToEnd();
}
finally
{
resp.Close();
}
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null)
{
var resp = (HttpWebResponse)ex.Response;
if (resp.StatusCode == HttpStatusCode.NotFound)
{
MessageBox.Show("Details unavailable or URL airport name incorrect", "Cannot Retrieve Details", MessageBoxButton.OK, MessageBoxImage.Error);
return "N/A";
}
}
}
return "";
}
然后,我通过以下方式从此代码中挑选列标题(因为它们因所选语言而异):
int startPos = 0;
int endPos = 0;
List<string> colHeaderText = new List<string>();
startPos = arrivalsDoc.IndexOf("<table class=\"timetable\">");
string colHeaders = arrivalsDoc.Substring(startPos, arrivalsDoc.Length - startPos);
startPos = colHeaders.IndexOf("<th>");
colHeaders = colHeaders.Substring(startPos, colHeaders.Length - startPos);
endPos = colHeaders.IndexOf("</tr>");
colHeaders = colHeaders.Substring(0, endPos);
string delimiter = "</th>";
string[] colHeadersSplit = colHeaders.Split(new[] { delimiter }, StringSplitOptions.None);
colHeaderText.Clear();
foreach (var item in colHeadersSplit)
{
if (item != "")
{
string headerText = item.Replace("<th>", "").Replace("</th>", "").Replace("\r","").Replace("\n","").Trim();
if (headerText != "")
{
colHeaderText.Add(headerText);
}
}
}
并通过以下方式添加到数据表:
DataTable dtArrivals = new DataTable();
dtArrivals.Columns.Add(colHeaderText[0], typeof(string)); //Scheduled
dtArrivals.Columns.Add(colHeaderText[4].ToString(), typeof(string)); //Airline
dtArrivals.Columns.Add(colHeaderText[5].ToString(), typeof(string)); //Flight No
dtArrivals.Columns.Add(colHeaderText[3].ToString(), typeof(string)); //Origin
dtArrivals.Columns.Add(colHeaderText[2].ToString(), typeof(string)); //Status
然后,我以与上述类似的方式将 HTML 代码中的每一行数据提取到单独的变量中,并通过以下方式填充数据表的新行
DataRow drArrivalFlight = dtArrivals.NewRow();
drArrivalFlight[0] = scheduled + " " + flightDate;
drArrivalFlight[1] = airline;
drArrivalFlight[2] = flightNo;
drArrivalFlight[3] = origin;
drArrivalFlight[4] = status;
dtArrivals.Rows.Add(drArrivalFlight);
我最终通过以下方式填充数据网格:
dgCurrentArrivals.ItemsSource = dtArrivals.DefaultView;
我还没有尝试过任何东西,因为我不熟悉 wpf 数据网格,并且刚刚成功地用文本值填充了数据网格,所以不知道从哪里开始使用图像列。
感谢您提前提供的任何帮助。
最佳答案
所以这是一个简化程序的某些部分并展示如何将图像绑定(bind)到数据网格的解决方案。通过编程来做到这一点并不容易,使用 xaml 简化了任务。所以我建议你检查一下你的程序,因为它真的不可能使用它,因为真的很复杂......
添加nuget包HtmlAgility Pack(简化html文件中标签的搜索)
在 Xaml View 中声明您的 DataGrid。
MainWindow.xaml(主视图)
<Window x:Class="TestDatagrid.MainWindow" Name="Root"
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:local="clr-namespace:TestDatagrid"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DataGrid HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0" AutoGenerateColumns="False"
Loaded="DataGrid_Loaded">
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding ElementName=Root}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="{Binding Data.Headers[0], Source={StaticResource proxy}}" Width="SizeToCells" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Scheduled}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Date}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="{Binding Data.Headers[4], Source={StaticResource proxy}}" Width="SizeToCells" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Logo}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{Binding Data.Headers[5], Source={StaticResource proxy}}"
Binding="{Binding Flight}"/>
<DataGridTextColumn Header="{Binding Data.Headers[3], Source={StaticResource proxy}}"
Binding="{Binding From}"/>
<DataGridTextColumn Header="{Binding Data.Headers[2], Source={StaticResource proxy}}"
Binding="{Binding Status}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
MainWindow.xaml.cs
using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace TestDatagrid
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public List<Arrival> Arrivals = new List<Arrival>();
public event PropertyChangedEventHandler? PropertyChanged;
public List<string> Headers { get; set; }
public MainWindow()
{
var html = MakeRequest("https://www.avionio.com/widget/en/lhr/arrivals");
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
//get headers from html file
Headers = doc.DocumentNode.SelectNodes("//table/thead/tr/th").Select(x => x.InnerText).ToList();
//get datas from TimeTables (get tag tr with 2 classes, one class is timetable-record)
var TimeTables = doc.DocumentNode.SelectNodes("//table/tbody/tr[contains(@class,'timetable-record')]")
.Where(x => x.GetAttributeValue("class", "").Split(' ').Count() == 2)
.Select(x =>
{
var dt = x.SelectNodes(".//td").Select(a =>
{
if (a.GetAttributeValue("class", "").Equals("timetable-flight"))
{
var y = a.SelectSingleNode(".//a");
return y.InnerText;
}
return a.InnerText;
}).ToArray();
return dt;
}).ToArray();
#region Load new logo from internet and save in folder for future use
var files = Directory.GetFiles(@"D:\files").Select(x => x.Split('\\').Last()).ToList();
var flags = TimeTables.Select(x => $"{x.Last()[0..2]}.png");
var newFlags = flags.Except(files).ToList();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//download new flags and add to files list
var folderTosaveLogos = @"D:\files\";
using (var client = new WebClient())
{
foreach (var flag in newFlags)
{
client.DownloadFile(new Uri($"https://pics.avs.io/71/25/{flag}"), $"{folderTosaveLogos}{flag}");
files.Add(flag);
}
}
#endregion
#region Create the global list Arrivals)
foreach (var t in TimeTables)
{
var f = new Arrival();
f.Scheduled = t[0];
f.Date = t[1];
f.Status = t[2];
f.From = t[3];
f.Airline = t[4];
f.Flight = t[5];
f.Logo = @$"D:\files\{f.Flight[0..2]}.png";
Arrivals.Add(f);
}
#endregion
}
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
// ... Assign ItemsSource to Arrivals List.
var grid = sender as DataGrid;
grid.ItemsSource = Arrivals;
}
public static string MakeRequest(string URL)
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
try
{
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
try
{
Stream response = resp.GetResponseStream();
StreamReader readStream = new StreamReader(response, Encoding.UTF8);
return readStream.ReadToEnd();
}
finally
{
resp.Close();
}
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null)
{
var resp = (HttpWebResponse)ex.Response;
if (resp.StatusCode == HttpStatusCode.NotFound)
{
MessageBox.Show("Details unavailable or URL airport name incorrect", "Cannot Retrieve Details", MessageBoxButton.OK, MessageBoxImage.Error);
return "N/A";
}
}
}
return "";
}
}
}
类 Arrival.cs
namespace TestDatagrid
{
public class Arrival
{
public string Scheduled { get; set; }
public string Date { get; set; }
public string Status { get; set; }
public string From { get; set; }
public string Airline { get; set; }
public string Flight { get; set; }
public string Logo { get; set; }
}
}
您必须为 Datagrid 创建一个绑定(bind)代理(请参阅 DataContext for Datagrid
BindingProxy.cs
using System.Windows;
namespace TestDatagrid
{
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
}
结果:
所以研究我的程序以利用 xaml 和绑定(bind)的力量
关于wpf - 将 Image 列添加到 WPF 数据网格并根据另一列中的前两个字符进行填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75923902/