在WPF DataGrid中加载多个按钮并重新加载会导致内存不足

时间:2019-07-03 08:05:18

标签: c# wpf memory-leaks datagrid media-player

我将DataGrid加载到UserControl中,并在初始化时加载数据,该数据由多行按钮组成,每行按钮均播放声音或显示图像。

我的类继承自IDisposable,我相信我会从每个事件处理程序退订,在加载文件并尝试DataGrid绑定模式一次后,我使用了Bitmap.Freeze()。尝试强制使用GC.Collect()多次,但没有任何影响。我还使用dotMemory查找泄漏,但这是我第一次使用它,我没有任何运气。

在“即时”窗口中,我也多次获得这些消息,我不知道是否存在任何关联:

  

System.Windows.Data错误:4:找不到引用为'RelativeSource FindAncestor的绑定源,AncestorType ='System.Windows.FrameworkElement',AncestorLevel ='1'。 BindingExpression:Path =(0); DataItem = null;目标元素是'AudioButton'(Name ='');目标属性为“前景”(类型为“画笔”)

UserControl.xaml文件:

<DataGrid x:Name="AssetDataGrid" IsReadOnly="True" ItemsSource="{Binding wordlist, IsAsync=True}" EnableColumnVirtualization="True" EnableRowVirtualization="True"  AutoGenerateColumns="False" Margin="0,50,0,0"  HeadersVisibility="Column" CanUserAddRows="False" CanUserReorderColumns="False" CanUserResizeRows="True" Initialized="AssetDataGrid_Initialized">
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Copy selected assets" Click="MenuItem_Click" />
                </ContextMenu>
            </DataGrid.ContextMenu>
            <DataGrid.Columns>

                <DataGridTextColumn Header="ID" Binding="{Binding id, Mode=OneWay}" Width="Auto" />
                <DataGridTextColumn Header="Word" Binding="{Binding word, Mode=OneWay}" Width="*" />
                <DataGridTextColumn Header="Book" Binding="{Binding book_name, Mode=OneWay}" Width="*" />

                <DataGridTemplateColumn Header="" IsReadOnly="False" Width="Auto">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding AudioCheck, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding hasAudio, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Audios" MinWidth="40" Width="*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ItemsControl x:Name="audios" ItemsSource="{Binding audio_path, Mode=OneWay}" Padding="0" Margin="-10">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Margin="0" Orientation="Horizontal" />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="" IsReadOnly="False" Width="Auto">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding ImageCheck, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding hasImage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Images" MinWidth="40" Width="*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ItemsControl ItemsSource="{Binding image_path, Mode=OneWay}" Padding="0" Margin="-10">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Margin="0" Orientation="Horizontal" />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>

UserControl.cs文件:

private async void AssetDataGrid_Initialized(object sender, EventArgs e)
{
    Task LoadDataTask = LoadDataAsync();
    await LoadDataTask;
}

public async void LoadDataAsync()
{
     string ConnectionString = InternalSettings.GetConnectionStringByName("Default Connection");

      if (!string.IsNullOrEmpty(ConnectionString))
      {
             MySqlConnection conn = new MySqlConnection(ConnectionString);
             try
             {
                    conn.Open();
                    MySqlCommand cmd = new MySqlCommand("SELECT * FROM wordlist.wordlist;", conn);
                    MySqlDataAdapter adp = new MySqlDataAdapter(cmd);
                    DataSet ds = new DataSet();
                    await adp.FillAsync(ds);

                    foreach (DataRow wordData in ds.Tables[0].Rows)
                    {
                        WordlistMain word = new WordlistMain(int.Parse(wordData[0].ToString()), wordData[1].ToString(), wordData[2].ToString(), wordData[3].ToString(), wordData[4].ToString());
                        wordlist.Add(word);
                    }

                    cv = CollectionViewSource.GetDefaultView(wordlist);
               }
               catch (Exception ex)
               {
                   MessageBox.Show(ex.Message, ex.GetType().Name, MessageBoxButton.OK, MessageBoxImage.Error);
               }
               finally
               {
                   conn.Close();
                   conn.Dispose();
               }
       }
       else
       {
           MessageBox.Show("Connection string cannot be Null or Empty!");
       }
}

private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
      if (wordlist != null)
      {
            foreach (WordlistMain item in wordlist)
            {
                  item.Dispose();
            }
      }
      //GC.Collect();
      //GC.WaitForPendingFinalizers();
      //GC.Collect();
}

类文件:

public class WordlistMain : IDisposable
    {
        public WordlistMain(int id, string word, string book_name, string audio_path, string image_path)
        {
            string NasPath = ConfigurationManager.AppSettings["AssetsPath"];

            this.id = id;
            this.word = word ?? throw new ArgumentNullException(nameof(word));
            this.book_name = book_name.Replace("|", ", ") ?? throw new ArgumentNullException(nameof(book_name));

            string[] audioDirectories = audio_path.Split(new char[] { '|' });
            string[] ImageDirectories = image_path.Split(new char[] { '|' });

            this.audio_path = new List<AudioButton>();

            for (int i = 0; i < audioDirectories.Length; i++)
            {
                if (!string.IsNullOrEmpty(audioDirectories[i]) && !string.IsNullOrWhiteSpace(audioDirectories[i]))
                {
                    if (!hasAudio)
                    {
                        hasAudio = true;
                    }

                    this.audio_path.Add(new AudioButton());
                    this.audio_path[i].SetResourceReference(FrameworkElement.StyleProperty, "MaterialDesignToolForegroundButton");
                    this.audio_path[i].Padding = new Thickness(0);
                    PackIcon packIcon = new PackIcon();
                    packIcon.Kind = PackIconKind.Play;
                    packIcon.Width = double.NaN;
                    packIcon.Height = double.NaN;
                    this.audio_path[i].Content = packIcon;
                    this.audio_path[i].MinHeight = 20;
                    this.audio_path[i].MinWidth = 20;
                    this.audio_path[i].Path = NasPath + audioDirectories[i];

                    AudioButton button = this.audio_path[i];

                    this.audio_path[i].Click += AudioButton_Click;
                }
            }

            this.image_path = new List<ImageButton>();
            for (int i = 0; i < ImageDirectories.Length; i++)
            {
                if (!string.IsNullOrEmpty(ImageDirectories[i]) && !string.IsNullOrWhiteSpace(ImageDirectories[i]))
                {
                    this.image_path.Add(new ImageButton());
                    if (!hasImage)
                    {
                        hasImage = true;
                    }
                    this.image_path[i].SetResourceReference(FrameworkElement.StyleProperty, "MaterialDesignToolForegroundButton");
                    this.image_path[i].Padding = new Thickness(0);
                    PackIcon packIcon = new PackIcon();
                    packIcon.Kind = PackIconKind.Image;
                    packIcon.Width = double.NaN;
                    packIcon.Height = double.NaN;
                    this.image_path[i].Content = packIcon;
                    this.image_path[i].MinHeight = 20;
                    this.image_path[i].MinWidth = 20;
                    this.image_path[i].Path = NasPath + ImageDirectories[i];

                    BitmapSource bmp = new BitmapImage(new Uri(this.image_path[i].Path));
                    bmp.Freeze();

                    Image img = new Image()
                    {
                        Source = bmp,
                        Width = bmp.Width,
                        Height = bmp.Height,
                        Stretch = Stretch.UniformToFill
                    };
                    img.Stretch = Stretch.UniformToFill;
                    ToolTip toolTip = new ToolTip();
                    Viewbox stackPanel = new Viewbox();
                    stackPanel.Child = img;
                    stackPanel.MaxHeight = 200;
                    stackPanel.MaxWidth = 200;
                    toolTip.Content = stackPanel;
                    this.image_path[i].ToolTip = toolTip;
                }
            }
        }

        private void AudioButton_Click(object sender, RoutedEventArgs e)
        {
            AudioButton button = sender as AudioButton;

            StaticAudioPlayer.Player.AudioStoped += (sender2, e2) => MediaPlayer_MediaEnded(sender2, e2, button);
            StaticAudioPlayer.Player.MediaEnded += (sender2, e2) => MediaPlayer_MediaEnded(sender2, e2, button);

            StopPlayers();
            PackIcon packIcon = new PackIcon();
            packIcon.Kind = PackIconKind.PlayBoxOutline;
            packIcon.Width = double.NaN;
            packIcon.Height = double.NaN;
            button.Content = packIcon;

            if (StaticAudioPlayer.Player.Position != new TimeSpan(0))
            {
                StaticAudioPlayer.Player.Stop();
            }

            StaticAudioPlayer.Player.Open(new Uri(button.Path));

            StaticAudioPlayer.Player.Play();
            CurrentPlayers.Players.Add(StaticAudioPlayer.Player);
        }

        private void StopPlayers()
        {
            for (int i = CurrentPlayers.Players.Count - 1; i >= 0; i--)
            {
                CurrentPlayers.Players[i].Stop();
                CurrentPlayers.Players[i].Close();
                CurrentPlayers.Players[i].AudioStoped?.Invoke(CurrentPlayers.Players[i], EventArgs.Empty);
                CurrentPlayers.Players.RemoveAt(i);
            }
        }

        private void MediaPlayer_MediaEnded(object sender, EventArgs e, Button button)
        {

            PackIcon packIcon = new PackIcon();
            packIcon.Kind = PackIconKind.Play;
            packIcon.Width = double.NaN;
            packIcon.Height = double.NaN;
            button.Content = packIcon;
            StaticAudioPlayer.Player.Close();
            foreach (AudioButton audioButton in this.audio_path)
            {
                StaticAudioPlayer.Player.MediaEnded -= (sender2, e2) => MediaPlayer_MediaEnded(sender2, e2, audioButton);
                StaticAudioPlayer.Player.AudioStoped -= (sender2, e2) => MediaPlayer_MediaEnded(sender2, e2, audioButton);
            }
        }

        #region Properties
        private int _id;
        public int id
        {
            get
            {
                return _id;
            }
            set
            {
                _id = value;
                NotifyPropertyChanged("id");
            }
        }

        private string _word;
        public string word
        {
            get
            {
                return _word;
            }
            set
            {
                _word = value;
                NotifyPropertyChanged("word");
            }
        }

        private string _book_name;
        public string book_name
        {
            get
            {
                return _book_name;
            }
            set
            {
                _book_name = value;
                NotifyPropertyChanged("book_name");
            }
        }

        private bool _AudioCheck = false;
        public bool AudioCheck
        {
            get
            {
                return _AudioCheck;

            }
            set
            {
                _AudioCheck = value;
                NotifyPropertyChanged("AudioCheck");
            }
        }


        private bool _hasAudio = false;
        public bool hasAudio
        {
            get
            {
                return _hasAudio;
            }
            set
            {
                _hasAudio = value;
                NotifyPropertyChanged("hasAudio");
            }
        }

        private List<AudioButton> _audio_path;
        public List<AudioButton> audio_path
        {
            get
            {
                return _audio_path;
            }
            set
            {
                _audio_path = value;
                NotifyPropertyChanged("audio_path");
            }
        }

        private bool _ImageCheck = false;
        public bool ImageCheck
        {
            get
            {
                return _ImageCheck;
            }
            set
            {
                _ImageCheck = value;
                NotifyPropertyChanged("ImageCheck");
            }
        }

        private bool _hasImage = false;
        public bool hasImage
        {
            get
            {
                return _hasImage;
            }
            set
            {
                _hasImage = value;
                NotifyPropertyChanged("hasImage");
            }
        }

        private List<ImageButton> _image_path { get; set; }

        public List<ImageButton> image_path
        {
            get
            {
                return _image_path;
            }
            set
            {
                _image_path = value;
                NotifyPropertyChanged("image_path");
            }
        }
        #endregion

        public event PropertyChangedEventHandler PropertyChanged;
        internal void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public void Dispose()
        {
            for (int i = 0; i < this.audio_path.Count; i++)
            {
                StaticAudioPlayer.Player.Close();
                StaticAudioPlayer.Player.MediaEnded -= (sender2, e2) => MediaPlayer_MediaEnded(sender2, e2, this.audio_path[i]);
                StaticAudioPlayer.Player.AudioStoped -= (sender2, e2) => MediaPlayer_MediaEnded(sender2, e2, this.audio_path[i]);
                this.audio_path[i].Click -= AudioButton_Click;
            }

            CurrentPlayers.Players.Clear();
            //GC.SuppressFinalize(this);
            //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        }
    }

    public class AudioButton : Button
    {
        public string Path { get; set; }
    }

    public class ImageButton : Button
    {
        public string Path { get; set; }
    }

    public static class CurrentPlayers
    {
        public static List<AssetMediaPlayer> Players = new List<AssetMediaPlayer>();
    }

static class StaticAudioPlayer
    {
        public static AssetMediaPlayer Player = new AssetMediaPlayer();
    }

    public class AssetMediaPlayer : System.Windows.Media.MediaPlayer
    {
        public EventHandler AudioStoped { get; set; }
    }

感谢您的帮助!

0 个答案:

没有答案