我正在使用MVVM模式。我建立了一个简单的应用程序,其中仅包括-一个ListView,它显示UserControl的简单集合,-一个按钮,将新项目添加到ListView和-a ContextMenu,该菜单应允许用户编辑SelectedItem。
我有两个绑定到我的View的ICommand。 ItemBeginEdit
和ItemEndEdit
。在整个应用程序中都可以正确调用它们,并且可以正确传递参数。经过测试。每个命令的CommandParameter是ListView.SelectedItem(UserControl)的绑定源。明确说明:-ListView.ItemSource
= Models.Item
的集合-ListView.DataTemplate
=UserControls.Item
-UserControls.Item.Source
=类型DependencyProperty
的Models.Item
。更改整个Collection
时,ListView
正确显示我的所有项目。当我从reference
获得Models.Item
的ListView.SelectedItem
并在ICommand执行中更改其属性之一时,它不会在UI上更新。我注意到,如果我在Constructor
的ViewModel
中编辑集合,则UI仅在该时间更新。如果我尝试通过ICommand
将其改回,则什么也不会发生。INotifyPropertyChanged
被实现为相关类。我仍在学习WPF和XAML。我想念什么?
-MainWindow.xaml(查看)
<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}" />
<Button x:Name="btnEditItem"
Content="Edit"
Width="150"
Command="{Binding ItemBeginEdit}"
CommandParameter="{Binding SelectedItem}" />
<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}">
</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>
-MainVM.cs(ViewModel)
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);
}
}
}
-CustomItem.cs(模型)
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();
}
}
}
}
}
-CmdItemBeginEdit.cs(ICommand)
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;
}
}
}
-CmdItemEndEdit.cs(ICommand)
using System;
using System.Windows.Input;
namespace Demo.ViewModels.Commands
{
public class CmdItemEndEdit : 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 = false;
}
#endregion
//Properties
public MainVM VM { get; set; }
//Constructors
public CmdItemEndEdit(MainVM VM)
{
this.VM = VM;
}
}
}
-Item.xaml(UserControl)
<UserControl x:Class="Demo.Views.UserControls.Item"
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:Demo.Views.UserControls"
mc:Ignorable="d"
d:DesignHeight="45"
d:DesignWidth="90">
<StackPanel HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBlock x:Name="tblockName"
Text="Name"
Margin="2"
FontSize="15"
VerticalAlignment="Center" />
<TextBox x:Name="tboxInput"
Text="Name"
Visibility="Collapsed"
Margin="2"
FontSize="14"
FontStyle="Italic"
VerticalAlignment="Center">
</TextBox>
</StackPanel>
</UserControl>
-Item.xaml.cs(UserControl)
using System.Windows;
using System.Windows.Controls;
namespace Demo.Views.UserControls
{
public partial class Item : UserControl
{
#region DependencyProperty - DataSource
public Models.CustomItem DataSource
{
get { return (Models.CustomItem)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(Models.CustomItem), typeof(Item), new PropertyMetadata(null, SetDataSource));
private static void SetDataSource(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as Item;
var value = e.NewValue as Models.CustomItem;
if (value == null)
{
control.tblockName.Text = string.Empty;
control.tblockName.Visibility = Visibility.Collapsed;
control.tboxInput.Visibility = Visibility.Visible;
}
else
{
control.tblockName.Text = value.Name;
if (value.IsEditing)
{
control.tblockName.Visibility = Visibility.Collapsed;
control.tboxInput.Visibility = Visibility.Visible;
}
else
{
control.tboxInput.Visibility = Visibility.Collapsed;
control.tblockName.Visibility = Visibility.Visible;
}
}
}
#endregion
//Constructors
public Item()
{
InitializeComponent();
}
}
}
替换
public class CustomItem
作者
public class CustomItem : INotifyPropertyChanged
[除此之外,似乎您假设只要所引用的DataSource
类的属性发生更改,就会设置Item
控件的CustomItem
属性。事实并非如此。
您应该删除DataSource
属性,并添加绑定到视图模型项属性的属性,如下所示:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<uc:Item Text="{Binding Name}" ... />
...
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>