我正在玩MVVM模式。
我建立了一个简单的应用程序,其中仅包含
-一个ListView,显示UserControl的简单集合,
-一个将新项目添加到ListView和
的按钮
-a ContextMenu,该菜单应允许用户编辑SelectedItem。
我有两个绑定到我的View的ICommands。 ItemBeginEdit
和ItemEndEdit
。
在整个应用程序中都可以正确调用它们,并且可以正确传递参数。经过测试。
每个命令的CommandParameter是ListView.SelectedItem(UserControl)的绑定源。
明确说明:
-ListView.ItemSource
= Models.Item
的集合
-ListView.DataTemplate
= UserControls.Item
-UserControls.Item.Source
=类型DependencyProperty
的{{1}}。
更改整个Models.Item
时,Collection
正确显示我的所有物品。
当我从ListView
获得reference
的{{1}}并在ICommand执行中更改其属性之一时,它不会在UI上更新。
我注意到,如果我在Models.Item
的{{1}}中编辑集合,则UI仅在该时间更新。
如果我尝试通过ListView.SelectedItem
将其改回,则什么也不会发生。
Constructor
已实现到相关类。
我仍在学习WPF和XAML。我想念什么?
- MainWindow.xaml (查看)
ViewModel
- MainVM.cs (ViewModel)
ICommand
- CustomItem.cs (模型)
INotifyPropertyChanged
- CmdItemBeginEdit.cs (ICommand)
<Window x:Class="Demo.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:Demo"
xmlns:uc="clr-namespace:Demo.Views.UserControls"
xmlns:vm="clr-namespace:Demo.ViewModels"
xmlns:b="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
x:Name="windowMain"
Title="Demo"
Height="300"
Width="300"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:MainVM />
</Window.DataContext>
<StackPanel>
<Button x:Name="btnAddItem"
Content="Add"
Width="150"
Command="{Binding ItemNew}" />
<ListView x:Name="lviewCollection"
Width="150"
MinHeight="100"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<b:Interaction.Triggers>
<b:EventTrigger EventName="LostFocus">
<b:InvokeCommandAction Command="{Binding ItemEndEdit}"
CommandParameter="{Binding SelectedItem}" />
</b:EventTrigger>
</b:Interaction.Triggers>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<uc:Item DataSource="{Binding}"
IsEditing="{Binding IsEditing}">
</uc:Item>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding Source={x:Reference Name=windowMain}, Path=DataContext}">
<MenuItem Header="Rename"
Command="{Binding ItemBeginEdit}"
CommandParameter="{Binding SelectedItem}" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
- CmdItemEndEdit.cs (ICommand)
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Demo.ViewModels
{
public class MainVM : INotifyPropertyChanged
{
//INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string PropertyName = "")
{
var args = new PropertyChangedEventArgs(PropertyName);
PropertyChanged?.Invoke(this, args);
}
//Properties
public ObservableCollection<Models.CustomItem> Items { get; set; }
private Models.CustomItem selectedItem;
public Models.CustomItem SelectedItem
{
get { return selectedItem; }
set
{
if (value == null)
return;
if (value == selectedItem)
return;
selectedItem = value;
OnPropertyChanged();
}
}
public Commands.CmdItemNew ItemNew { get; set; }
public Commands.CmdItemBeginEdit ItemBeginEdit { get; set; }
public Commands.CmdItemEndEdit ItemEndEdit { get; set; }
//Constructors
public MainVM()
{
Items = new ObservableCollection<Models.CustomItem>();
SelectedItem = null;
ItemNew = new Commands.CmdItemNew(this);
ItemBeginEdit = new Commands.CmdItemBeginEdit(this);
ItemEndEdit = new Commands.CmdItemEndEdit(this);
ReadItems();
}
//Methods
public void SetupItems()
{
//Just create demo items. Not used.
var items = DatabaseHelper.ReadItems();
int itemsToCreate = 3 - items.Length;
while (itemsToCreate > 0)
{
CreateItem();
itemsToCreate--;
}
}
public void CreateItem()
{
var item = new Models.CustomItem()
{
Name = "New Item"
};
DatabaseHelper.Insert(item);
}
public void ReadItems()
{
var items = DatabaseHelper.ReadItems();
Items.Clear();
foreach (var item in items)
Items.Add(item);
}
}
}
- Item.xaml (UserControl)
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using SQLite;
namespace Demo.Models
{
public class CustomItem : INotifyPropertyChanged
{
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string PropertyName = "")
{
if (PropertyChanged != null)
{
var args = new PropertyChangedEventArgs(PropertyName);
PropertyChanged.Invoke(this, args);
}
}
#endregion
//Properties
private int id;
[PrimaryKey, AutoIncrement]
public int Id
{
get { return id; }
set
{
if (value != id)
{
id = value;
OnPropertyChanged();
}
}
}
private string name;
public string Name
{
get { return name; }
set
{
if (value != name)
{
name = value;
OnPropertyChanged();
}
}
}
private bool isEditing;
[Ignore]
public bool IsEditing
{
get
{
return isEditing;
}
set
{
if (value != isEditing)
{
isEditing = value;
OnPropertyChanged();
}
}
}
}
}
- Item.xaml.cs (UserControl)
using System;
using System.Windows.Input;
namespace Demo.ViewModels.Commands
{
public class CmdItemBeginEdit : ICommand
{
#region ICommand implementation
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
var item = parameter as Models.CustomItem;
if (item == null)
return;
item.IsEditing = true;
}
#endregion
//Properties
public MainVM VM { get; set; }
//Constructors
public CmdItemBeginEdit(MainVM VM)
{
this.VM = VM;
}
}
}
答案 0 :(得分:0)
似乎您假设只要所引用的DataSource
类的属性发生更改,Item
控件的CustomItem
属性就会被设置。事实并非如此。
您应该删除DataSource
属性,并添加绑定到视图模型项属性的属性,如下所示:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<uc:Item Text="{Binding Name}" ... />
...
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
UserControl的XAML中的元素将绑定到其属性,例如
<TextBlock Text="{Binding Text,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
暴露一组UserControl属性的另一种方法是,将UserControl的XAML中的元素直接绑定到视图模型属性,例如
<TextBlock Text="{Binding Name}" ... />
然后ItemTemplate将仅包含以下内容:
<uc:Item />
但是,这会使UserControl依赖于视图模型类型(或更确切地说,依赖于公开等效属性集的任何类型)。