C# 在一个表单中编辑列表中的项目-MVVM方法

C# 在一个表单中编辑列表中的项目-MVVM方法,c#,.net,wpf,mvvm,C#,.net,Wpf,Mvvm,编辑2: 我正在做一个项目,其中我的组合框包含一个可编辑元素列表。选择其中一个元素后,这些元素可编辑。我想要的是,当我取消选择所选元素时,它的编辑属性会被记住,并且组合框下拉列表会用新元素的描述更新。下面是这个示例的样子: 现在,切换后会记住更改,但下拉列表仍然包含旧名称,并且不会自动更新: 这是密码 main window.xaml <Window x:Class="MVVMEditComboBoxItemsSample.MainWindow" xmlns="http

编辑2: 我正在做一个项目,其中我的组合框包含一个可编辑元素列表。选择其中一个元素后,这些元素可编辑。我想要的是,当我取消选择所选元素时,它的编辑属性会被记住,并且组合框下拉列表会用新元素的描述更新。下面是这个示例的样子:

现在,切换后会记住更改,但下拉列表仍然包含旧名称,并且不会自动更新:

这是密码

main window.xaml

<Window x:Class="MVVMEditComboBoxItemsSample.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:MVVMEditComboBoxItemsSample"
        mc:Ignorable="d"
        Title="MVVM ComboBox edit sample" Height="372.52" Width="415.214">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50*"></RowDefinition>
            <RowDefinition Height="81*"/>
            <RowDefinition Height="26*"/>
            <RowDefinition Height="45*"/>
            <RowDefinition Height="26*"/>
            <RowDefinition Height="44*"/>

        </Grid.RowDefinitions>
        <ComboBox Grid.Row="0" Margin="10" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" ItemsSource="{Binding Path=Things}" SelectedItem="{Binding Path=SelectedThing, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></ComboBox>
        <StackPanel Grid.Row="1" Margin="10">
            <Button Command="{Binding Path=AddCommand}">Add Item</Button>
            <Button Command="{Binding Path=CloneCommand}">Clone Item</Button>
            <Button Command="{Binding Path=DeleteCommand}">Delete Item</Button>
        </StackPanel>
        <Label Grid.Row="2" Margin="0"  >Name</Label>
        <TextBox Grid.Row="3" HorizontalAlignment="Stretch" Margin="10"  VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Text="{Binding Path=Name}"/>
        <Label Grid.Row="4" Margin="0"  >Price</Label>
        <TextBox Grid.Row="5" HorizontalAlignment="Stretch" Margin="10" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Text="{Binding Path=Price}"/>

    </Grid>
</Window>
ThingDataManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMEditComboBoxItemsSample.MockModel
{
    class ThingDataManager
    {
        private static ThingDataManager _instance;

        public static ThingDataManager Instance
        {
            get
            {
                if (_instance==null)
                {
                    _instance = new ThingDataManager();
                }
                return _instance;
            }
        }

        private ThingDataManager()
        {
        }

        public List<Thing> GetThings()
        {
            List<Thing> things = new List<Thing>();
            things.Add(new Thing { Name = "Book", Price = "12$" });
            things.Add(new Thing { Name = "Hammer", Price = "2$" });
            things.Add(new Thing { Name = "Fridge", Price = "1200$" });

            return things;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
命名空间MVVMEditComboxItemsSample.MockModel
{
类ThingDataManager
{
私有静态ThingDataManager_实例;
公共静态ThingDataManager实例
{
得到
{
if(_instance==null)
{
_instance=newthingdatamanager();
}
返回_实例;
}
}
私有ThingDataManager()
{
}
公共列表GetThings()
{
列出事物=新列表();
添加(新事物{Name=“Book”,Price=“12$”});
添加(新事物{Name=“Hammer”,Price=“2$”});
添加(新事物{Name=“冰箱”,Price=“1200$”});
归还物品;
}
}
}
我希望像ViewModelBase和CommandBase这样的其余部分足够自我描述:)

你知道为什么我的下拉列表没有用当前的事物名称更新吗

编辑: 好的,我在github上上传了简化版:

github.com/piotr-napadlek/mvvmeditcomboxItemsSample


仍然不起作用的是,当更改Name textbox的值时,ComboBox下拉列表的项不会更改。另外,请让我知道这个例子中的一般方法是否正确。干杯。

如果您查看本文中的示例项目,它将为您提供MVVM应用程序的良好体系结构的概念和想法。它使用的是Silverlight,它只是WPF的精简版本:


好吧,看来我把事情弄得太复杂了。将对象类的属性映射到ViewModel中的另一个属性是多余的。实际上,将WPF窗口上的文本框直接映射到SelectedThing.Name和SelectedThing.Price,并调用仅在SelectedThing上更改的属性,就足以解决我的问题。以下是工作版本:

视图模型:

using MVVMEditComboBoxItemsSample.MockModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MVVMEditComboBoxItemsSample
{
    class MainViewModel : ViewModelBase
    {
        private ObservableCollection<Thing> things;
        private Thing selectedThing;

    private ICommand addCommand;
    private ICommand cloneCommand;
    private ICommand deleteCommand;

    public MainViewModel()
    {
        Things = new ObservableCollection<Thing>(ThingDataManager.Instance.GetThings());
        SelectedThing = Things.FirstOrDefault();
    }

    public ObservableCollection<Thing> Things
    {
        get
        {
            return things;
        }

        set
        {
            things = value;
            OnPropertyChanged(nameof(Things));
        }
    }

    public Thing SelectedThing
    {
        get
        {
            return selectedThing;
        }

        set
        {
            selectedThing = value;
            OnPropertyChanged(nameof(SelectedThing));
        }
    }

    public ICommand AddCommand
    {
        get
        {
            if(addCommand==null)
            {
                addCommand = new CommandBase(i => AddItem(), null);
            }
            return addCommand;
        }
    }

    public ICommand CloneCommand
    {
        get
        {
            if(cloneCommand==null)
            {
                cloneCommand = new CommandBase(i => CloneItem(), i => SelectedThing!=null);
            }
            return cloneCommand;
        }
    }

    public ICommand DeleteCommand
    {
        get
        {
            if(deleteCommand==null)
            {
                deleteCommand = new CommandBase(i => DeleteItem(), i => SelectedThing!=null);
            }
            return deleteCommand;
        }
    }

    public void AddItem()
    {
        Thing newThing = new Thing();
        Things.Add(newThing);
        SelectedThing = newThing;
    }

    public void CloneItem()
    {
        Thing clonedThing = new Thing();
        clonedThing.Name = SelectedThing.Name + " - copy";
        clonedThing.Price = SelectedThing.Price;
        Things.Add(clonedThing);
        SelectedThing = clonedThing;
    }

    public void DeleteItem()
    {
        Thing tempThing = new Thing();
        tempThing = SelectedThing;
        if (Things.IndexOf(SelectedThing) != 0)
        {
            SelectedThing = Things.FirstOrDefault();
        }
        else if (Things.Count==1)
        {
            SelectedThing = null;
        }
        else
        {
            SelectedThing = Things[1];
        }

        Things.Remove(tempThing);
    }
}
使用mvvmeditcomboxItemsSample.MockModel;
使用制度;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows.Input;
命名空间MVVMEditComboxItemsSample
{
类MainViewModel:ViewModelBase
{
私人可观察的收集物品;
私事;
私有ICommand addCommand;
私人ICommand cloneCommand;
私有ICommand deleteCommand;
公共主视图模型()
{
Things=newobserveCollection(ThingDataManager.Instance.GetThings());
SelectedThing=Things.FirstOrDefault();
}
公众可观察到的收集物品
{
得到
{
归还物品;
}
设置
{
事物=价值;
财产变更(事物名称);
}
}
公共物品选择
{
得到
{
返回所选内容;
}
设置
{
选择的内容=值;
关于已更改的属性(名称(所选内容));
}
}
公共ICommand AddCommand
{
得到
{
if(addCommand==null)
{
addCommand=newcommandbase(i=>AddItem(),null);
}
返回addCommand;
}
}
公共ICommand CloneCommand
{
得到
{
if(cloneCommand==null)
{
cloneCommand=newcommandbase(i=>CloneItem(),i=>SelectedThing!=null);
}
返回克隆命令;
}
}
公共ICommand delete命令
{
得到
{
如果(deleteCommand==null)
{
deleteCommand=new CommandBase(i=>DeleteItem(),i=>SelectedThing!=null);
}
返回删除命令;
}
}
公共无效附加项()
{
新事物;
添加(newThing);
所选内容=新内容;
}
公共空克隆项()
{
事物克隆事物=新事物();
clonedThing.Name=SelectedThing.Name+“-copy”;
clonedThing.Price=选择的thing.Price;
事物。添加(克隆事物);
SelectedThing=clonedThing;
}
公共无效删除项()
{
事物诱惑=新事物();
诱惑=选择的事物;
if(Things.IndexOf(SelectedThing)!=0)
{
SelectedThing=Things.FirstOrDefault();
}
else if(Things.Count==1)
{
SelectedThing=null;
}
其他的
{
SelectedThing=事物[1];
}
事物。移除(诱惑);
}
}
}

主视图:

<Window x:Class="MVVMEditComboBoxItemsSample.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:MVVMEditComboBoxItemsSample"
    mc:Ignorable="d"
    Title="MVVM ComboBox edit sample" Height="372.52" Width="415.214">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50*"></RowDefinition>
        <RowDefinition Height="81*"/>
        <RowDefinition Height="26*"/>
        <RowDefinition Height="45*"/>
        <RowDefinition Height="26*"/>
        <RowDefinition Height="44*"/>

    </Grid.RowDefinitions>
    <ComboBox Grid.Row="0" Margin="10" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" ItemsSource="{Binding Path=Things}" SelectedItem="{Binding Path=SelectedThing, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" DisplayMemberPath="Name"></ComboBox>
    <StackPanel Grid.Row="1" Margin="10">
        <Button Command="{Binding Path=AddCommand}">Add Item</Button>
        <Button Command="{Binding Path=CloneCommand}">Clone Item</Button>
        <Button Command="{Binding Path=DeleteCommand}">Delete Item</Button>
    </StackPanel>
    <Label Grid.Row="2" Margin="0"  >Name</Label>
    <TextBox Grid.Row="3" HorizontalAlignment="Stretch" Margin="10"  VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Text="{Binding Path=SelectedThing.Name, UpdateSourceTrigger=PropertyChanged}"/>
    <Label Grid.Row="4" Margin="0"  >Price</Label>
    <TextBox Grid.Row="5" HorizontalAlignment="Stretch" Margin="10" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Text="{Binding Path=SelectedThing.Price}"/>

</Grid>

添加项
克隆项目
删除项目
名称
价格

using MVVMEditComboBoxItemsSample.MockModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MVVMEditComboBoxItemsSample
{
    class MainViewModel : ViewModelBase
    {
        private ObservableCollection<Thing> things;
        private Thing selectedThing;

    private ICommand addCommand;
    private ICommand cloneCommand;
    private ICommand deleteCommand;

    public MainViewModel()
    {
        Things = new ObservableCollection<Thing>(ThingDataManager.Instance.GetThings());
        SelectedThing = Things.FirstOrDefault();
    }

    public ObservableCollection<Thing> Things
    {
        get
        {
            return things;
        }

        set
        {
            things = value;
            OnPropertyChanged(nameof(Things));
        }
    }

    public Thing SelectedThing
    {
        get
        {
            return selectedThing;
        }

        set
        {
            selectedThing = value;
            OnPropertyChanged(nameof(SelectedThing));
        }
    }

    public ICommand AddCommand
    {
        get
        {
            if(addCommand==null)
            {
                addCommand = new CommandBase(i => AddItem(), null);
            }
            return addCommand;
        }
    }

    public ICommand CloneCommand
    {
        get
        {
            if(cloneCommand==null)
            {
                cloneCommand = new CommandBase(i => CloneItem(), i => SelectedThing!=null);
            }
            return cloneCommand;
        }
    }

    public ICommand DeleteCommand
    {
        get
        {
            if(deleteCommand==null)
            {
                deleteCommand = new CommandBase(i => DeleteItem(), i => SelectedThing!=null);
            }
            return deleteCommand;
        }
    }

    public void AddItem()
    {
        Thing newThing = new Thing();
        Things.Add(newThing);
        SelectedThing = newThing;
    }

    public void CloneItem()
    {
        Thing clonedThing = new Thing();
        clonedThing.Name = SelectedThing.Name + " - copy";
        clonedThing.Price = SelectedThing.Price;
        Things.Add(clonedThing);
        SelectedThing = clonedThing;
    }

    public void DeleteItem()
    {
        Thing tempThing = new Thing();
        tempThing = SelectedThing;
        if (Things.IndexOf(SelectedThing) != 0)
        {
            SelectedThing = Things.FirstOrDefault();
        }
        else if (Things.Count==1)
        {
            SelectedThing = null;
        }
        else
        {
            SelectedThing = Things[1];
        }

        Things.Remove(tempThing);
    }
}
<Window x:Class="MVVMEditComboBoxItemsSample.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:MVVMEditComboBoxItemsSample"
    mc:Ignorable="d"
    Title="MVVM ComboBox edit sample" Height="372.52" Width="415.214">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50*"></RowDefinition>
        <RowDefinition Height="81*"/>
        <RowDefinition Height="26*"/>
        <RowDefinition Height="45*"/>
        <RowDefinition Height="26*"/>
        <RowDefinition Height="44*"/>

    </Grid.RowDefinitions>
    <ComboBox Grid.Row="0" Margin="10" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" ItemsSource="{Binding Path=Things}" SelectedItem="{Binding Path=SelectedThing, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" DisplayMemberPath="Name"></ComboBox>
    <StackPanel Grid.Row="1" Margin="10">
        <Button Command="{Binding Path=AddCommand}">Add Item</Button>
        <Button Command="{Binding Path=CloneCommand}">Clone Item</Button>
        <Button Command="{Binding Path=DeleteCommand}">Delete Item</Button>
    </StackPanel>
    <Label Grid.Row="2" Margin="0"  >Name</Label>
    <TextBox Grid.Row="3" HorizontalAlignment="Stretch" Margin="10"  VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Text="{Binding Path=SelectedThing.Name, UpdateSourceTrigger=PropertyChanged}"/>
    <Label Grid.Row="4" Margin="0"  >Price</Label>
    <TextBox Grid.Row="5" HorizontalAlignment="Stretch" Margin="10" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Text="{Binding Path=SelectedThing.Price}"/>

</Grid>