WPF MVVM视图时的ViewModels性能
我有一个单独的细节的树状视图通过PRISM库注射时,我对我的树型视图的一个点击(我可以更新我的项目的所有属性它)。我的所有物品都有启用属性。
问题:
- 当我更新我的编程产权的ViewModels,我的对象被更新。如果我点击其他treeviewitem并返回到第一个,我会看到该属性已更新。
- 当我启用/禁用使用我的详细信息视图的项目(前景变灰,属性发生变化)时,所有更新都很好
- 但在我的情况下,当我尝试通过由一个contextMenu它不会触发视图和所有更新...但我的viewmodel属性更新...
我该怎么去错了?
我在我的treeview中使用ObservableCollection,也许我需要更改我的集合的类型?
我有我的BaseViewModel谁实现NotifyPropertyChanged
public abstract class NotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(Expression<Func<object>> propertyExpression)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(GetPropertyName(propertyExpression)));
}
private string GetPropertyName(Expression<Func<object>> propertyExpression)
{
var unaryExpression = propertyExpression.Body as UnaryExpression;
var memberExpression = unaryExpression == null ? (MemberExpression)propertyExpression.Body : (MemberExpression)unaryExpression.Operand;
var propertyName = memberExpression.Member.Name;
return propertyName;
}
}
因此,我调用属性变化的方法,但为什么我的看法是没有,那么更新?
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool Enabled
{
get
{
return Model.Enabled;
}
set
{
if (value != Model.Enabled)
{
Model.Enabled = value;
OnPropertyChanged(() => Model.Enabled);
}
}
}
这是我的看法的代码(命令)
<MenuItem Header="Enable/Disable this equipment" Command="{Binding PlacementTarget.Tag.DataContext.ToogleEquipmentCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
CommandParameter="{Binding}" InputGestureText="CTRL+D"/>
这里是我认为的代码(分层数据模板从我的树视图)
<!-- ModuleItems > IP/Name -->
<HierarchicalDataTemplate DataType="{x:Type siemens:ModuleItemSiemensViewModel}" >
<StackPanel Orientation="Horizontal">
<TextBlock Name="ItemIp"
Text="{Binding Path=Ip}" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="/" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Background" Value="LightGray"/>
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Name="ItemName" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"
Text="{Binding Path=Name}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
编辑:
这是我的viewmodel和模型: 我真正的问题是当我更新一个项目(与我的属性启用)它更新项目,但我的列表(ModuleItems)没有更新,我需要做什么来正确实现MVVM,并使我的领域自动更新?
public class ModuleParamSiemensViewModel : ModuleParamBaseViewModel
{
#region Attributes
private ObservableCollection<ModuleItemSiemensViewModel> _moduleItems;
private ModuleParamSiemens _model;
private string _moduleType;
#endregion
#region Constructor
public ModuleParamSiemensViewModel(ModuleParamSiemens moduleParam) : base(moduleParam)
{
this.Model = moduleParam;
this.ModuleType = "Siemens";
ModuleItems = new ObservableCollection<ModuleItemSiemensViewModel>();
Initialize();
}
#endregion
#region Properties
public new ModuleParamSiemens Model
{
get
{
return _model;
}
set
{
if (value != _model)
{
_model = value;
OnPropertyChanged(() => Model);
}
}
}
public new ObservableCollection<ModuleItemSiemensViewModel> ModuleItems
{
get
{
return _moduleItems;
}
set
{
this._moduleItems = value;
OnPropertyChanged(() => ModuleItems);
}
}
public override string ModuleType
{
get
{
return _moduleType;
}
set
{
this._moduleType = value;
OnPropertyChanged(() => ModuleType);
}
}
#endregion
#region Public Methods
public void Initialize()
{
foreach (ModuleItemSiemens item in this.Model.ModuleItems)
{
Add(new ModuleItemSiemensViewModel(item));
}
}
public void Add(ModuleItemSiemensViewModel item)
{
ModuleItems.Add(item);
}
#endregion
}
型号:
public class ModuleParamSiemens : ModuleParam
{
public new ObservableCollection<ModuleItemSiemens> ModuleItems { get; set; }
public ModuleParamSiemens()
{
ModuleItems = new ObservableCollection<ModuleItemSiemens>();
}
}
编辑2:
添加ItemSiemensViewModel
public class ItemSiemensViewModel : ItemBaseViewModel
{
#region Attributes
private ItemSiemens _model;
#endregion
#region Constructor
public ItemSiemensViewModel(ItemSiemens item)
{
this.Model = item;
}
#endregion
#region Properties
public new ItemSiemens Model
{
get
{
return _model;
}
set
{
if (value != _model)
{
_model = value;
OnPropertyChanged(() => Model);
}
}
}
public new OPCInfo Opc
{
get
{
return Model.Opc;
}
set
{
if (value != Model.Opc)
{
Model.Opc = value;
OnPropertyChanged(() => Model.Opc);
}
}
}
public ProtocolInfoSiemens Protocol
{
get
{
return Model.Protocol;
}
set
{
if (value != Model.Protocol)
{
Model.Protocol = value;
OnPropertyChanged(() => Model.Protocol);
}
}
}
#endregion
#region Public Methods
#endregion
}
ItemSiemens:
public class ItemSiemens : Item
{
public ProtocolInfoSiemens Protocol { get; set; }
}
ItemBaseViewModel
public abstract class ItemBaseViewModel : BaseViewModel
{
public OPCInfoBaseViewModel Opc { get; set; }
public ItemBaseViewModel()
{
}
}
项目
public abstract class Item
{
public OPCInfo Opc { get; set; }
}
我找到了答案。
我的绑定是正确的(或至少它的工作原理)
的问题是,我用的ObservableCollection集合,当一个产品更新这个集合中它甚至没有发出一个事件地说,事情已经改变(其确实为添加和删除项目)
所以我已经实现了我自己的ItemsChangeObservableCollection(你可以看看这个答案:https://*.com/a/33866549/8237280)
现在所有我在我所有的应用程序问题都解决了!
你对你的ModuleItemSiemensViewModel
发送INotifyPropertyChanged的该属性Model.Enabled
。这并没有太大的意义,因为没有人在虚拟机(ModuleItemSiemensViewModel
)上列出此更改。 INPC接口不允许这种更新。每个控件监听与其绑定属性相同的对象。这意味着您只能发送属性PropertyChanged,该属性位于声明该接口的同一个类/实例中。
您必须将NotifyPropertyChanged
移动到“模型”实例并在那里调用它。像这样:
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool Enabled
{
get
{
return Model.Enabled;
}
set
{
if (value != Model.Enabled)
{
Model.Enabled = value;
Model.OnPropertyChanged(() => Enabled);
}
}
}
'OnPropertyChanged()'所做的就是使用传递表达式中属性的名称。 “Model.Enabled”属性的名称与“Enabled”属性的名称相同,因此尽管我同意实现不正确,但似乎并不能解释缺少更新。 –
感谢您的评论,我不明白为什么我的实施不正确?当我更改ViewModel时,我需要反映我的模型中的更改吗?在这里,我发现我的错误,当我使用集合时,viewmodel类型与模型类型不同,我不知道如何在我的模型中反映它(我从我的视图更新视图模型,但我不知道如何做到这一点...)我会更新我的帖子与集合。 – Karakayn
@PeterDuniho是的,你是对的。发送的“名称”是相同的,但是在OnPropertyChange方法被调用的对象上非常重要。但你对OP的帖子的评论也是正确的。Xaml代码没有意义,因为所有绑定都指向:他没有包含在代码示例中的:sIiemens:ModuleItemSiemensViewModel.Enabled 。 – JPVenson
没有一个好的[mcve]可以可靠地重现问题,所以不可能知道所有可能的错误。也就是说,使用'Expression'来获取属性名称是毫无意义的。你应该使用'nameof'运算符或([IMHO好得多]'[CallerMemberName]'属性来获取被更新的属性的名称。不是说这里有任何迹象表明这会改变你的问题,但你应该修正它(也许它会解决你的问题)。 –