Wpf 应用程序冻结 - 大量内存泄漏之谜
Wpf application freezes - massive memory leak puzzle
我有一个 mvvm wpf 应用程序,它一直正常工作,直到它冻结并导致大量内存泄漏的那一刻。
解决方案文件:
Google Disk Solution Link
该应用程序使用本地 mdf 文件,使用 Mahhaps 和一些其他参考资料(例如用于显示 gif 的参考资料)
这是导致问题的视图模型中的方法调用,使用 await 分配联系人导致了问题 - 这在另一个视图模型中使用,它没有任何问题,即使在这里它一直工作到某一刻。
public async void OnLoad()
{
IsRefreshEnabled = false;
IsRefreshProgressActive = true;
Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
IsRefreshEnabled = true;
IsRefreshProgressActive = false;
}
这是视图呈现的方式
<DataGrid SelectedItem="{Binding Contact}" AutoGenerateColumns="True" ItemsSource="{Binding Path=Contacts, Mode=TwoWay}" Style="{StaticResource AzureDataGrid}" x:Name="dataGridCodeBehind" Margin="10,54,521,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" ColumnWidth="*">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontSize" Value="10"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
我尝试删除 gif 的库(gif 未显示在该视图中,而在另一个视图中没有出现任何问题)。
对存储库的相同调用 - 获取我在另一个视图中的联系人数据,并且没有产生任何问题。
此联系人视图一直运行良好,直到突然之间。
我尝试调试,但调试器甚至没有找到代码。
将一些数据加载到数据库后,我冻结了。
冻结是通过 ContactsViewModel 的 OnLoad 方法进行的 - 此对存储库的调用用于另一个没有任何问题的 ViewModel - 它 returns 数据快速
ContactsViewModel 代码:
using Digital_Data_House_Bulk_Mailer.Commands;
using Digital_Data_House_Bulk_Mailer.Model;
using Digital_Data_House_Bulk_Mailer.Repository;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Digital_Data_House_Bulk_Mailer.ViewModel
{
class ContactsViewModel : INotifyPropertyChanged
{
private Contact _contact;
private IContactRepository _repository = new ContactRepository();
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
public RelayCommand UpdateCommand { get; set; }
public RelayCommand LoadCommand { get; set; }
public RelayCommand DeleteCommand { get; set; }
public RelayCommand DeleteAllContactsCommand { get; set; }
public RelayCommand RecreateFiltersCommand { get; set; }
private ObservableCollection<Contact> _contacts;
public ObservableCollection<Contact> Contacts
{
get { return _contacts; }
set
{
_contacts = value;
PropertyChanged(this, new PropertyChangedEventArgs("Contacts"));
//used in case of static Contacts property
//NotifyStaticPropertyChanged("Contacts");
}
}
public Contact Contact
{
get { return _contact; }
set
{
_contact = value;
DeleteCommand.RaiseCanExecuteChanged();
UpdateCommand.RaiseCanExecuteChanged();
PropertyChanged(this, new PropertyChangedEventArgs("Contact"));
}
}
private bool _isRefreshEnabled=true;
public bool IsRefreshEnabled
{
get { return _isRefreshEnabled; }
set
{
_isRefreshEnabled = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsRefreshEnabled"));
}
}
private bool _isRefreshProgressActive = false;
public bool IsRefreshProgressActive
{
get { return _isRefreshProgressActive; }
set
{
_isRefreshProgressActive = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsRefreshProgressActive"));
}
}
public ContactsViewModel()
{
DeleteCommand = new RelayCommand(OnDelete, CanDelete);
UpdateCommand = new RelayCommand(OnUpdate, CanUpdate);
LoadCommand = new RelayCommand(OnLoad, CanLoad);
DeleteAllContactsCommand = new RelayCommand(OnDeleteAllContacts, CanDeleteAllContacts);
RecreateFiltersCommand = new RelayCommand(OnRecreateFilters, CanRecreateFilters);
OnLoad();
}
public bool CanRecreateFilters()
{
return true;
}
public async void OnRecreateFilters()
{
IsRefreshProgressActive = true;
await Task.Run(() => _repository.ResetFilters());
IsRefreshProgressActive = false;
}
public async void OnDeleteAllContacts()
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure?", "DELETE ALL EXISTING CONTACTS", System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
{
IsRefreshProgressActive = true;
await Task.Run(() => _repository.DeleteAllContacts());
IsRefreshProgressActive = false;
}
}
public bool CanDeleteAllContacts()
{
return true;
}
private void OnDelete()
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure?", "Delete Contact Confirmation", System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
{
_repository.DeleteContactAsync(Contact);
Contacts.Remove(Contact);
}
}
private bool CanDelete()
{
if (Contact != null )
{
return true;
}
return false;
}
private void OnUpdate()
{
_repository.AddContactAsync(Contact);
}
private bool CanUpdate()
{
if (Contact != null )
{
return true;
}
return false;
}
public async void OnLoad()
{
IsRefreshEnabled = false;
IsRefreshProgressActive = true;
Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
IsRefreshEnabled = true;
IsRefreshProgressActive = false;
}
private ObservableCollection<Contact> GetContactsAsync()
{
return _repository.GetContactsAsync();
}
public bool CanLoad()
{
return true;
}
}
}
联系人存储库class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Digital_Data_House_Bulk_Mailer.Model;
using System.Data.Entity;
using System.Collections.ObjectModel;
using System.Data.Entity.Migrations;
using System.Collections;
using System.Data.Entity.Core.Objects;
using System.Data.SqlClient;
namespace Digital_Data_House_Bulk_Mailer.Repository
{
class ContactRepository : IContactRepository
{
digital_datahouse_bulk_mailerEntities db = null;
public ContactRepository()
{
db = new digital_datahouse_bulk_mailerEntities();
}
public string SELECT_ALL { get { return "Select All"; } private set { } }
public ObservableCollection<StateFilter> GetStates()
{
ObservableCollection<StateFilter> stateFilters = new ObservableCollection<StateFilter>();
foreach (StateFilter state in db.StateFilters.ToList() )
{
db.Entry<StateFilter>(state).Reload();
stateFilters.Add(state);
}
return stateFilters;
}
public ObservableCollection<CountyFilter> GetCounties()
{
ObservableCollection<CountyFilter> countyFilters = new ObservableCollection<CountyFilter>();
foreach (CountyFilter county in db.CountyFilters.ToList())
{
db.Entry<CountyFilter>(county).Reload();
countyFilters.Add(county);
}
return countyFilters;
}
public ObservableCollection<GenderFilter> GetGenders()
{
ObservableCollection<GenderFilter> genderFilters = new ObservableCollection<GenderFilter>();
foreach (GenderFilter gender in db.GenderFilters.ToList())
{
db.Entry<GenderFilter>(gender).Reload();
genderFilters.Add(gender);
}
return genderFilters;
}
public ObservableCollection<IndustryFilter> GetIndustries()
{
ObservableCollection<IndustryFilter> industryFilters = new ObservableCollection<IndustryFilter>();
foreach (IndustryFilter industry in db.IndustryFilters.ToList())
{
db.Entry<IndustryFilter>(industry).Reload();
industryFilters.Add(industry);
}
return industryFilters;
}
public ObservableCollection<IsContactedFilter> GetIsContacted()
{
ObservableCollection<IsContactedFilter> isContactedFilters = new ObservableCollection<IsContactedFilter>();
foreach (IsContactedFilter isContacted in db.IsContactedFilters.ToList())
{
db.Entry<IsContactedFilter>(isContacted).Reload();
isContactedFilters.Add(isContacted);
}
return isContactedFilters;
}
public ObservableCollection<SicCodeDescriptionFilter> GetSicCodeDescriptions()
{
ObservableCollection<SicCodeDescriptionFilter> sicCodeDescriptionFilters = new ObservableCollection<SicCodeDescriptionFilter>();
foreach (SicCodeDescriptionFilter sicCodeDescriptionFilter in db.SicCodeDescriptionFilters.ToList())
{
db.Entry<SicCodeDescriptionFilter>(sicCodeDescriptionFilter).Reload();
sicCodeDescriptionFilters.Add(sicCodeDescriptionFilter);
}
return sicCodeDescriptionFilters;
}
public void AddContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.AddOrUpdate(contact);
db.SaveChangesAsync();
}
}
public void DeleteContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.Remove(contact);
db.SaveChangesAsync();
}
}
public void UpdateContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.AddOrUpdate(contact);
db.SaveChangesAsync();
}
}
public ObservableCollection<Contact> GetContactsAsync()
{
db = new digital_datahouse_bulk_mailerEntities();
ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();
foreach (var contact in db.Contacts.ToList())
{
contacts.Add(contact);
}
return contacts;
}
public ObservableCollection<Contact> FilterContacts(ObservableCollection<StateFilter> states, ObservableCollection<CountyFilter> counties,
ObservableCollection<GenderFilter> genders, ObservableCollection<IndustryFilter> industries, ObservableCollection<IsContactedFilter> contacted,
ObservableCollection<SicCodeDescriptionFilter> codes, bool hasWebsite)
{
db = new digital_datahouse_bulk_mailerEntities();
ObservableCollection<Contact> filteredContacts = new ObservableCollection<Contact>();
string[] stateArray = (from s in states where s.IsChecked==true select s.State).ToArray();
string[] countyArray= (from c in counties where c.IsChecked==true select c.County).ToArray();
string[] genderArray= (from g in genders where g.IsChecked==true select g.Gender).ToArray();
string[] industryArray = (from i in industries where i.IsChecked==true select i.Industry).ToArray();
string[] contactedArray = (from c in contacted where c.IsChecked==true select c.IsContacted.ToString()).ToArray();
string[] sicCodeArray = (from c in codes where c.IsChecked==true select c.SicCodeDescription).ToArray();
var contacts=(from c in db.Contacts
where
stateArray.Contains(c.State) &&
countyArray.Contains(c.County) &&
genderArray.Contains(c.Gender) &&
industryArray.Contains(c.Industry) &&
contactedArray.Contains(c.IsContacted) &&
sicCodeArray.Contains(c.SIC_Code_Description)
select c).ToList();
foreach (Contact contact in contacts)
{
if(hasWebsite==true)
{
if(!String.IsNullOrEmpty(contact.WebSite))
{
filteredContacts.Add(contact);
}
}
else
{
filteredContacts.Add(contact);
}
}
return filteredContacts;
}
public void AddContactsRange(ObservableCollection<Contact> contacts)
{
db.Contacts.AddRange(contacts);
db.SaveChanges();
}
public void ResetFilters()
{
ResetStates();
ResetCounties();
ResetIsContactedFilters();
ResetGenders();
ResetIndustries();
ResetSicFilters();
}
private void ResetStates()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [StateFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<StateFilter> stateFilters = new List<StateFilter>();
var states = (
from c in db.Contacts
select c.State
).Distinct().ToList();
foreach (var stateName in states)
{
StateFilter state = new StateFilter();
state.State = stateName;
state.IsChecked = true;
stateFilters.Add(state);
}
db.StateFilters.AddRange(stateFilters);
db.SaveChanges();
}
public void DeleteAllContacts()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [Contact]");
db = new digital_datahouse_bulk_mailerEntities();
}
private void ResetCounties()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [CountyFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<CountyFilter> countyFilters = new List<CountyFilter>();
var counties = (
from c in db.Contacts
select c.County
).Distinct().ToList();
foreach (var countyName in counties)
{
CountyFilter county = new CountyFilter();
county.County = countyName;
county.IsChecked = true;
countyFilters.Add(county);
}
db.CountyFilters.AddRange(countyFilters);
db.SaveChanges();
}
private void ResetGenders()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [GenderFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<GenderFilter> genderFilters = new List<GenderFilter>();
var genders = (
from c in db.Contacts
select c.Gender
).Distinct().ToList();
foreach (var genderName in genders)
{
GenderFilter gender = new GenderFilter();
gender.Gender = genderName;
gender.IsChecked = true;
genderFilters.Add(gender);
}
db.GenderFilters.AddRange(genderFilters);
db.SaveChanges();
}
private void ResetIndustries()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [IndustryFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<IndustryFilter> industryFilters = new List<IndustryFilter>();
var industries = (
from c in db.Contacts
select c.Industry
).Distinct().ToList();
foreach (var industryName in industries)
{
IndustryFilter industry = new IndustryFilter();
industry.Industry = industryName;
industry.IsChecked = true;
industryFilters.Add(industry);
}
db.IndustryFilters.AddRange(industryFilters);
db.SaveChanges();
}
private void ResetIsContactedFilters()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [IsContactedFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<IsContactedFilter> isContactedFilters = new List<IsContactedFilter>();
var isContacted = (
from c in db.Contacts
select c.IsContacted
).Distinct().ToList();
foreach (var contactedName in isContacted)
{
IsContactedFilter contacted = new IsContactedFilter();
contacted.IsContacted = contactedName;
contacted.IsChecked = true;
isContactedFilters.Add(contacted);
}
db.IsContactedFilters.AddRange(isContactedFilters);
db.SaveChanges();
}
private void ResetSicFilters()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [SicCodeDescriptionFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<SicCodeDescriptionFilter> sicFilters = new List<SicCodeDescriptionFilter>();
var sics = (
from c in db.Contacts
select c.SIC_Code_Description
).Distinct().ToList();
foreach (var sic in sics)
{
SicCodeDescriptionFilter sicCode = new SicCodeDescriptionFilter();
sicCode.SicCodeDescription = sic;
sicCode.IsChecked = true;
sicFilters.Add(sicCode);
}
db.SicCodeDescriptionFilters.AddRange(sicFilters);
db.SaveChanges();
}
public void UpdateIsContactedInformation(Contact contact)
{
db = new digital_datahouse_bulk_mailerEntities();
contact.IsContacted = "True";
contact.MessageDateSent = DateTime.Today;
db.Contacts.AddOrUpdate(contact);
db.SaveChanges();
}
}
}
这是使用名为 ContactsView 的 ViewModel 的视图 - 该视图与其他视图一样包含在 MainWindow 中
联系人视图:
<UserControl x:Class="Digital_Data_House_Bulk_Mailer.View.ContactsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Digital_Data_House_Bulk_Mailer.View"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:model="clr-namespace:Digital_Data_House_Bulk_Mailer.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<model:ContactsViewModel/>
</UserControl.DataContext>
<Grid Margin="0,0,-690,-36" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="61*"/>
<ColumnDefinition Width="10*"/>
</Grid.ColumnDefinitions>
<Button Style="{StaticResource AccentedSquareButtonStyle}" Command="{Binding DeleteCommand}" Controls:TextBoxHelper.ClearTextButton="True" x:Name="delete" Content="DELETE" HorizontalAlignment="Left" Margin="115,21,0,0" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.267,0.519"/>
<Button Style="{StaticResource AccentedSquareButtonStyle}" Command="{Binding UpdateCommand}" Controls:TextBoxHelper.ClearTextButton="True" x:Name="update" Content="add / update" HorizontalAlignment="Left" Margin="10,21,0,0" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.267,0.519"/>
<Button Style="{StaticResource AccentedSquareButtonStyle}" Controls:TextBoxHelper.ClearTextButton="True" x:Name="bulk_import" Content="import new data" HorizontalAlignment="Left" Margin="220,21,0,0" VerticalAlignment="Top" Width="110" RenderTransformOrigin="0.267,0.519" Click="bulk_import_Click"/>
<DataGrid SelectedItem="{Binding Contact}" AutoGenerateColumns="True" ItemsSource="{Binding Path=Contacts, Mode=TwoWay}" Style="{StaticResource AzureDataGrid}" x:Name="dataGridCodeBehind" Margin="10,54,521,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" ColumnWidth="*">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontSize" Value="10"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
<Button IsEnabled="{Binding IsRefreshEnabled}" Style="{StaticResource AccentedSquareButtonStyle}" Command="{Binding LoadCommand}" x:Name="refreshData" Content="Refresh Contacts" HorizontalAlignment="Left" Height="22" Margin="335,21,0,0" VerticalAlignment="Top" Width="114"/>
<Controls:ProgressRing IsActive="{Binding IsRefreshProgressActive}" Foreground="{DynamicResource AccentColorBrush}" Margin="720,0,0,0" RenderTransformOrigin="-2.889,0.463" HorizontalAlignment="Left" VerticalAlignment="Top" Height="50" Width="50"/>
<Button x:Name="deleteContacts" Command="{Binding DeleteAllContactsCommand}" Style="{StaticResource AccentedSquareButtonStyle}" Content="Delete All Contacts" HorizontalAlignment="Left" Height="22" Margin="454,21,0,0" VerticalAlignment="Top" Width="114"/>
<Button x:Name="recreateFilters" Command="{Binding RecreateFiltersCommand}" Content="Recreate TO MAIL Filters" HorizontalAlignment="Left" Style="{StaticResource AccentedSquareButtonStyle}" Height="22" Margin="573,21,0,0" VerticalAlignment="Top" Width="142"/>
</Grid>
非常糟糕的是,当我尝试调试时,它甚至没有转到代码 - 方法调用。
也尝试使用一些分析工具,但我没有得出任何结论是什么导致了这个冻结问题...
当我 运行 从数据库编辑器查询数据库时,我立即得到结果。
我还在使用 ReportViewer 显示的应用程序中使用 SSRS 报告 - 它也可以正常工作 returns 相同的数据 - 它显示在单独的视图中。
在其中一个视图中,我使用 WpfAnimatedGif 库显示 GIF 动画 - 我试图删除此引用以查看是否是问题所在,但它并没有继续冻结...
我还尝试重写我的存储库 class 以使用 using 命令为每个方法创建新的数据库实例,但这不是问题所在。
解决方案文件:
Google Disk Solution Link
我终于解决了这个问题。我需要的是将 Height="500" 属性 添加到数据网格。
删除高度 属性 会使 DataGrid 在我的案例中显示超过 50 行时冻结。
添加高度后,它可以在不到一秒的时间内加载 5000 条记录。
只是一个建议。
我正在使用带有大列表的网格。我建议您打开虚拟化,这样可以进一步提高 UI.
的性能
你可以参考这个link了解更多信息
我有一个 mvvm wpf 应用程序,它一直正常工作,直到它冻结并导致大量内存泄漏的那一刻。 解决方案文件: Google Disk Solution Link
该应用程序使用本地 mdf 文件,使用 Mahhaps 和一些其他参考资料(例如用于显示 gif 的参考资料)
这是导致问题的视图模型中的方法调用,使用 await 分配联系人导致了问题 - 这在另一个视图模型中使用,它没有任何问题,即使在这里它一直工作到某一刻。
public async void OnLoad()
{
IsRefreshEnabled = false;
IsRefreshProgressActive = true;
Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
IsRefreshEnabled = true;
IsRefreshProgressActive = false;
}
这是视图呈现的方式
<DataGrid SelectedItem="{Binding Contact}" AutoGenerateColumns="True" ItemsSource="{Binding Path=Contacts, Mode=TwoWay}" Style="{StaticResource AzureDataGrid}" x:Name="dataGridCodeBehind" Margin="10,54,521,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" ColumnWidth="*">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontSize" Value="10"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
我尝试删除 gif 的库(gif 未显示在该视图中,而在另一个视图中没有出现任何问题)。
对存储库的相同调用 - 获取我在另一个视图中的联系人数据,并且没有产生任何问题。
此联系人视图一直运行良好,直到突然之间。
我尝试调试,但调试器甚至没有找到代码。
将一些数据加载到数据库后,我冻结了。
冻结是通过 ContactsViewModel 的 OnLoad 方法进行的 - 此对存储库的调用用于另一个没有任何问题的 ViewModel - 它 returns 数据快速
ContactsViewModel 代码:
using Digital_Data_House_Bulk_Mailer.Commands;
using Digital_Data_House_Bulk_Mailer.Model;
using Digital_Data_House_Bulk_Mailer.Repository;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Digital_Data_House_Bulk_Mailer.ViewModel
{
class ContactsViewModel : INotifyPropertyChanged
{
private Contact _contact;
private IContactRepository _repository = new ContactRepository();
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
public RelayCommand UpdateCommand { get; set; }
public RelayCommand LoadCommand { get; set; }
public RelayCommand DeleteCommand { get; set; }
public RelayCommand DeleteAllContactsCommand { get; set; }
public RelayCommand RecreateFiltersCommand { get; set; }
private ObservableCollection<Contact> _contacts;
public ObservableCollection<Contact> Contacts
{
get { return _contacts; }
set
{
_contacts = value;
PropertyChanged(this, new PropertyChangedEventArgs("Contacts"));
//used in case of static Contacts property
//NotifyStaticPropertyChanged("Contacts");
}
}
public Contact Contact
{
get { return _contact; }
set
{
_contact = value;
DeleteCommand.RaiseCanExecuteChanged();
UpdateCommand.RaiseCanExecuteChanged();
PropertyChanged(this, new PropertyChangedEventArgs("Contact"));
}
}
private bool _isRefreshEnabled=true;
public bool IsRefreshEnabled
{
get { return _isRefreshEnabled; }
set
{
_isRefreshEnabled = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsRefreshEnabled"));
}
}
private bool _isRefreshProgressActive = false;
public bool IsRefreshProgressActive
{
get { return _isRefreshProgressActive; }
set
{
_isRefreshProgressActive = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsRefreshProgressActive"));
}
}
public ContactsViewModel()
{
DeleteCommand = new RelayCommand(OnDelete, CanDelete);
UpdateCommand = new RelayCommand(OnUpdate, CanUpdate);
LoadCommand = new RelayCommand(OnLoad, CanLoad);
DeleteAllContactsCommand = new RelayCommand(OnDeleteAllContacts, CanDeleteAllContacts);
RecreateFiltersCommand = new RelayCommand(OnRecreateFilters, CanRecreateFilters);
OnLoad();
}
public bool CanRecreateFilters()
{
return true;
}
public async void OnRecreateFilters()
{
IsRefreshProgressActive = true;
await Task.Run(() => _repository.ResetFilters());
IsRefreshProgressActive = false;
}
public async void OnDeleteAllContacts()
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure?", "DELETE ALL EXISTING CONTACTS", System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
{
IsRefreshProgressActive = true;
await Task.Run(() => _repository.DeleteAllContacts());
IsRefreshProgressActive = false;
}
}
public bool CanDeleteAllContacts()
{
return true;
}
private void OnDelete()
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure?", "Delete Contact Confirmation", System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
{
_repository.DeleteContactAsync(Contact);
Contacts.Remove(Contact);
}
}
private bool CanDelete()
{
if (Contact != null )
{
return true;
}
return false;
}
private void OnUpdate()
{
_repository.AddContactAsync(Contact);
}
private bool CanUpdate()
{
if (Contact != null )
{
return true;
}
return false;
}
public async void OnLoad()
{
IsRefreshEnabled = false;
IsRefreshProgressActive = true;
Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
IsRefreshEnabled = true;
IsRefreshProgressActive = false;
}
private ObservableCollection<Contact> GetContactsAsync()
{
return _repository.GetContactsAsync();
}
public bool CanLoad()
{
return true;
}
}
}
联系人存储库class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Digital_Data_House_Bulk_Mailer.Model;
using System.Data.Entity;
using System.Collections.ObjectModel;
using System.Data.Entity.Migrations;
using System.Collections;
using System.Data.Entity.Core.Objects;
using System.Data.SqlClient;
namespace Digital_Data_House_Bulk_Mailer.Repository
{
class ContactRepository : IContactRepository
{
digital_datahouse_bulk_mailerEntities db = null;
public ContactRepository()
{
db = new digital_datahouse_bulk_mailerEntities();
}
public string SELECT_ALL { get { return "Select All"; } private set { } }
public ObservableCollection<StateFilter> GetStates()
{
ObservableCollection<StateFilter> stateFilters = new ObservableCollection<StateFilter>();
foreach (StateFilter state in db.StateFilters.ToList() )
{
db.Entry<StateFilter>(state).Reload();
stateFilters.Add(state);
}
return stateFilters;
}
public ObservableCollection<CountyFilter> GetCounties()
{
ObservableCollection<CountyFilter> countyFilters = new ObservableCollection<CountyFilter>();
foreach (CountyFilter county in db.CountyFilters.ToList())
{
db.Entry<CountyFilter>(county).Reload();
countyFilters.Add(county);
}
return countyFilters;
}
public ObservableCollection<GenderFilter> GetGenders()
{
ObservableCollection<GenderFilter> genderFilters = new ObservableCollection<GenderFilter>();
foreach (GenderFilter gender in db.GenderFilters.ToList())
{
db.Entry<GenderFilter>(gender).Reload();
genderFilters.Add(gender);
}
return genderFilters;
}
public ObservableCollection<IndustryFilter> GetIndustries()
{
ObservableCollection<IndustryFilter> industryFilters = new ObservableCollection<IndustryFilter>();
foreach (IndustryFilter industry in db.IndustryFilters.ToList())
{
db.Entry<IndustryFilter>(industry).Reload();
industryFilters.Add(industry);
}
return industryFilters;
}
public ObservableCollection<IsContactedFilter> GetIsContacted()
{
ObservableCollection<IsContactedFilter> isContactedFilters = new ObservableCollection<IsContactedFilter>();
foreach (IsContactedFilter isContacted in db.IsContactedFilters.ToList())
{
db.Entry<IsContactedFilter>(isContacted).Reload();
isContactedFilters.Add(isContacted);
}
return isContactedFilters;
}
public ObservableCollection<SicCodeDescriptionFilter> GetSicCodeDescriptions()
{
ObservableCollection<SicCodeDescriptionFilter> sicCodeDescriptionFilters = new ObservableCollection<SicCodeDescriptionFilter>();
foreach (SicCodeDescriptionFilter sicCodeDescriptionFilter in db.SicCodeDescriptionFilters.ToList())
{
db.Entry<SicCodeDescriptionFilter>(sicCodeDescriptionFilter).Reload();
sicCodeDescriptionFilters.Add(sicCodeDescriptionFilter);
}
return sicCodeDescriptionFilters;
}
public void AddContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.AddOrUpdate(contact);
db.SaveChangesAsync();
}
}
public void DeleteContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.Remove(contact);
db.SaveChangesAsync();
}
}
public void UpdateContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.AddOrUpdate(contact);
db.SaveChangesAsync();
}
}
public ObservableCollection<Contact> GetContactsAsync()
{
db = new digital_datahouse_bulk_mailerEntities();
ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();
foreach (var contact in db.Contacts.ToList())
{
contacts.Add(contact);
}
return contacts;
}
public ObservableCollection<Contact> FilterContacts(ObservableCollection<StateFilter> states, ObservableCollection<CountyFilter> counties,
ObservableCollection<GenderFilter> genders, ObservableCollection<IndustryFilter> industries, ObservableCollection<IsContactedFilter> contacted,
ObservableCollection<SicCodeDescriptionFilter> codes, bool hasWebsite)
{
db = new digital_datahouse_bulk_mailerEntities();
ObservableCollection<Contact> filteredContacts = new ObservableCollection<Contact>();
string[] stateArray = (from s in states where s.IsChecked==true select s.State).ToArray();
string[] countyArray= (from c in counties where c.IsChecked==true select c.County).ToArray();
string[] genderArray= (from g in genders where g.IsChecked==true select g.Gender).ToArray();
string[] industryArray = (from i in industries where i.IsChecked==true select i.Industry).ToArray();
string[] contactedArray = (from c in contacted where c.IsChecked==true select c.IsContacted.ToString()).ToArray();
string[] sicCodeArray = (from c in codes where c.IsChecked==true select c.SicCodeDescription).ToArray();
var contacts=(from c in db.Contacts
where
stateArray.Contains(c.State) &&
countyArray.Contains(c.County) &&
genderArray.Contains(c.Gender) &&
industryArray.Contains(c.Industry) &&
contactedArray.Contains(c.IsContacted) &&
sicCodeArray.Contains(c.SIC_Code_Description)
select c).ToList();
foreach (Contact contact in contacts)
{
if(hasWebsite==true)
{
if(!String.IsNullOrEmpty(contact.WebSite))
{
filteredContacts.Add(contact);
}
}
else
{
filteredContacts.Add(contact);
}
}
return filteredContacts;
}
public void AddContactsRange(ObservableCollection<Contact> contacts)
{
db.Contacts.AddRange(contacts);
db.SaveChanges();
}
public void ResetFilters()
{
ResetStates();
ResetCounties();
ResetIsContactedFilters();
ResetGenders();
ResetIndustries();
ResetSicFilters();
}
private void ResetStates()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [StateFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<StateFilter> stateFilters = new List<StateFilter>();
var states = (
from c in db.Contacts
select c.State
).Distinct().ToList();
foreach (var stateName in states)
{
StateFilter state = new StateFilter();
state.State = stateName;
state.IsChecked = true;
stateFilters.Add(state);
}
db.StateFilters.AddRange(stateFilters);
db.SaveChanges();
}
public void DeleteAllContacts()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [Contact]");
db = new digital_datahouse_bulk_mailerEntities();
}
private void ResetCounties()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [CountyFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<CountyFilter> countyFilters = new List<CountyFilter>();
var counties = (
from c in db.Contacts
select c.County
).Distinct().ToList();
foreach (var countyName in counties)
{
CountyFilter county = new CountyFilter();
county.County = countyName;
county.IsChecked = true;
countyFilters.Add(county);
}
db.CountyFilters.AddRange(countyFilters);
db.SaveChanges();
}
private void ResetGenders()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [GenderFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<GenderFilter> genderFilters = new List<GenderFilter>();
var genders = (
from c in db.Contacts
select c.Gender
).Distinct().ToList();
foreach (var genderName in genders)
{
GenderFilter gender = new GenderFilter();
gender.Gender = genderName;
gender.IsChecked = true;
genderFilters.Add(gender);
}
db.GenderFilters.AddRange(genderFilters);
db.SaveChanges();
}
private void ResetIndustries()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [IndustryFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<IndustryFilter> industryFilters = new List<IndustryFilter>();
var industries = (
from c in db.Contacts
select c.Industry
).Distinct().ToList();
foreach (var industryName in industries)
{
IndustryFilter industry = new IndustryFilter();
industry.Industry = industryName;
industry.IsChecked = true;
industryFilters.Add(industry);
}
db.IndustryFilters.AddRange(industryFilters);
db.SaveChanges();
}
private void ResetIsContactedFilters()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [IsContactedFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<IsContactedFilter> isContactedFilters = new List<IsContactedFilter>();
var isContacted = (
from c in db.Contacts
select c.IsContacted
).Distinct().ToList();
foreach (var contactedName in isContacted)
{
IsContactedFilter contacted = new IsContactedFilter();
contacted.IsContacted = contactedName;
contacted.IsChecked = true;
isContactedFilters.Add(contacted);
}
db.IsContactedFilters.AddRange(isContactedFilters);
db.SaveChanges();
}
private void ResetSicFilters()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [SicCodeDescriptionFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<SicCodeDescriptionFilter> sicFilters = new List<SicCodeDescriptionFilter>();
var sics = (
from c in db.Contacts
select c.SIC_Code_Description
).Distinct().ToList();
foreach (var sic in sics)
{
SicCodeDescriptionFilter sicCode = new SicCodeDescriptionFilter();
sicCode.SicCodeDescription = sic;
sicCode.IsChecked = true;
sicFilters.Add(sicCode);
}
db.SicCodeDescriptionFilters.AddRange(sicFilters);
db.SaveChanges();
}
public void UpdateIsContactedInformation(Contact contact)
{
db = new digital_datahouse_bulk_mailerEntities();
contact.IsContacted = "True";
contact.MessageDateSent = DateTime.Today;
db.Contacts.AddOrUpdate(contact);
db.SaveChanges();
}
}
}
这是使用名为 ContactsView 的 ViewModel 的视图 - 该视图与其他视图一样包含在 MainWindow 中
联系人视图:
<UserControl x:Class="Digital_Data_House_Bulk_Mailer.View.ContactsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Digital_Data_House_Bulk_Mailer.View"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:model="clr-namespace:Digital_Data_House_Bulk_Mailer.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<model:ContactsViewModel/>
</UserControl.DataContext>
<Grid Margin="0,0,-690,-36" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="61*"/>
<ColumnDefinition Width="10*"/>
</Grid.ColumnDefinitions>
<Button Style="{StaticResource AccentedSquareButtonStyle}" Command="{Binding DeleteCommand}" Controls:TextBoxHelper.ClearTextButton="True" x:Name="delete" Content="DELETE" HorizontalAlignment="Left" Margin="115,21,0,0" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.267,0.519"/>
<Button Style="{StaticResource AccentedSquareButtonStyle}" Command="{Binding UpdateCommand}" Controls:TextBoxHelper.ClearTextButton="True" x:Name="update" Content="add / update" HorizontalAlignment="Left" Margin="10,21,0,0" VerticalAlignment="Top" Width="100" RenderTransformOrigin="0.267,0.519"/>
<Button Style="{StaticResource AccentedSquareButtonStyle}" Controls:TextBoxHelper.ClearTextButton="True" x:Name="bulk_import" Content="import new data" HorizontalAlignment="Left" Margin="220,21,0,0" VerticalAlignment="Top" Width="110" RenderTransformOrigin="0.267,0.519" Click="bulk_import_Click"/>
<DataGrid SelectedItem="{Binding Contact}" AutoGenerateColumns="True" ItemsSource="{Binding Path=Contacts, Mode=TwoWay}" Style="{StaticResource AzureDataGrid}" x:Name="dataGridCodeBehind" Margin="10,54,521,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" ColumnWidth="*">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontSize" Value="10"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
<Button IsEnabled="{Binding IsRefreshEnabled}" Style="{StaticResource AccentedSquareButtonStyle}" Command="{Binding LoadCommand}" x:Name="refreshData" Content="Refresh Contacts" HorizontalAlignment="Left" Height="22" Margin="335,21,0,0" VerticalAlignment="Top" Width="114"/>
<Controls:ProgressRing IsActive="{Binding IsRefreshProgressActive}" Foreground="{DynamicResource AccentColorBrush}" Margin="720,0,0,0" RenderTransformOrigin="-2.889,0.463" HorizontalAlignment="Left" VerticalAlignment="Top" Height="50" Width="50"/>
<Button x:Name="deleteContacts" Command="{Binding DeleteAllContactsCommand}" Style="{StaticResource AccentedSquareButtonStyle}" Content="Delete All Contacts" HorizontalAlignment="Left" Height="22" Margin="454,21,0,0" VerticalAlignment="Top" Width="114"/>
<Button x:Name="recreateFilters" Command="{Binding RecreateFiltersCommand}" Content="Recreate TO MAIL Filters" HorizontalAlignment="Left" Style="{StaticResource AccentedSquareButtonStyle}" Height="22" Margin="573,21,0,0" VerticalAlignment="Top" Width="142"/>
</Grid>
非常糟糕的是,当我尝试调试时,它甚至没有转到代码 - 方法调用。 也尝试使用一些分析工具,但我没有得出任何结论是什么导致了这个冻结问题...
当我 运行 从数据库编辑器查询数据库时,我立即得到结果。
我还在使用 ReportViewer 显示的应用程序中使用 SSRS 报告 - 它也可以正常工作 returns 相同的数据 - 它显示在单独的视图中。
在其中一个视图中,我使用 WpfAnimatedGif 库显示 GIF 动画 - 我试图删除此引用以查看是否是问题所在,但它并没有继续冻结...
我还尝试重写我的存储库 class 以使用 using 命令为每个方法创建新的数据库实例,但这不是问题所在。
解决方案文件: Google Disk Solution Link
我终于解决了这个问题。我需要的是将 Height="500" 属性 添加到数据网格。
删除高度 属性 会使 DataGrid 在我的案例中显示超过 50 行时冻结。 添加高度后,它可以在不到一秒的时间内加载 5000 条记录。
只是一个建议。
我正在使用带有大列表的网格。我建议您打开虚拟化,这样可以进一步提高 UI.
的性能你可以参考这个link了解更多信息