更新绑定属性不会更新WPF中的视图

问题描述 投票:0回答:1

我正在使用MVVM模式。我建立了一个简单的应用程序,其中仅包括-一个ListView,它显示UserControl的简单集合,-一个按钮,将新项目添加到ListView和-a ContextMenu,该菜单应允许用户编辑SelectedItem。


问题:

我有两个绑定到我的View的ICommand。 ItemBeginEditItemEndEdit。在整个应用程序中都可以正确调用它们,并且可以正确传递参数。经过测试。每个命令的CommandParameter是ListView.SelectedItem(UserControl)的绑定源。明确说明:-ListView.ItemSource = Models.Item的集合-ListView.DataTemplate =UserControls.Item-UserControls.Item.Source =类型DependencyPropertyModels.Item。更改整个Collection时,ListView正确显示我的所有项目。当我从reference获得Models.ItemListView.SelectedItem并在ICommand执行中更改其属性之一时,它不会在UI上更新。我注意到,如果我在ConstructorViewModel中编辑集合,则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();
        }
    }
}
wpf xaml user-interface mvvm bind
1个回答
0
投票

替换

public class CustomItem

作者

public class CustomItem : INotifyPropertyChanged

[除此之外,似乎您假设只要所引用的DataSource类的属性发生更改,就会设置Item控件的CustomItem属性。事实并非如此。

您应该删除DataSource属性,并添加绑定到视图模型项属性的属性,如下所示:

<ListView.ItemTemplate>
    <DataTemplate>
        <StackPanel>
            <uc:Item Text="{Binding Name}" ... />
            ...
        </StackPanel>
    </DataTemplate>
</ListView.ItemTemplate>
© www.soinside.com 2019 - 2024. All rights reserved.