C# 如何设置ItemsControl.ItemTemplate的dependency属性并处理其事件

C# 如何设置ItemsControl.ItemTemplate的dependency属性并处理其事件,c#,.net,wpf,xaml,mvvm,C#,.net,Wpf,Xaml,Mvvm,我有一个主控件(MainWindow.xaml)和一个用户控件(ItemView.xaml)。主窗口包含一个用于所有ItemView的ItemsControl和一个用于添加项目的简单按钮。所有逻辑都(应该?)在两个相应的视图模型(MainWindowViewModel和ItemViewModel)中。下面是我的代码(尽量简短),但我有两个问题: 添加新项时,会正确显示该项,但引发异常(无法创建默认转换器以在“WPFAApplication1.ItemView”和“WPFAApplication1

我有一个主控件(MainWindow.xaml)和一个用户控件(ItemView.xaml)。主窗口包含一个用于所有ItemView的ItemsControl和一个用于添加项目的简单按钮。所有逻辑都(应该?)在两个相应的视图模型(MainWindowViewModel和ItemViewModel)中。下面是我的代码(尽量简短),但我有两个问题:

  • 添加新项时,会正确显示该项,但引发异常(无法创建默认转换器以在“WPFAApplication1.ItemView”和“WPFAApplication1.ItemViewModel”类型之间执行“双向”转换)
  • 是否从未引发MainWindowViewModel中的OnDelete事件处理程序?编辑:实际上BtnDeleteClick中的ViewModel属性为空,所以是的。。。当然
  • 顺便说一句,我用

    MainWindow.xaml:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:wpfApplication1="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525"
            DataContext="{Binding RelativeSource={RelativeSource Self}}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Button Grid.Row="0" Width="100" Height="35" Content="Add" HorizontalAlignment="Left" Margin="10" Click="BtnAddClick"></Button>
            <Border Grid.Row="1" MinHeight="50">
                <ItemsControl ItemsSource="{Binding ViewModel.Items}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <wpfApplication1:ItemView ViewModel="{Binding ., PresentationTraceSources.TraceLevel=High, Mode=TwoWay}"></wpfApplication1:ItemView>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Border>
        </Grid>
    </Window>
    
    MainWindowViewModel.cs:

    [ImplementPropertyChanged]
    public class MainWindowViewModel
    {
        public ObservableCollection<ItemViewModel> Items { get; set; }
    
        public MainWindowViewModel()
        {
            Items = new ObservableCollection<ItemViewModel>();
        }
    
        public void Add()
        {
            var item = new ItemViewModel();
            item.OnDelete += (sender, args) =>
            {
                Debug.WriteLine("-- WAITING FOR THIS TO HAPPEN --");
                Items.Remove(item);
            };
            Items.Add(item);
        }
    }
    
    [ImplementPropertyChanged]
    public class ItemViewModel
    {
        public event EventHandler OnDelete;
    
        public void Delete()
        {
            var handler = OnDelete;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }
    }
    
    和ItemViewModel.cs:

    [ImplementPropertyChanged]
    public class MainWindowViewModel
    {
        public ObservableCollection<ItemViewModel> Items { get; set; }
    
        public MainWindowViewModel()
        {
            Items = new ObservableCollection<ItemViewModel>();
        }
    
        public void Add()
        {
            var item = new ItemViewModel();
            item.OnDelete += (sender, args) =>
            {
                Debug.WriteLine("-- WAITING FOR THIS TO HAPPEN --");
                Items.Remove(item);
            };
            Items.Add(item);
        }
    }
    
    [ImplementPropertyChanged]
    public class ItemViewModel
    {
        public event EventHandler OnDelete;
    
        public void Delete()
        {
            var handler = OnDelete;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }
    }
    
    你不应该设置

    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    
    在ItemView的XAML中。它有效地破坏了MainWindow.xaml中的
    ViewModel=“{Binding.}”
    绑定,因为DataContext不再是ItemsView模型,而是ItemsView

    通常,您不应该显式设置UserControl的DataContext,因为所有“外部”绑定都需要显式的
    Source
    RelativeSource


    也就是说,你做这一切太复杂了。您不必在ItemsView中使用按钮单击处理程序,只需使用delete命令创建一个视图模型,并将按钮的
    命令
    属性绑定到此命令

    可能是这样的:

    public class ItemViewModel
    {
        public string Name { get; set; }
        public ICommand Delete { get; set; }
    }
    
    public class MainViewModel
    {
        public MainViewModel()
        {
            Items = new ObservableCollection<ItemViewModel>();
        }
    
        public ObservableCollection<ItemViewModel> Items { get; private set; }
    
        public void AddItem(string name)
        {
            Items.Add(new ItemViewModel
            {
                Name = name,
                Delete = new DelegateCommand(p => Items.Remove(p as ItemViewModel))
            });
        }
    }
    
    <UserControl x:Class="WpfApplication1.ItemView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Grid>
            <Button Content="Delete"
                    Command="{Binding Delete}"
                    CommandParameter="{Binding}"/>
        </Grid>
    </UserControl>
    
    公共类ItemViewModel
    {
    公共字符串名称{get;set;}
    public ICommand Delete{get;set;}
    }
    公共类主视图模型
    {
    公共主视图模型()
    {
    Items=新的ObservableCollection();
    }
    公共可观测集合项{get;private set;}
    公共void附加项(字符串名称)
    {
    Items.Add(新的ItemViewModel
    {
    Name=Name,
    Delete=newdelegateCommand(p=>Items.Remove(p为ItemViewModel))
    });
    }
    }
    
    并且会这样使用:

    public class ItemViewModel
    {
        public string Name { get; set; }
        public ICommand Delete { get; set; }
    }
    
    public class MainViewModel
    {
        public MainViewModel()
        {
            Items = new ObservableCollection<ItemViewModel>();
        }
    
        public ObservableCollection<ItemViewModel> Items { get; private set; }
    
        public void AddItem(string name)
        {
            Items.Add(new ItemViewModel
            {
                Name = name,
                Delete = new DelegateCommand(p => Items.Remove(p as ItemViewModel))
            });
        }
    }
    
    <UserControl x:Class="WpfApplication1.ItemView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Grid>
            <Button Content="Delete"
                    Command="{Binding Delete}"
                    CommandParameter="{Binding}"/>
        </Grid>
    </UserControl>
    
    
    
    只需从
    ItemView.ViewModel
    绑定中删除
    Mode=TwoWay
    。在我看来,您还应该从ItemView中删除
    ViewModel
    属性,并改用
    DataContext
    (它已经包含对该项的引用)。只需在
    btndeleteck
    @Clemens中将其转换为
    ItemViewModel
    :即使删除Mode=TwoWay,确切的问题仍然存在。感谢解释:)从Viewmodel到View的其他绑定如何?例如,在DataContext设置为Self之前,我可以在Viewmodel和textbox视图中使用Text={Binding Viewmodel.property}的属性。现在绑定必须是{binding Path=ViewModel.Property,RelativeSource={RelativeSource-AncestorType={x:Type-views:ItemView}}。所以我必须将RelativeSource设置为视图上的所有控件?如果不显式设置DataContext,它将自动从父控件继承。在ItemsControl的ItemTemplate中,DataContext引用ItemsSource集合中的元素。更具体的问题是:我在ItemViewModel类中添加了“string TestProperty”属性,在ItemView中添加了“ItemViewModel ViewModel”属性。在ItemView的构造函数中,ViewModel被实例化,TestProperty被设置为“rickrolled”。我在ItemView的文本框中看到“rickrolled”文本的唯一方法是,如果绑定是text=“{binding Path=ViewModel.TestProperty,RelativeSource={RelativeSource AncestorType={x:Type views:ItemView}}”。这需要大量的输入,只需要一个控件。。。有什么更好的方法可以做到这一点吗?如上所述,ItemView不应该有
    ViewModel
    属性。视图模型已被DataContext引用。如果您有任何进一步的问题,您可能应该问一个关于StackOverflow的新问题。