首页 > 解决方案 > WPF MVVM itemsControl 和 button 命令不起作用?

问题描述

我的 WPF MVVM 应用程序有问题。我有一个视图 (CaisseView.xaml) 和它的 ViewModel (CaisseViewModel.cs,它用作自另一个 .cs 以来调用的视图的 Datacontext)。在我看来,我正在使用 itemsControl 以便为模型的 observablecollection 的每个元素设置一个按钮。此外,在我看来,我有一个 ListView。我希望每次单击 itemsControl 的按钮时,我的 Lisview 中都会出现一个新行。最后一件事是它不起作用!使用调试我输入我的命令,该命令将一个对象添加到我的可观察集合中,但应用程序没有实现视图。

在我的 ViewModel 中,我使用了 INotifyPropertyChanged、RelayCommand 和 ICommand。

我让你我的代码。你能帮我解决我的问题吗???谢谢:

我的观点 :

<UserControl x:Class="LMarket.Views.CaisseView"
             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:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:local="clr-namespace:LMarket.Views"
             xmlns:localM="clr-namespace:LMarket.Models"
             xmlns:localModel="clr-namespace:LMarket.ViewModels"
             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
             mc:Ignorable="d">
    <UserControl.Resources>
        <localModel:CaisseViewModel x:Key="CaisseViewModel"/>
        <localM:ProduitDuStock x:Key="ProduitDuStockModel"/>
    </UserControl.Resources>
    <Grid Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Image Cursor="Hand" Source="/Resources/homepage.png" Width="32" Height="32" HorizontalAlignment="Left" Margin="15,10" Grid.Row="0">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseLeftButtonDown">
                    <i:InvokeCommandAction Command="{Binding DataContext.OnHomeCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Image>
        <Image Cursor="Hand" Source="/Resources/LogOut.png" Width="32" Height="32" HorizontalAlignment="Right" Margin="15,10" Grid.Row="0">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseLeftButtonDown">
                    <i:InvokeCommandAction Command="{Binding DataContext.BackConnectionCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Image>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Border Grid.Column="0" BorderThickness="2" BorderBrush="Azure" Margin="5,5,1,5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="50"/>
                        <RowDefinition Height="1*" />
                        <RowDefinition Height="50" />
                        <RowDefinition Height="50"/>
                    </Grid.RowDefinitions>
                    <Grid Grid.Row="0" Background="#FFC2E0ED">
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                            <Image Cursor="Hand" VerticalAlignment="Center"  Width="32" Height="32" Source="/Resources/files-and-folders.png"/>
                            <Image Cursor="Hand" VerticalAlignment="Center" Margin="10,0" Width="32" Height="32" Source="/Resources/cancel.png"/>
                        </StackPanel>
                    </Grid>
                    <ListView Grid.Row="1" Margin="5" SelectionMode="Single"
                              HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Path=LstProducts, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                        <ListView.ItemTemplate>
                            <DataTemplate DataType="localM:ProduitDuStock">
                                <StackPanel Margin="2" MinWidth="244">
                                    <StackPanel.Resources>
                                        <Style TargetType="{x:Type StackPanel}">
                                           <!-- <Style.Triggers>
                                                <DataTrigger Binding="{Binding Path=Selected}" Value="True">
                                                    <Setter Property="Background" Value="LightBlue"/>
                                                </DataTrigger>
                                        </Style.Triggers> -->
                                        </Style>
                                    </StackPanel.Resources>
                                    <DockPanel >
                                        <TextBlock FontWeight="Bold" Text="Produit: " DockPanel.Dock="Left" Margin="5,0,5,0"/>
                                        <TextBlock Text="  " />
                                        <TextBlock Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Foreground="Green" FontWeight="Bold" />
                                    </DockPanel>
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                    <DockPanel Grid.Row="2" Margin="5" Background="#FF296B88">
                        <Label Content="TOTAL" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" FontFamily="Rockwell Condensed" Foreground="#FFF2FF5A" />
                        <Label Content="$$" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" FontFamily="Rockwell Condensed" Foreground="#FFF2FF5A" />
                    </DockPanel>
                    <Grid Grid.Row="3" Background="#FFC2E0ED">
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2">
                            <Button Cursor="Hand" Width="80" Content="Cuenta %" Background="#FF98ADA8" BorderBrush="#FF4A6DFF" Margin="2,0">
                                <Button.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="8"/>
                                    </Style>
                                </Button.Resources>
                            </Button>
                            <Button Cursor="Hand" Width="80" Content="Producto %" Background="#FF98ADA8" BorderBrush="#FF4A6DFF" Margin="2,0">
                                <Button.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="8"/>
                                    </Style>
                                </Button.Resources>
                            </Button>
                            <Button Cursor="Hand" Width="80" Content="-1" Background="#FFDDCF00" BorderBrush="#FF4A6DFF" FontWeight="Bold" FontSize="20">
                                <Button.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="8"/>
                                    </Style>
                                </Button.Resources>
                            </Button>
                            <Button Cursor="Hand" Width="80" Content="+1" Background="#FF79C837" BorderBrush="#FF4A6DFF" FontWeight="Bold" FontSize="20" Margin="2,0">
                                <Button.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="8"/>
                                    </Style>
                                </Button.Resources>
                            </Button>
                            <Button Cursor="Hand" Width="auto" Content="  Validar para Pagar  " Command="{Binding AddNewProductCommand}" Background="#FF98ADA8" BorderBrush="#FF4A6DFF"  FontSize="19" Margin="80,0,2,0" Foreground="Black">
                                <Button.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="8"/>
                                    </Style>
                                </Button.Resources>
                            </Button>
                        </StackPanel>
                    </Grid>
                </Grid>
            </Border>
            <Border Grid.Column="1" BorderThickness="2" BorderBrush="Azure" Margin="1,5,5,5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" MaxHeight="720"/>
                        <RowDefinition Height="1" />
                    </Grid.RowDefinitions>
                    <ScrollViewer Grid.Row="0" Background="Transparent"
                                  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                        <ItemsControl ItemsSource="{Binding ListOfProducts}" MaxWidth="762">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Border Cursor="Hand" CornerRadius="8" Margin="4,3" BorderThickness="1.5" BorderBrush="#FFFDFF00">
                                        <Border.Background>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FF6F92DC" Offset="1"/>
                                                <GradientStop Color="#FFA6DBFF" Offset="0.534"/>
                                            </LinearGradientBrush>
                                        </Border.Background>
                                        <Button BorderBrush="Transparent" Background="Transparent" Command="{Binding Source={StaticResource CaisseViewModel}, Path=AddNewProductCommand}" CommandParameter="{Binding Id}">
                                            <Button.Resources>
                                                <Style TargetType="{x:Type Border}">
                                                    <Setter Property="CornerRadius" Value="8"/>
                                                </Style>
                                            </Button.Resources>
                                            <StackPanel Orientation="Vertical" Margin="2" Width="80" Height="80">

                                                <Image Source="{Binding ProductImageUrl}" Width="40" Height="40" Margin="0,10,0,5"
                                                   HorizontalAlignment="Center"/>

                                            <Label Content="{Binding Surname}" FontWeight="Bold" HorizontalAlignment="Center" FontSize="11"/>
                                        </StackPanel>
                                        </Button>
                                    </Border>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </ScrollViewer>
                </Grid>
            </Border>
        </Grid>
    </Grid>
</UserControl>

我的视图模型:

using LMarket.Models;
using LMarket.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml;

namespace LMarket.ViewModels
{
    class CaisseViewModel : INotifyPropertyChanged
    {
        //Event for INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] string str = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(str));
        }

        #region Attributs
        private ObservableCollection<ProduitDuStock> listOfProducts;
        private ObservableCollection<ProduitDuStock> lstProducts;
        private ProduitDuStock selectedProduct;
        #endregion
        #region Getter/Setter
        public ObservableCollection<ProduitDuStock> ListOfProducts
        {
            get
            {
                return listOfProducts;
            }
            set
            {
                if (listOfProducts != value)
                {
                    listOfProducts = value;
                }
                NotifyPropertyChanged();
            }
        }
        public ObservableCollection<ProduitDuStock> LstProducts
        {
            get
            {
                return lstProducts;
            }
            set
            {
                if (lstProducts != value)
                {
                    lstProducts = value;
                }
                NotifyPropertyChanged();
            }
        }
        public ProduitDuStock SelectedProduct
        {
            get
            {
                return selectedProduct;
            }
            set
            {
                if (selectedProduct != value)
                {
                    selectedProduct = value;
                    NotifyPropertyChanged();
                }
            }
        }

        #endregion


        #region Constructeur
        public CaisseViewModel()
        {
            ListOfProducts = new ObservableCollection<ProduitDuStock>();
            LstProducts = new ObservableCollection<ProduitDuStock>();
            toto();
            //Lecture du fichier ou son enregistrer les données du stock (En local sur un .xml)
            try
            {
                //Ouverture du document XML
                XmlDocument xmlDoc = new XmlDocument();
                string Path = Directory.GetCurrentDirectory().ToString();
                Path = Path + "\\Configuration\\Stockes.xml";
                xmlDoc.Load(Path);
                //Recupérer la section des produits
                XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes("/Stockes/Products/product");
                //Pour chaque produit dans le fichier, créer un objet ProduitDuStock et l'ajouter à l'ObservableCollection.
                foreach (XmlNode node in nodeList)
                {
                    //Creation du nouvel objet 
                    ProduitDuStock testProduit = new ProduitDuStock();
                    foreach (XmlAttribute att in node.Attributes)
                    {
                        //Récupération des parametres de l'objet.
                        switch (att.Name)
                        {
                            case "id":
                                testProduit.Id = int.Parse(att.Value);
                                break;
                            case "name":
                                testProduit.Name = att.Value;
                                break;
                            case "pays":
                                testProduit.Pays = att.Value;
                                break;
                            case "surname":
                                testProduit.Surname = att.Value;
                                break;
                            case "quantite":
                                testProduit.Quantite = int.Parse(att.Value);
                                break;
                            case "unitprice":
                                testProduit.UnitPrice = int.Parse(att.Value);
                                break;
                            case "productImageUrl":
                                testProduit.ProductImageUrl = att.Value;
                                break;
                            default:
                                break;
                        }
                    }
                    //Ajouter l'objet créé a notre ObservableCollection.
                    ListOfProducts.Add(testProduit);
                }

            }
            catch (Exception ex)
            {

            }
        }
        #endregion

        #region Command Definition
        //Event RelayCommand Definition
        private RelayCommand addProductToBill = null;
        public ICommand AddProductToBill
        {
            get
            {
                if (addProductToBill == null)
                {
                    addProductToBill = new RelayCommand(OnAddProductToBill);
                }
                return addProductToBill;
            }
        }
        private RelayCommand addNewProductCommand = null;
        public ICommand AddNewProductCommand
        {
            get
            {
                if (addNewProductCommand == null)
                {
                    addNewProductCommand = new RelayCommand(OnAddNewProductCommand);
                }
                return addNewProductCommand;
            }
        }
        #endregion

        #region Command function definition
        //Event lorsque le curseur rentre dans la zone du boutton "Stock"
        private void OnAddNewProductCommand(object obj)
        {
            toto();
        }

        private void OnAddProductToBill(object obj)
        {
            toto();
        }
        #endregion

        #region function
        public void toto()
        {
            ProduitDuStock myAddProductToBill = new ProduitDuStock();
            myAddProductToBill.Name = "trrt";
            LstProducts.Add(myAddProductToBill);
        }
        #endregion
    }
}

视图中的重要部分是下一个:

<Button BorderBrush="Transparent" Background="Transparent" Command="{Binding Source={StaticResource CaisseViewModel}, Path=AddNewProductCommand}" CommandParameter="{Binding Id}">

请记住,此按钮被定义为 itemsControl !所以按钮的Datacontext不是我的viewModel,这就是我使用staticResource的原因......

我希望对我有帮助:) 非常感谢。

标签: wpfmvvmcommanddatacontextitemscontrol

解决方案


欢迎来到 SO!为了将来参考,如果您发布MCVE ,您将有更好的机会回答您的问题,您发布的内容中有足够多的不必要的代码让大多数人望而却步。

要回答您的问题,此代码实际上有很多问题,但主要问题是您正在创建 type 的回调处理程序RelayCommand,但您的命令处理程序 (AddProductToBill) 需要一个对象参数,因此应该是 type RelayCommand<object>。(更令人困惑的是,您的一个绑定是传入 CommandParameter,而另一个不是)。

要避免的另一件事是在资源块中声明您的视图模型,这通常由于多种原因而不会这样做(您确定将该实例分配为您的 DataContext 而不是声明一个新实例吗?)。如果您将 DataContext 分配到其他地方,那么您的列表元素可以通过以下方式绑定到它:

<UserControl ...
    x:Name="_this">
    .
    .
    <!-- Inside ItemsControl.ItemTemplate -->
    <Button
        Command="{Binding ElementName=_this, Path=DataContext.MyHandler}"
        CommandParameter="{Binding}" ... />

推荐阅读