我正在尝试了解WPF视图模型。
假设我有一个项目列表。在某些情况下,我想填充列表并指定应该选择的项的值。
在其他情况下,我只想重新填充列表,控件应尝试选择之前选择的项目。
在其他情况下,我想在删除项目后重新填充列表。在这种情况下,无法选择之前选择的项目,我希望代码在相同的列表位置选择项目(如果最后一项被删除,则选择较低的项目)。
通常情况下,我会填充列表,如上所述。但如果我必须绑定ItemsSource
和SelectedItem
,这似乎很尴尬。
这种逻辑可以在MVVM设计中实现吗?
答案 0 :(得分:2)
这种逻辑可以在MVVM设计中实现吗?
不确定。
但是您应该注意,idx = [ el_idx for el_idx, el in enumerate(theList) if el.IsSomething() ]
[ theList[i].SetIt(False) for i in idx ]
中设置的值必须出现在SelectedItem
中。所以,你的物品必须有一些身份。例如,想象从Web服务加载的客户列表:
ItemsSource
在实施public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
排序时,您可能需要重新选择之前选择的客户:
RefreshCommand
类似的方法可用于您提到的其他案例。
答案 1 :(得分:1)
您可能需要考虑为INotifyCollectionChanged
使用ListBox.ItemsSource
界面。如果您正在处理专门的集合,您可能需要自己实现。但是,您可以通过使用(或子类化)ObservableCollection<T>
来实现上述接口。无论哪种方式,使用此接口都允许ListBox在更新基础集合时自动更新。
答案 2 :(得分:1)
我想与您分享这个示例项目,作为如何将MVVM与集合一起使用的简单演示。我希望你觉得它很有用。
因此,您要做的第一件事就是定义一个ObservableObject
类,使您可以访问INotifyPropertyChanged
。这是一个非常基本的实现,但它在大多数情况下都能很好地工作。
using System.ComponentModel;
namespace WpfApplication8
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
我们将在我们的视图模型中实现此功能,以及我们希望使用的任何自定义类型INotifyPropertyChanged
。
此演示将使用Book
课程。 Book
类只会包含几个字段,我们还会在同一个文件中包含ICommand
实现,以便以后更轻松地进行绑定。每当ICommand
专门应用于Type
时,我就会使用Type
的代码文件来整齐地组合在一起。
using System;
using System.Windows.Input;
namespace WpfApplication8
{
public class Book : ObservableObject
{
private string _title = "";
public string Title
{
get
{
return _title;
}
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
private string _author = "";
public string Author
{
get
{
return _author;
}
set
{
_author = value;
RaisePropertyChanged("Author");
}
}
}
public abstract class BookCommand : ICommand
{
public abstract void Execute(object parameter);
public virtual bool CanExecute(object parameter)
{
return parameter is Book;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
接下来,我们需要一个ObservableCollection
实现,让我们完全控制添加或删除对象时会发生什么。我们还希望ICommand
个对象添加删除图书。
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System;
namespace WpfApplication8
{
public class BookCollection : ObservableCollection<Book>
{
private ObservableObject parent;
private string name = "";
public BookCollection(ObservableObject parent, string name)
{
this.parent = parent;
AddBook = new CMD_AddBookToCollection(this);
RemoveBook = new CMD_RemoveBookFromCollection(this);
CollectionChanged += (s, e) =>
{
if(e.Action == NotifyCollectionChangedAction.Add)
{
// what will i do when a new item is added to the collection?
}
else if(e.Action == NotifyCollectionChangedAction.Remove)
{
// what will i do when an item is removed from the collection?
if(Items.Count > 0)
{
if(Items.Count < _lastSelectedIndex)
{
SelectedItem = Items[_lastSelectedIndex];
}
else
{
SelectedItem = Items[Items.Count - 1];
}
}
}
};
}
private int _lastSelectedIndex = -1;
private Book _selectedItem = null;
public Book SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
_lastSelectedIndex = Items.IndexOf(_selectedItem);
parent.RaisePropertyChanged(name);
}
}
public ICommand AddBook { get; set; }
public ICommand RemoveBook { get; set; }
}
public abstract class BookCollectionCommand : BookCommand
{
public BookCollection Collection { get; set; }
public BookCollectionCommand(BookCollection collection)
{
this.Collection = collection;
}
}
public class CMD_RemoveBookFromCollection : BookCollectionCommand
{
public CMD_RemoveBookFromCollection(BookCollection collection) : base(collection) { }
public override void Execute(object parameter)
{
base.Collection.Remove((Book)parameter);
}
}
public class CMD_AddBookToCollection : BookCollectionCommand
{
public CMD_AddBookToCollection(BookCollection collection) : base(collection) { }
public override void Execute(object parameter)
{
Book template = parameter as Book;
Book book = new Book()
{
Author = template.Author,
Title = template.Title
};
base.Collection.Add(book);
template.Author = "";
template.Title = "";
}
}
}
一开始看起来似乎势不可挡,但是当你逐步分解它时,它并不是那么糟糕。 CollectionChanged
事件使我们能够在从集合中添加或删除项目时添加自定义功能。例如,这可以很容易地在删除某些内容后从列表中选择一个新项目。我们还创建了一个BookCollectionCommand
抽象类作为我们具体ICommand
实现的基础。这样做的原因纯粹是为了减少冗余,根据项目的不同,这可能并不理想,但这里效果很好。
接下来我们需要的是一个根视图模型,它将直接绑定到我们的视图。
namespace WpfApplication8
{
public class ViewModel : ObservableObject
{
public ViewModel()
{
MyBooks = new BookCollection(this, "MyBooks");
}
public BookCollection MyBooks { get; set; }
public Book NewBookTemplate { get; set; } = new Book();
}
}
请注意NewBookTemplate
字段。您可能想知道为什么我没有将CMD_AddBookToCollection
的参数直接推送到集合中。此模式中的参数只是一个模板,一个抛弃对象,用于将视图中的输入传递到视图模型中。这给了我们两个非常重要的自由:我们可以在将对象传递到我们的集合之前清理对象,并且我们可以避免为#34添加一本特殊的视图模型添加一本新书&#34;形成。 Book
类本身可以充当视图模型,既可以节省我们的工作,又可以让我们自由地在任何其他视图中重用该对象。现在我们只需要实现一个基本视图并对其进行全面测试。
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication8"
x:Name="main_window"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<StackPanel.Resources>
<Style TargetType="FrameworkElement" x:Key="default_styles">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="30" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5" />
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource default_styles}" >
<Setter Property="Width" Value="50" />
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource default_styles}" />
<Style TargetType="Button" BasedOn="{StaticResource default_styles}" />
</StackPanel.Resources>
<TextBlock Text="Title: " />
<TextBox Text="{Binding NewBookTemplate.Title, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Author: " />
<TextBox Text="{Binding NewBookTemplate.Author, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Add Book " Command="{Binding MyBooks.AddBook}" CommandParameter="{Binding NewBookTemplate}" />
<Button Content="Remove Book " Command="{Binding MyBooks.RemoveBook}" CommandParameter="{Binding MyBooks.SelectedItem}" />
<TextBlock Text="Count: " />
<TextBlock Text="{Binding MyBooks.Count}" />
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding MyBooks}" SelectedItem="{Binding MyBooks.SelectedItem, UpdateSourceTrigger=PropertyChanged}" >
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding MyBooks.RemoveBook}" CommandParameter="{Binding MyBooks.SelectedItem}" />
</ListBox.InputBindings>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Author}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
嗯,一切都在我的身上。我希望这个例子有助于使MVVM
更容易消化。快乐的编码!