wpf - 将 Image 列添加到 WPF 数据网格并根据另一列中的前两个字符进行填充

标签 wpf image datagrid

我对 wpf 数据网格的工作方式有点陌生,我有一个项目,给定机场 IATA 代码,该项目将在 wpf 数据网格中显示该机场当前的到达和出发航类。

航类数据取自网站www.avionio.com ,并且由于该网站是多语言的,因此列标题将根据所选语言而变化。例如,以下是飞往伦敦希思罗机场的抵达航类的英文版本:

enter image description here

这是德语版本:

enter image description here

由于列标题是基于所选语言的动态列标题,因此在最终将数据网格源设置为数据表之前,它们由“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 简化了任务。所以我建议你检查一下你的程序,因为它真的不可能使用它,因为真的很复杂......

  1. 添加nuget包HtmlAgility Pack(简化html文件中标签的搜索)

  2. 在 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));
    }
}

结果:

Resultat


所以研究我的程序以利用 xaml 和绑定(bind)的力量

关于wpf - 将 Image 列添加到 WPF 数据网格并根据另一列中的前两个字符进行填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75923902/

相关文章:

wpf - 是否可以从 VisualBrush 在 WPF 中创建游标?

java - 使用APACHE POI转换PPT转图片时汉字转成方 block

javascript - 如何在 impress.js 中用图像填充屏幕?

node.js - 在 React 中使用 index.js 导入多个图片资源

wpf - 为什么我无法设置 DataGridTextColumn 的样式?

c# - 我们可以在 WPF 中的 DataGrid 单元格内有一个网格吗?

WPF DataGrid 样式中的标题内容每个触发器仅显示一次

wpf - MVVM 消息传递或事件或其他选项?

c# - 以 WPF 形式显示 EmguCV 图像

WPF:ScrollViewer 和 Grid - 禁用 TexBox 调整大小