C# 如何设置ItemsControl.ItemTemplate的dependency属性并处理其事件
我有一个主控件(MainWindow.xaml)和一个用户控件(ItemView.xaml)。主窗口包含一个用于所有ItemView的ItemsControl和一个用于添加项目的简单按钮。所有逻辑都(应该?)在两个相应的视图模型(MainWindowViewModel和ItemViewModel)中。下面是我的代码(尽量简短),但我有两个问题: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
<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的新问题。