【问题标题】:How to bind a datagrid column to a combobox如何将数据网格列绑定到组合框
【发布时间】:2021-02-16 02:22:38
【问题描述】:

我正在尝试设置以下播放器集合,但在显示/更新数据集合时遇到了问题。 我有 2 个课程: CountryModel 和 PlayerModel CountryModel 类定义如下:

public static class CountryModel
    {
        public class Country
        {
            public string Name { get; set; }
            public string Abbr { get; set; }
            public byte[] Flag { get; set; }
        }

        public static async Task<ObservableCollection<Country>> GetCountriesAsync()
        {
            // Get all country names from system
            var ThreeLetterMapping = CultureInfo.GetCultures(CultureTypes.SpecificCultures)
                                        .Select(ci => new RegionInfo(ci.LCID))
                                        .GroupBy(ri => ri.EnglishName)
                                        .ToDictionary(g => g.Key, g => g.First().ThreeLetterISORegionName);
            // Create observablecollection to contain list of country names
            ObservableCollection<Country> getNames = new ObservableCollection<Country>();
            // Run task of getting every country files
            await Task.Run(() =>
            {
                // Get all files from Resources directory
                string[] Files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + @"\Resources\");
                foreach (string Name in Files)
                {
                    // Add only country name without extension if png format
                    if (Path.GetExtension(Name) == ".png" && !Name.Contains("BCA"))
                    {

                        byte[] image = System.IO.File.ReadAllBytes(Name);


                        // Country name without extension
                        string CountryName = Path.GetFileNameWithoutExtension(Name);
                        // Get abbreviation from above ThreeLetterMapping variable
                        string abbr = null;
                        try
                        {
                            // Country exists and mapping is possible
                            abbr = ThreeLetterMapping[CountryName];
                        }
                        catch
                        {
                            // Custom mapping to 3 letter using first 3 letters in country name
                            abbr = CountryName.Substring(0, 3);
                        }
                        // Add country with data to list
                        getNames.Add(new Country { Name = CountryName, Abbr = abbr, Flag = image });
                        //getNames.Add(new Country { Name = CountryName, Abbr = abbr, Flag = GetFlag(CountryName) });
                    }
                }
            });
            return getNames;
        }

        public static BitmapImage GetFlag(string CountryName)
        {
            try
            {
                BitmapImage getFlag = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + @"\Resources\" + CountryName + ".png"));
                //BitmapImage getFlag = new BitmapImage(new Uri("pack://application:,,,/Resources/" + CountryName + ".png"));
                return getFlag;
            }
            catch (Exception e)
            {
                MessageBox.Show("Error: " + e.Message, "Retrieve Flag", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
        }

从上面的代码可以看出,我可以成功地从上面的类中检索到一个List&lt;CountryModel&gt;

现在我的 MainWindow 看起来像这样:

文本框定义如下:

<TextBox x:Name="TextBoxId" Grid.Column="1" Grid.Row="0" Text="{Binding SelectedItem.Id, ElementName=DataGridPlayers}" IsEnabled="False" Height="26" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,12,0" IsTabStop="False"/>
<TextBox x:Name="TextBoxFullName" Grid.Column="1" Grid.Row="1" Text="{Binding SelectedItem.FullName, ElementName=DataGridPlayers}" IsEnabled="False" Height="26" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,12,0" IsTabStop="False"/>
<TextBox x:Name="TextBoxLastName" Grid.Column="1" Grid.Row="2" Text="{Binding SelectedItem.LastName, ElementName=DataGridPlayers}" Height="26" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,12,0" TabIndex="1"/>
<TextBox x:Name="TextBoxFirstName" Grid.Column="1" Grid.Row="3" Text="{Binding SelectedItem.FirstName, ElementName=DataGridPlayers}" Height="26" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,12,0" TabIndex="2"/>
<TextBox x:Name="TextBoxMiddleName" Grid.Column="1" Grid.Row="4" Text="{Binding SelectedItem.MiddleName, ElementName=DataGridPlayers}" Height="26" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,12,0" TabIndex="3"/>
<ComboBox x:Name="ComboBoxGender" Grid.Column="1" Grid.Row="5" SelectedIndex="{Binding SelectedItem.Male, ElementName=DataGridPlayers}" Height="26" HorizontalAlignment="Left" Width="100" TabIndex="4">
     <ComboBoxItem Content="Female"/>
     <ComboBoxItem Content="Male"/>
</ComboBox>
<ComboBox x:Name="ComboBoxCountry" Grid.Column="1" Grid.Row="6" SelectedValuePath="Name" SelectedValue="{Binding SelectedItem.Country, ElementName=DataGridPlayers}" IsTextSearchEnabled="True" TextSearch.TextPath="Name" Height="26" HorizontalAlignment="Stretch" Margin="0,0,12,0" TabIndex="5">
     <ComboBox.ItemTemplate>
          <DataTemplate>
               <StackPanel Orientation="Horizontal">
                    <Image Width="30" Source="{Binding Flag}"/>
                    <TextBlock Text="{Binding Name}" Margin="4,0,0,0"/>
               </StackPanel>
          </DataTemplate>
     </ComboBox.ItemTemplate>
</ComboBox>

而DataGrid的定义如下:

<DataGrid Grid.Column="1" x:Name="DataGridPlayers" AutoGenerateColumns="False" IsReadOnly="True" CanUserSortColumns="True" CanUserAddRows="False" AlternatingRowBackground="LightGray" SelectionChanged="DataGridPlayers_SelectionChanged" TabIndex="12">
     <DataGrid.Columns>
          <DataGridTextColumn Binding="{Binding Id}" Visibility="Hidden"/>
          <DataGridTextColumn Binding="{Binding LastName}" Visibility="Hidden"/>
          <DataGridTextColumn Binding="{Binding FirstName}" Visibility="Hidden"/>
          <DataGridTextColumn Binding="{Binding MiddleName}" Visibility="Hidden"/>
          <DataGridTextColumn Header="Player full name" Binding="{Binding FullName}" Width="1.5*"/>
          <DataGridTextColumn Binding="{Binding Male}" Visibility="Hidden"/>
          <DataGridTextColumn Header="Country" Binding="{Binding Country}" Width="*"/>
          <DataGridTemplateColumn Header="Flag" Width="40" IsReadOnly="True">
               <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                         <Image Source="{Binding Flag}"/>
                    </DataTemplate>
               </DataGridTemplateColumn.CellTemplate>
          </DataGridTemplateColumn>
     </DataGrid.Columns>
     <DataGrid.RowStyle>
          <Style TargetType="DataGridRow">
               <Style.Triggers>
                    <DataTrigger Binding="{Binding Flag}" Value="{x:Null}">
                         <Setter Property="Foreground" Value="Red"/>
                    </DataTrigger>
               </Style.Triggers>
          </Style>
     </DataGrid.RowStyle>
</DataGrid>

这是我的代码:

public partial class MainWindow : Window
    {
        private ObservableCollection<CountryModel.Country> ListCountries;
        private ObservableCollection<PlayerModel> ListPlayers;
        public MainWindow()
        {
            InitializeComponent();
            GetCountriesAsync();
            ListPlayers = new ObservableCollection<PlayerModel>();
            DataGridPlayers.ItemsSource = ListPlayers;
        }

        private async void GetCountriesAsync()
        {
            ListCountries = await Task.Run(() => CountryModel.GetCountriesAsync());
            ComboBoxCountry.ItemsSource = ListCountries;
        }

        private void ButtonAddPlayer_Click(object sender, RoutedEventArgs e)
        {
            PlayerModel newplayer = new PlayerModel { LastName = "Lastname", FirstName = "Firstname", MiddleName = "Middlename", Male = true, Country = "" }; //, Flag = null
            ListPlayers.Add(newplayer);
            DataGridPlayers.SelectedItem = ListPlayers.Last();
        }
    }

现在我有几个问题: 如何将 Flag 列绑定到该国家/地区的国旗图像? 为什么当我更新左侧的字段(例如姓氏或国家/地区)时,数据网格没有相应更新?

感谢您抽出宝贵时间提供帮助! 绝地大师

【问题讨论】:

  • 您是否安装了 Fody.PropertyChanged?如果不是,你应该看看那个。如果你不想安装任何包,那么你的模型需要实现 INotifyPropertyChanged。否则 WPF 不知道 DataGrid 中的属性是通过左侧的框更改的。您需要将此通知 WPF。
  • 太棒了!经过一番测试,我终于选择了 Fody!生活变得如此轻松!
  • 很高兴它帮助了^.^

标签: c# wpf combobox binding datagrid


【解决方案1】:

第二期: 如果你没有安装 Fody,你的类应该是这样的

我建议你看看。正如您在示例中看到的那样,清理了很多代码。 https://github.com/Fody/PropertyChanged

否则 WPF 不知道,DataGrid 中的属性是通过左侧的框更改的。您需要将此通知 WPF。

    public class Country : INotifyPropertyChanged
    {
        private string name;

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged(nameof(Name));
            }
        }

        private string abbr;

        public string Abbr
        {
            get { return abbr; }
            set
            {
                abbr = value;
                OnPropertyChanged(nameof(Abbr));
            }
        }

        private string countryName;

        public string CountryName
        {
            get { return countryName; }
            set
            {
                countryName = value;
                OnPropertyChanged(nameof(CountryName));
                OnPropertyChanged(nameof(Flag));
            }
        }

        public BitmapImage Flag => GetFlag(countryName);

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

福迪示例:

    public class Country : INotifyPropertyChanged
    {       
        public string Name { get; set; }

        public string Abbr { get; set; }

        public string CountryName { get; set; }

        public BitmapImage Flag => GetFlag(CountryName);

        public event PropertyChangedEventHandler PropertyChanged;
    }

第一个问题:当你有你的类设置作为示例时,你可以将 ImageSource 绑定到 Flag。

【讨论】:

  • 我开始了解 INotifyPropertyChanged 的​​用途。但是,正如您在我的帖子中看到的那样,我确实将标志图像加载到内存中,以免频繁访问文件。您在上面编写的类定义中不是这种情况,您将标志添加为不断读取文件的“get”吗?
  • 另外,由于我在绑定DataGrid时使用ObservableCollection,所以INotifyPropertyChanged不应该已经实现了吗?
  • @JediMaster ObservableCollection 通知在集合中添加或删除对象。它不会通知这些对象内部的属性更改。当我开始时,我也对此感到困惑。关于国旗。是的,每次CountryName 更改时它都会读取文件,但我不认为这是一个问题。但是由于您需要对属性的更改做出反应,所以无论如何它都会这样做。您可以做的一件事是在.SetNewFlag() 之类的模型中创建一个方法,该方法将在CountryName 的setter 中调用,并设置标志属性并将其保存在内存中。
  • 所以它只会在每次 CountryName 更改时设置一次。 (抱歉消息拆分,但字符已用完☺)
猜你喜欢
  • 2017-12-08
  • 2014-03-01
  • 1970-01-01
  • 2012-06-05
  • 2016-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多