我将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; }
}
感谢您的帮助!