将图像列添加到 WPF 数据表格并根据另一列中的前两个字符填充。

huangapple go评论52阅读模式
英文:

Add an Image column to a WPF datagrid and populate according to the first two characters in another column

问题

我对WPF数据网格的工作方式还不太熟悉,我有一个项目,根据机场IATA代码,将显示该机场当前到达和出发的航班在WPF数据网格中。

航班数据来自网站www.avionio.com,因为这个网站是多语言的,列标题会根据选择的语言而变化。例如,下面是伦敦希思罗机场到达航班的英文版本:

将图像列添加到 WPF 数据表格并根据另一列中的前两个字符填充。

这是德文版本:

将图像列添加到 WPF 数据表格并根据另一列中的前两个字符填充。

因为列标题根据选择的语言是动态的,所以它们是通过DataTable.Columns.Add方法填充的,行是通过DataRow方法添加到数据表中,最后将数据网格源设置为数据表。

这一切都运行良好,数据网格根据选择的语言正确更新。

现在我想做的是,不是让“航空公司”列显示航空公司名称作为文本,而是显示航空公司徽标,可以从以下网站获取:https://pics.avs.io/71/25。该网站使用航班号的前两个字符来引用正确的航空公司徽标,所以对于数据网格中的第一个航班(汉莎航空),完整的URL将是https://pics.avs.io/71/25/LH.png。

我的问题是,如何根据相应“航班”列中的前两个字符替换数据网格上的“航空公司”列,以显示相应的航空公司徽标。

我使用的语言是C#,以下是我已有的用于填充数据网格的现有代码:

使用网页https://www.avionio.com/widget/en/LHR/arrivals(显示当前伦敦希思罗机场到达信息的英文页面)。首先,通过以下代码获取网站背后的HTML代码:

public static string MakeRequest(string URL)
{
    // ...
}

然后,通过以下代码从此代码中提取列标题(因为它们根据选择的语言而变化):

int startPos = 0;
int endPos = 0;
List<string> colHeaderText = new List<string>();
// ...

并通过以下代码添加到DataTable:

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数据网格不熟悉,我刚刚成功地用文本值填充了数据网格,因此不知道从图像列开始要从哪里开始。

感谢您提前的任何帮助。

英文:

I am a bit new to how wpf datagrids work, and I have a project which, given an airport IATA code will display the current arrival and departure flights for that airport in a wpf datagrid.

The flight data is taken from the website www.avionio.com, and as this website is multi-lingual, the column headers will change dependent on the language selected. For example, below is the English version of the arrival flights into London Heathrow:

将图像列添加到 WPF 数据表格并根据另一列中的前两个字符填充。

and this is the German version:

将图像列添加到 WPF 数据表格并根据另一列中的前两个字符填充。

Because the column headers are dynamic based on the language selected, they are populated by the 'DataTable.Columns.Add' method and the rows being added to the datatable via the DataRow method, before finally setting the datagrid source to the datable.

This all works fine and the datagrid is updated correctly depending on which language is selected.

What I now want to do is instead of the 'Airline' column displaying the airline name as text, I would like it to show the airline logo, which can be taken from the following site https://pics.avs.io/71/25. This site uses the first two characters of the flight number to reference the correct airline logo, so for the first flight in the datagrid above (Lufthansa) the full URL would be https://pics.avs.io/71/25/LH.png.

My question is how do I replace the Airline column on the datagrid with the relevant airline logo depending on the first two characters in the corresponding 'Flight' column.

The language I am using is C# and below is the existing code I have that populates the datagrid:

Using the webpage https://www.avionio.com/widget/en/LHR/arrivals (this displays the current London Heathrow arrivals in English. I firstly get the HTML code behind the website via this code:

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 &amp;&amp; ex.Response != null)
                {
                    var resp = (HttpWebResponse)ex.Response;
                    if (resp.StatusCode == HttpStatusCode.NotFound)
                    {
                        MessageBox.Show(&quot;Details unavailable or URL airport name incorrect&quot;, &quot;Cannot Retrieve Details&quot;, MessageBoxButton.OK, MessageBoxImage.Error);
                        return &quot;N/A&quot;;
                    }
                }
            }
            return &quot;&quot;;
        }

I then pick out the column headings from this code (as they vary by language chosen) via:

int startPos = 0;
int endPos = 0;

List&lt;string&gt; colHeaderText = new List&lt;string&gt;();
startPos = arrivalsDoc.IndexOf(&quot;&lt;table class=\&quot;timetable\&quot;&gt;&quot;);
string colHeaders = arrivalsDoc.Substring(startPos, arrivalsDoc.Length - startPos);
startPos = colHeaders.IndexOf(&quot;&lt;th&gt;&quot;);
colHeaders = colHeaders.Substring(startPos, colHeaders.Length - startPos);
endPos = colHeaders.IndexOf(&quot;&lt;/tr&gt;&quot;);
colHeaders = colHeaders.Substring(0, endPos);
string delimiter = &quot;&lt;/th&gt;&quot;;
string[] colHeadersSplit = colHeaders.Split(new[] { delimiter }, StringSplitOptions.None);
colHeaderText.Clear();
foreach (var item in colHeadersSplit)
{
    if (item != &quot;&quot;)
    {
         string headerText = item.Replace(&quot;&lt;th&gt;&quot;, &quot;&quot;).Replace(&quot;&lt;/th&gt;&quot;, &quot;&quot;).Replace(&quot;\r&quot;,&quot;&quot;).Replace(&quot;\n&quot;,&quot;&quot;).Trim();
         if (headerText != &quot;&quot;)
         {
             colHeaderText.Add(headerText);
         }
     }
 }

And add to a DataTable via:

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

I then extract each row of data from the HTML code into separate variables in a similar way to the above and populate a new row of the datatable via

DataRow drArrivalFlight = dtArrivals.NewRow();
drArrivalFlight[0] = scheduled + &quot; &quot; + flightDate;
drArrivalFlight[1] = airline;
drArrivalFlight[2] = flightNo;
drArrivalFlight[3] = origin;
drArrivalFlight[4] = status;
dtArrivals.Rows.Add(drArrivalFlight);

I finally populate the datagrid via:

dgCurrentArrivals.ItemsSource = dtArrivals.DefaultView;

I haven't tried anything yet as I am not familiar with wpf datagrids, and have only just successfully populated the datagrid with text values, so wouldn't know where to start with an image column.

Thanks for any help in advance.

答案1

得分: 2

这是简化程序并展示如何将图像绑定到数据网格的解决方案。很难通过编程实现,XAML的使用简化了任务。建议您审查程序,因为它真的很难使用,非常复杂。

  1. 添加NuGet包HtmlAgility Pack(简化HTML文件中标记的搜索)

  2. 在XAML视图中声明DataGrid。

MainWindow.xaml(主视图)

<Window x:Class="TestDatagrid.MainWindow" Name="Root"
        ...
        xmlns:local="clr-namespace:TestDatagrid"
        ...>
    ...
    <DataGrid HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0" AutoGenerateColumns="False"
              Loaded="DataGrid_Loaded">
        ...
    </DataGrid>
</Window>

MainWindow.xaml.cs

using HtmlAgilityPack;
using ...
namespace TestDatagrid
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        ...
        public List<Arrival> Arrivals = new List<Arrival>();
        ...
    }
    ...
}

Arrival.cs

namespace TestDatagrid
{
    public class Arrival
    {
        ...
    }
}

您需要为DataGrid创建一个绑定代理(请参见DataGrid的DataContext)。

BindingProxy.cs

using System.Windows;

namespace TestDatagrid
{
    public class BindingProxy : Freezable
    {
        ...
    }
}

结果:

将图像列添加到 WPF 数据表格并根据另一列中的前两个字符填充。

研究我的程序以充分利用XAML和绑定。


<details>
<summary>英文:</summary>
So this is a solution which simplify some parts of your program and show how to bind image to datagrid. Its no easy to do that by programmation, the use of xaml simplify the task. So i suggest you to review your program, because its really impossible to use it, because really complex...
1) add the nuget package HtmlAgility Pack (simplify the search of tag in html file)
2) declare your DataGrid in the Xaml view.
**MainWindow.xaml (Main view)**
&lt;Window x:Class=&quot;TestDatagrid.MainWindow&quot; Name=&quot;Root&quot;
xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
xmlns:local=&quot;clr-namespace:TestDatagrid&quot;
mc:Ignorable=&quot;d&quot;
Title=&quot;MainWindow&quot; Height=&quot;450&quot; Width=&quot;800&quot;&gt;
&lt;Grid&gt;
&lt;DataGrid HorizontalAlignment=&quot;Left&quot; VerticalAlignment=&quot;Top&quot; Margin=&quot;10,10,0,0&quot; AutoGenerateColumns=&quot;False&quot;
Loaded=&quot;DataGrid_Loaded&quot;&gt;
&lt;DataGrid.Resources&gt;
&lt;local:BindingProxy x:Key=&quot;proxy&quot; Data=&quot;{Binding ElementName=Root}&quot; /&gt;
&lt;/DataGrid.Resources&gt;
&lt;DataGrid.Columns&gt;
&lt;DataGridTemplateColumn Header=&quot;{Binding Data.Headers[0], Source={StaticResource proxy}}&quot; Width=&quot;SizeToCells&quot; IsReadOnly=&quot;True&quot;&gt;
&lt;DataGridTemplateColumn.CellTemplate&gt;
&lt;DataTemplate&gt;
&lt;StackPanel Orientation=&quot;Horizontal&quot; &gt;
&lt;TextBlock Text=&quot;{Binding Scheduled}&quot; Margin=&quot;0,0,5,0&quot;/&gt;
&lt;TextBlock Text=&quot;{Binding Date}&quot;/&gt;
&lt;/StackPanel&gt;
&lt;/DataTemplate&gt;
&lt;/DataGridTemplateColumn.CellTemplate&gt;
&lt;/DataGridTemplateColumn&gt;
&lt;DataGridTemplateColumn Header=&quot;{Binding Data.Headers[4], Source={StaticResource proxy}}&quot; Width=&quot;SizeToCells&quot; IsReadOnly=&quot;True&quot;&gt;
&lt;DataGridTemplateColumn.CellTemplate&gt;
&lt;DataTemplate&gt;
&lt;Image Source=&quot;{Binding Logo}&quot; /&gt;
&lt;/DataTemplate&gt;
&lt;/DataGridTemplateColumn.CellTemplate&gt;
&lt;/DataGridTemplateColumn&gt;
&lt;DataGridTextColumn Header=&quot;{Binding Data.Headers[5], Source={StaticResource proxy}}&quot;
Binding=&quot;{Binding Flight}&quot;/&gt;
&lt;DataGridTextColumn Header=&quot;{Binding Data.Headers[3], Source={StaticResource proxy}}&quot;
Binding=&quot;{Binding From}&quot;/&gt;
&lt;DataGridTextColumn Header=&quot;{Binding Data.Headers[2], Source={StaticResource proxy}}&quot;
Binding=&quot;{Binding Status}&quot;/&gt;
&lt;/DataGrid.Columns&gt;
&lt;/DataGrid&gt;
&lt;/Grid&gt;
&lt;/Window&gt;
___
**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&lt;Arrival&gt; Arrivals = new List&lt;Arrival&gt;();
public event PropertyChangedEventHandler? PropertyChanged;
public List&lt;string&gt; Headers { get; set; }
public MainWindow()
{
var html = MakeRequest(&quot;https://www.avionio.com/widget/en/lhr/arrivals&quot;);
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
//get headers from html file 
Headers = doc.DocumentNode.SelectNodes(&quot;//table/thead/tr/th&quot;).Select(x =&gt; x.InnerText).ToList();
//get datas from TimeTables (get tag tr with 2 classes, one class is timetable-record)
var TimeTables = doc.DocumentNode.SelectNodes(&quot;//table/tbody/tr[contains(@class,&#39;timetable-record&#39;)]&quot;)
.Where(x =&gt; x.GetAttributeValue(&quot;class&quot;, &quot;&quot;).Split(&#39; &#39;).Count() == 2)
.Select(x =&gt; 
{
var dt = x.SelectNodes(&quot;.//td&quot;).Select(a =&gt;
{
if (a.GetAttributeValue(&quot;class&quot;, &quot;&quot;).Equals(&quot;timetable-flight&quot;))
{
var y = a.SelectSingleNode(&quot;.//a&quot;);
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(@&quot;D:\files&quot;).Select(x =&gt; x.Split(&#39;\\&#39;).Last()).ToList();
var flags = TimeTables.Select(x =&gt; $&quot;{x.Last()[0..2]}.png&quot;);
var newFlags = flags.Except(files).ToList();        
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//download new flags and add to files list
var folderTosaveLogos = @&quot;D:\files\&quot;;
using (var client = new WebClient())
{
foreach (var flag in newFlags)
{
client.DownloadFile(new Uri($&quot;https://pics.avs.io/71/25/{flag}&quot;), $&quot;{folderTosaveLogos}{flag}&quot;);
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 = @$&quot;D:\files\{f.Flight[0..2]}.png&quot;;
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 &amp;&amp; ex.Response != null)
{
var resp = (HttpWebResponse)ex.Response;
if (resp.StatusCode == HttpStatusCode.NotFound)
{
MessageBox.Show(&quot;Details unavailable or URL airport name incorrect&quot;, &quot;Cannot Retrieve Details&quot;, MessageBoxButton.OK, MessageBoxImage.Error);
return &quot;N/A&quot;;
}
}
}
return &quot;&quot;;
}
}
}
___
**the Class 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; }
}
}
___
you have to create a binding Proxy for Datagrid (see [DataContext for Datagrid][1]
**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(&quot;Data&quot;, typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
}
___
**Result:**
[![Resultat][2]][2]
___
So study my program to use the power of xaml and binding
[1]: https://thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
[2]: https://i.stack.imgur.com/xTYj1.png
</details>
# 答案2
**得分**: 1
你可以通过处理 `DataGrid.AutoGeneratingColumns` 事件来修改自动生成的列。监听此事件可以让你重新排序、过滤或替换列。
以下示例允许只显式定义 logo 列,从而创建一个动态的 `DataGrid`。
用于显示标志的 `Image` 控件将自动从由单元格值提供的 URL 下载图像。
重要的是要注意,`WebRequest` 类及其关联的类已被弃用。Microsoft 声明我们不应再使用它们,而是应使用 `HttpClient` 类(`WebClient` 也已被弃用)。
1) 修复与 HTTP 请求相关的代码。使用推荐的 `HttpClient` 允许使用异步 API,这将提高性能:
```c#
private async Task RunCodeAsync()
{
string arrivalsDoc = await MakeRequestAsync("https://www.avionio.com/widget/en/LHR/arrivals");
// TODO::解析结果并创建 DataTable
}
public async Task<string> MakeRequestAsync(string url)
{
using var httpClient = new HttpClient();
using HttpResponseMessage response = await httpClient.GetAsync(url);
try
{
// 如果状态代码不在 200-299 范围内,将引发异常。
// 如果异常在本地处理,则为了进一步提高性能,应避免异常,
// 而是替换以下行为条件检查的 `HttpResponseMessage.IsSuccessStatusCode` 属性。
_ = response.EnsureSuccessStatusCode();
}
catch (HttpRequestException)
{
if (response.StatusCode == HttpStatusCode.NotFound)
{
_ = MessageBox.Show("详情不可用或URL机场名称不正确", "无法检索详情", MessageBoxButton.OK, MessageBoxImage.Error);
return "N/A";
}
return "";
}
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
  1. 你应该向你的 DataTable 添加一个额外的列 AirlineLogo,其中包含图标的路径:
DataTable dtArrivals = new DataTable();
dtArrivals.Columns.Add(colHeaderText[0], typeof(string));     
dtArrivals.Columns Add(colHeaderText[4], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[5], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[3], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[2], typeof(string));     

// 追加 logo 列。
// 列名稍后将替换为 "Airline" 列名。
dtArrivals.Columns.Add("AirlineLogo", typeof(string)); 
  1. 然后将每行映射到相应的航空公司标志资源。该路径可以是指向Web资源的原始URL,例如 "https://pics.avs.io/71/25/LH.png"
DataRow drArrivalFlight = dtArrivals.NewRow();
drArrivalFlight[0] = scheduled + " " + flightDate;
drArrivalFlight[1] = airline;
drArrivalFlight[2] = flightNo;
drArrivalFlight[3] = origin;
drArrivalFlight[4] = status;

// 使用航班ID的前两个字母构建标志的URL
drArrivalFlight["AirlineLogo"] = $"https://pics.avs.io/71/25/{flightNo.Substring(0, 2)}.png";

dtArrivals.Rows.Add(drArrivalFlight);
  1. 为替换的列定义模板,以便从URL获取图像并显示它(使用 Image 控件):
<Window>
<DataGrid x:Name="dgCurrentArrivals" 
AutoGenerateColumns="True"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn">
<DataGrid.Resources>
<DataGridTemplateColumn x:Key="AirlineLogoColumn">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<!-- 将图像绑定到 DataTable 的 'AirlineLogo' 列的 URL 值 -->
<Image Source="{Binding [AirlineLogo]}"
Height="24" />
<!-- 绑定到航空公司名称列 -->
<TextBlock Text="{Binding [1]}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Resources>
</DataGrid>
</Window>
  1. 使用 DataGrid.AutoGeneratingColumn 事件隐藏/替换 Airline 列:
private const int ReplacedColumnIndex = 1;
private string ReplacedColumnName { get; set; }
private int CurrentColumnIndex { get; set; }

private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
  if (this.CurrentColumnIndex == ReplacedColumnIndex)
  {
    // 存储实际语言的列名
    this.ReplacedColumnName = e.PropertyName;

    // 隐藏列
    e.Cancel = true;
  }
  else if (e.PropertyName == "AirlineLogo")
  {
    var dataGrid = sender as DataGrid;
    var dataGridImageColumn = dataGrid.Resources["AirlineLogoColumn"] as DataGridColumn;
    
    // 重新排序替换的列
    dataGridImageColumn.DisplayIndex = ReplacedColumnIndex;

    // 使用当前表语言的名称重命名替换的列
    dataGridImageColumn.Header = this.ReplacedColumnName;

    // 用我们的自定义图像列替换自动生成的文本列
    e.Column = dataGridImageColumn;
  }

  this.CurrentColumnIndex++;
}
英文:

You can modify autogenerated columns by handling the DataGrid.AutoGeneratingColumns event. Listening to this event allows you to reorder or to filter/replace columns.

The following example allows to have a dynamic DataGrid by only defining the logo column explicitly.
The Image control which is used to display the logos will automatically download the images from an URL which is provided by the cell value.

It's important to note that the WebRequest class and all its associated classes are deprecated. Microsoft states that we should no longer use them. Instead, we must use the HttpClient class (WebClient is also deprecated).

  1. Fix the HTTP request related code. Using the recommended HttpClient allows to use async APIs, which will improve the performance:
private async Task RunCodeAsync()
{
  string arrivalsDoc = await MakeRequestAsync(&quot;https://www.avionio.com/widget/en/LHR/arrivals&quot;);

  // TODO::Parse result and create DataTable
}

public async Task&lt;string&gt; MakeRequestAsync(string url)
{
  using var httpClient = new HttpClient();
  using HttpResponseMessage response = await httpClient.GetAsync(url);
  try
  {
    // Will throw if the status code is not in the range 200-299.
    // In case the exception is handled locally, 
    // to further improve performance you should avoid the exception
    // and instead replace the following line with a conditional check 
    // of the &#39;HttpResponseMessage.IsSuccessStatusCode&#39; property.
    _ = response.EnsureSuccessStatusCode();
  }
  catch (HttpRequestException)
  {
    if (response.StatusCode == HttpStatusCode.NotFound)
    {
      _ = MessageBox.Show(&quot;Details unavailable or URL airport name incorrect&quot;, &quot;Cannot Retrieve Details&quot;, MessageBoxButton.OK, MessageBoxImage.Error);
      return &quot;N/A&quot;;
    }

    return &quot;&quot;;
  }

  string responseBody = await response.Content.ReadAsStringAsync();
  return responseBody;
}
  1. You should add an extra column AirlineLogo to your DataTable that contains the path to the icon:
DataTable dtArrivals = new DataTable();
dtArrivals.Columns.Add(colHeaderText[0], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[4], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[5], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[3], typeof(string));     
dtArrivals.Columns.Add(colHeaderText[2], typeof(string));     

// Append the logo column. 
// The column name will be replaced with the &quot;Airline&quot; column name later.
dtArrivals.Columns.Add(&quot;AirlineLogo&quot;, typeof(string)); 
  1. Then map each row to the corresponding airline logo resource. This path can be a the original URL that points to a web resource, for example &quot;https://pics.avs.io/71/25/LH.png&quot;:
DataRow drArrivalFlight = dtArrivals.NewRow();
drArrivalFlight[0] = scheduled + &quot; &quot; + flightDate;
drArrivalFlight[1] = airline;
drArrivalFlight[2] = flightNo;
drArrivalFlight[3] = origin;
drArrivalFlight[4] = status;

// Compose the URL to the logo using the first two letters of the flight ID
drArrivalFlight[&quot;AirlineLogo&quot;] = $&quot;https://pics.avs.io/71/25/{flightNo[0..2]}.png&quot;;

dtArrivals.Rows.Add(drArrivalFlight);
  1. Define the template for the replaced column so that it will fetch the image from the URL to display it (by using the Image control):
&lt;Window&gt;
&lt;DataGrid x:Name=&quot;dgCurrentArrivals&quot; 
AutoGenerateColumns=&quot;True&quot;
AutoGeneratingColumn=&quot;DataGrid_AutoGeneratingColumn&quot;&gt;
&lt;DataGrid.Resources&gt;
&lt;DataGridTemplateColumn x:Key=&quot;AirlineLogoColumn&quot;&gt;
&lt;DataGridTemplateColumn.CellTemplate&gt;
&lt;DataTemplate&gt;
&lt;StackPanel&gt;
&lt;!-- Bind the Image to the URL value of the &#39;AirlineLogo&#39; column of the DataTable --&gt;
&lt;Image Source=&quot;{Binding [AirlineLogo]}&quot;
Height=&quot;24&quot; /&gt;
&lt;!-- Bind to airline name column --&gt;
&lt;TextBlock Text=&quot;{Binding [1]}&quot; /&gt;
&lt;/StackPanel&gt;
&lt;/DataTemplate&gt;
&lt;/DataGridTemplateColumn.CellTemplate&gt;
&lt;/DataGridTemplateColumn&gt;
&lt;/DataGrid.Resources&gt;
&lt;/DataGrid&gt;
&lt;/Window&gt;
  1. Hide/replace the Airline column using the DataGrid.AutoGeneratingColumn event:
private const int ReplacedColumnIndex = 1;
private string ReplacedColumnName { get; set; }
private int CurrentColumnIndex { get; set; }

private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
  if (this.CurrentColumnIndex == ReplacedColumnIndex)
  {
    // Store the column name of the actual language
    this.ReplacedColumnName = e.PropertyName;

    // Hide the column
    e.Cancel = true;
  }
  else if (e.PropertyName == &quot;AirlineLogo&quot;)
  {
    var dataGrid = sender as DataGrid;
    var dataGridImageColumn = dataGrid.Resources[&quot;AirlineLogoColumn&quot;] as DataGridColumn;
    
    // Reorder replaced column
    dataGridImageColumn.DisplayIndex = ReplacedColumnIndex;

    // Rename replaced column with the name of the current table language
    dataGridImageColumn.Header = this.ReplacedColumnName;

    // Replace the auto generated text column with our custom image column
    e.Column = dataGridImageColumn;
  }

  this.CurrentColumnIndex++;
}

huangapple
  • 本文由 发表于 2023年4月4日 05:27:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75923902.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定