我正在学习C#WPF,此刻我面临一个问题。
应用程序:
我正在玩MVVM模式。
我建立了一个简单的应用程序,其中仅包含
-一个ListView,显示UserControl的简单集合,
-一个将新项目添加到ListView和
的按钮
-a ContextMenu,该菜单应允许用户编辑SelectedItem。
问题:
我的ViewModel中有一个SelectedItem属性,它作为commandparameter传递给我的一个Commands。更具体地说,应该允许用户在ListView.SelectedItem UserControl中编辑文本的CmdItemBeginEdit。
我已经在TwoWay模式下将属性绑定到ListView.SelectedItem,并且还在相应控件中将属性绑定为commandparameter。
事实是,虽然在选择ListView项时正确设置了SelectedItem,但要执行Command的时间到了,它变为null。
为什么会这样呢?
更新#1
我找到了一些与我的问题有关的信息。它与Command或类似命令的执行顺序有关,似乎是.NET Framework错误(该死的microsoft)。
其他成员建议在XAML中在Command之前定义commandparameter,并首先将Command的IsAsync属性启用为true。我都尝试过,但都没用。
你听到过类似的话吗?
代码:
请注意,已经实现了许多技术和先决条件,例如ICommand(命令和commandparameter),模型和ViewModel上的INotifyPropertyChanged,ObservableCollection,Binding等。
我的项目包括:
- 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"
mc:Ignorable="d"
Title="Demo"
Height="300"
Width="300"
WindowStartupLocation="CenterScreen">
<!--To apply to Window level-->
<Window.DataContext>
<vm:MainVM />
</Window.DataContext>
<Window.Resources>
<!--For referencing-->
<vm:MainVM x:Key="VM" />
</Window.Resources>
<StackPanel>
<Button x:Name="btnAddItem"
Content="Add"
Width="150"
Command="{Binding ItemNew}" />
<ListView x:Name="lviewColelction"
Width="150"
MinHeight="100"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<uc:Item DataSource="{Binding}" />
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename"
Command="{Binding Source={StaticResource VM},Path=ItemBeginEdit}"
commandparameter="{Binding Source={StaticResource VM},Path=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
{
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string PropertyName = "")
{
var args = new PropertyChangedEventArgs(PropertyName);
PropertyChanged?.Invoke(this,args);
}
#endregion
//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
{
#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;
}
}
}
- CmdItemNew.cs (ICommand)
using System;
using System.Windows.Input;
namespace Demo.ViewModels.Commands
{
/// <summary>
/// We are going to assign a Command for each Button.
/// </summary>
public class CmdItemNew : 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)
{
VM.CreateItem();
VM.ReadItems();
}
#endregion
//Properties
public MainVM VM { get; set; }
//Constructor
public CmdItemNew(MainVM VM)
{
this.VM = VM;
}
}
}