我仍然对WPF和编程感到不满,所以请保持温柔......
我正在创建一个工具,用于将数据从一个源移动到另一个源。这涉及映射一堆不同的表;而不是为每个创建单独的View和ViewModel,我试图让我的ViewModel对象非常通用,并在App.xaml中为它们设置DataTemplate。为了实现这一点,ViewModel对象接受一个参数,以便它知道要使用哪个表,并且我想我可以在使用DataTemplate的任何地方在代码隐藏中提供该参数。 (我知道这不是纯粹的MVVM,但对于这个项目来说似乎是合理的。)
不幸的是,我还没有能够让我的XAML DataTemplate数据绑定工作,而且我还没有能够找出故障的位置。代码如下:提前感谢任何指导!
的App.xaml:
<Application x:Class="BulkDataImportTool.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:BulkDataImportTool.ViewModels"
xmlns:m="clr-namespace:BulkDataImportTool.Models"
StartupUri="MainWindow.xaml">
<Application.Resources>
<DataTemplate DataType="{x:Type vm:MappingViewModel}" x:Key="MappingDataTemplate">
<StackPanel>
<WrapPanel>
<Label>Import File: </Label>
<Button>Select File</Button>
<TextBox Text="{Binding Mode=TwoWay, Path=ImportSourceFileName}" Width="200"></TextBox>
</WrapPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="Mappings" Width="Auto"/>
<ColumnDefinition Name="Padding" Width="20"/>
<ColumnDefinition Name="UnmappedImportSourceColumns" Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Grid.Column="0" Grid.Row="1" ItemsSource="{Binding Source=TableMapping.Mappings}">
<ListView.View>
<GridView>
<GridViewColumn Header="Database Column" Width="Auto" DisplayMemberBinding="{Binding Path=ColumnMapping.DatabaseColumn.Name}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Mode=OneTime, Path=ColumnMapping.DatabaseColumn.Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Required?" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Mode=OneTime, Path=ColumnMapping.DatabaseColumn.IsRequired}" IsEnabled="False"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Import File Column" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=UnmappedImportSourceColumns}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode=TwoWay, Path=ImportSourceColumnName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<TextBlock Grid.Column="2" Grid.Row="0" Text="Unmapped Columns from CSV"/>
<ItemsControl Grid.Column="2" Grid.Row="1" ItemsSource="{Binding Path=UnmappedImportSourceColumns}"/>
</Grid>
</StackPanel>
</DataTemplate>
</Application.Resources>
</Application>
MappingViewModel:
using BulkDataImportTool.Models;
using System.Collections.ObjectModel;
namespace BulkDataImportTool.ViewModels
{
public class MappingViewModel : PropertyChangedBase
{
public ITableMapping TableMapping { get; private set; }
//ITableMapping is a List<T> of ColumnMapping objects, which are composed a of a Column object and a string property. The Column object should stay static but the string property does not, so the latter implements IPropertyNotifyChanged. I'm not including the code for my models for the sake of brevity, but would be happy to add it if that would be helpful.
private string importSourceFileName;
public string ImportSourceFileName
{
get { return this.importSourceFileName; }
set { this.SetProperty(ref this.importSourceFileName, value, () => this.ImportSourceFileName); }
}
public ObservableCollection<string> UnmappedImportSourceColumns;
public MappingViewModel (ITableMapping tableMapping)
{
this.TableMapping = tableMapping;
}
}
}
使用DataTemplate的测试视图(ScratchXAML.xaml):
<Window x:Class="BulkDataImportTool.Views.ScratchXAML"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:BulkDataImportTool.ViewModels"
Title="ScratchXAML" Height="600" Width="1000">
<Grid>
<ContentControl Name="testMappingDataTemplate" Content="{Binding Source=MappingViewModel}" ContentTemplate="{StaticResource MappingDataTemplate}"/>
</Grid>
</Window>
该视图的代码隐藏:
using System.Windows;
using BulkDataImportTool.ViewModels;
using BulkDataImportTool.Models;
using System.Collections.ObjectModel;
namespace BulkDataImportTool.Views
{
/// <summary>
/// Interaction logic for ScratchXAML.xaml
/// </summary>
public partial class ScratchXAML : Window
{
public ScratchXAML()
{
InitializeComponent();
var mappingVM = new MappingViewModel(new CertificatesTableMapping());
//This stuff would be done elsewhere but is temporarily in the code-behind for quick testing.
mappingVM.ImportSourceFileName = "Test File Name";
const string unmappedColumn1 = "Unmapped Column 1";
const string unmappedColumn2 = "Unmapped Column 2";
var unmappedColumns = new ObservableCollection<string>();
unmappedColumns.Add(unmappedColumn1);
unmappedColumns.Add(unmappedColumn2);
//Back to reality -- this would probably be done in the code-behind of my views, unless there's a better way.
mappingVM.UnmappedImportSourceColumns = unmappedColumns;
this.DataContext = mappingVM;
}
}
}
答案 0 :(得分:0)
更简单的方法是在InitializeComponent()
准备好之后移动DataLayer
:
public partial class ScratchXAML : Window
{
public ScratchXAML()
{
var mappingVM = new MappingViewModel(new CertificatesTableMapping());
//This stuff would be done elsewhere but is temporarily in the code-behind for quick testing.
mappingVM.ImportSourceFileName = "Test File Name";
const string unmappedColumn1 = "Unmapped Column 1";
const string unmappedColumn2 = "Unmapped Column 2";
var unmappedColumns = new ObservableCollection<string>();
unmappedColumns.Add(unmappedColumn1);
unmappedColumns.Add(unmappedColumn2);
//Back to reality -- this would probably be done in the code-behind of my views, unless there's a better way.
mappingVM.UnmappedImportSourceColumns = unmappedColumns;
this.DataContext = mappingVM;
//move this line after the Data has been prepared
InitializeComponent();
}
}
或者理想的方法是在
setter
的所有属性MappingViewModel
中提升属性更改。这也是推荐的设计方式,也是为了正确支持双向绑定。
<强>更新强>
更改您的Content
Binding
,如下所示:
<ContentControl Name="testMappingDataTemplate" Content="{Binding}" ContentTemplate="{StaticResource MappingDataTemplate}"/>
由于MappingViewModel
已经是父元素的DataContext
,因此应该直接传递给ContentControl
。