【发布时间】:2013-02-14 20:44:10
【问题描述】:
我正在尝试过滤绑定到我的 WPF UI 上的 ListView 的 ObservableCollection 数据。但是,当集合刷新时,ListView 变为空白并且不显示任何内容。我也在尝试自学 MVVM 模式。问题可能是我如何不刷新控件,但我怎样才能让它以 MVVM 模式刷新?我习惯于做 Winforms 和 code behind,当用户点击 Search 按钮时会发生过滤逻辑 - 这会调用 ApplicationModel.Search 方法。
型号代码:
public class AppObject
{
public string Name { get; set; }
public string Description { get; set; }
public string Owner { get; set; }
public string Email { get; set; }
public AppObject(string name, string desc, string owner, string email)
{
this.Name = name;
this.Description = desc;
this.Owner = owner;
this.Email = email;
}
}
public class ApplicationsModel : ObservableCollection<AppObject>
{
private static object threadLocker;
private static ApplicationsModel current;
static ApplicationsModel()
{
threadLocker = new object();
}
public static ApplicationsModel Current
{
get
{
lock (threadLocker)
{
if (current == null)
{
current = new ApplicationsModel();
}
}
return current;
}
}
private ApplicationsModel()
{
this.Refresh();
}
private ApplicationsModel(IEnumerable<AppObject> collection)
: base(collection)
{
}
private void Refresh()
{
try
{
// Query database to get the initial data - this code works fine
}
catch (Exception e)
{
string error = String.Format("Could not refresh repository list: {0}", e.Message);
MessageBox.Show(error, "Error Refreshing", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public void Search(string key)
{
IEnumerable<AppObject> newList = current.Items.Where<AppObject>(t => t.Name.Equals(key));
foreach (AppObject app in newList)
{
Console.WriteLine(app.Name);
Console.WriteLine(app.Description);
}
current.Clear();
current = null;
current = new ApplicationsModel(newList);
}
public void ClearSearch()
{
Console.WriteLine("ClearSearch method called");
}
}
ViewModel 代码:
public class ApplicationViewModel
{
public ObservableCollection<AppObject> AppCollection { get; set; }
static string searchString;
static string emailString;
public AppObject SelectedApp { get; set; }
public string AppToSearch
{
get
{
return searchString;
}
set
{
searchString = value;
}
}
public string AppToRequest
{
get
{
return SelectedApp.Email;
}
set
{
SelectedApp.Email = value;
}
}
private SearchButtonCommand searchButtonCmd;
private ClearButtonCommand clearButtonCmd;
private EmailButtonCommand emailButtonCmd;
public ApplicationViewModel()
{
this.AppCollection = ApplicationsModel.Current;
}
public ICommand SearchButtonPressed
{
get
{
if (this.searchButtonCmd == null)
{
this.searchButtonCmd = new SearchButtonCommand();
}
return this.searchButtonCmd;
}
}
public ICommand ClearButtonPressed
{
get
{
if (this.clearButtonCmd == null)
{
this.clearButtonCmd = new ClearButtonCommand();
}
return this.clearButtonCmd;
}
}
public ICommand EmailButtonPressed
{
get
{
if (this.emailButtonCmd == null)
{
this.emailButtonCmd = new EmailButtonCommand();
}
return this.emailButtonCmd;
}
}
private class SearchButtonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
string searchkey = ApplicationViewModel.searchString;
ApplicationsModel.Current.Search(searchkey);
}
public bool CanExecute(object parameter)
{
return true;
}
}
private class ClearButtonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
ApplicationsModel.Current.ClearSearch();
}
public bool CanExecute(object parameter)
{
return true;
}
}
private class EmailButtonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
string targetEmail = ApplicationViewModel.emailString;
}
public bool CanExecute(object parameter)
{
return true;
}
}
}
UI XAML:
<Window.DataContext>
<vm:ApplicationViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Height="84" HorizontalAlignment="Left" Margin="0,5,5,5" Name="imgNexusLogo" Stretch="Fill" VerticalAlignment="Top" Width="600" Source="C:\source\Nexus\NexusShop\Images\nexus1bannerlong.png" />
<Grid Grid.Row="1" HorizontalAlignment="Center" Margin="0,5,5,5" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Search for Application">
<Label.Foreground>
<SolidColorBrush Color="LightCyan" />
</Label.Foreground>
</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="3" Width="500" Text="{Binding AppToSearch}" />
<Button Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" Width="100" Height="20" Margin="3" Background="LightCyan" Content="Search" vm:ButtonBehaviour.SearchCommand="{Binding SearchButtonPressed}" />
<Button Grid.Row="0" Grid.Column="3" HorizontalAlignment="Right" Width="100" Height="20" Margin="3" Background="LightCyan" Content="Clear Search" vm:ButtonBehaviour.ClearCommand="{Binding ClearButtonPressed}"/>
</Grid>
<ListView Grid.Row="2" BorderBrush="Black" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=AppCollection}" SelectedItem="{Binding SelectedNexusApp}">
<ListView.View>
<GridView>
<GridViewColumn Header="Application Name" Width="100" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Application Description" Width="800" DisplayMemberBinding="{Binding Description}"/>
<GridViewColumn Header="Application Owner" Width="100" DisplayMemberBinding="{Binding Owner}"/>
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="3" HorizontalAlignment="Center" Width="200" Height="30" Margin="3" Background="LightCyan" Content="Request Application" vm:ButtonBehaviour.EmailCommand="{Binding EmailButtonPressed}" />
</Grid>
再次,过滤功能在单击搜索按钮时发生,它运行 ApplicationModel.Search(string stringtofilteron) 上的方法。我的 MVVM 类基于本教程:http://www.codeproject.com/Articles/81484/A-Practical-Quick-start-Tutorial-on-MVVM-in-WPF
感谢您的帮助!
固定代码: 公共类 ApplicationViewModel : INotifyPropertyChanged
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
ApplicationsModel.Current.Search(this.searchString);
NotifyPropertyChanged();
我更改了模型中的 Refresh 方法以在那里进行过滤。
【问题讨论】:
-
Jeffrey Khan 是对的 - 你缺少 INotify。几个问题。 1)你为什么使用threadlocker?所有这些工作都在 UI 线程上完成,不应被其他线程触及。 2)为什么每次刷新时都创建一个新的视图模型?为什么不直接刷新 View Model 上的 Observable Collection?在处理可能连接到视图模型的事件时,这更容易做到并且更友好。如果您不手动删除事件处理程序 - 视图模型永远不会被释放,您的应用将占用内存。
-
是的,那是一个 clusterf**k,会导致内存泄漏。很晚了,我累了,所以我才写了那种东西。我只是根据您的建议更改了刷新方法以采用参数进行过滤,然后效果很好,并且不会每次都创建新集合,我们不必担心处置。
标签: c# wpf listview mvvm observablecollection