编辑:问题不够明确。实际上有两个。
Q1:
我有一个UserControl“CustomView”,它是使用模板动态创建的:
<Window.Resources>
<DataTemplate DataType="{x:Type my:CustomViewModel}">
<my:CustomView/>
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Path=CustomList}"/>
其中CustomList是ObservableCollection类型的属性&lt;'CustomViewModel&gt;属于MainWindowViewModel,它是 Window 的DataContext。
在CustomView的Xaml代码中,有一些属性绑定到CustomViewModel的属性。一切正常。但是当我尝试在CustomView的代码中执行此操作时:
public CustomView()
{
InitializeComponents();
if (this.DataContext == null) Console.WriteLine ("DataContext is null");
else Console.WriteLine(this.DataContext.GetType().ToString());
}
它是用Console编写的:'DataContext为null',即使绑定在CustomView和CustomViewModel之间工作也是如此。你知道它为什么有效吗?
Q2:
现在,假设CustomView内部有另一个UserControl(IndexPicker)。 IndexPicker也有一个关联的ViewModel(IndexPickerViewModel)负责数据访问。我需要将此IndexPickerViewModel的一个属性(“Index”)绑定到之前的CustomViewModel属性“Id”。我想在StaticResources中实例化它并将它绑定到CustomViewModel(根据我之前的问题我认为是dataContext):
<UserControl x:Class="MyView.CustomView"
...
<UserControl.Resources>
<DataTemplate DataType="{x:Type myPicker:IndexPickerViewModel}">
<myPicker:IndexPicker/>
</DataTemplate>
<myPicker:IndexPickerViewModel x:Key="pickerViewModel" Index="{Binding Path=Id}/>
</Window.Resources/>
<ContentControl Content={StaticResource pickerViewModel}/>
我尝试过:我试图使“IndexPickerViewModel”继承自“DependencyObject”并使“Index”成为DependencyProperty。但是出现以下错误消息:
"System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Id; DataItem=null; target element is 'IndexPickerViewModel' (HashCode=59604175); target property is 'Index' (type 'Nullable`1')
我相信这是因为我上面提到的问题。但有可能做那样的事情吗?如果是的话,我错过了什么?而且:这是一个愚蠢的想法吗? 提前感谢您的帮助。
答案 0 :(得分:0)
现在,假设CustomView内部有另一个UserControl(IndexPicker)。 IndexPicker也有一个关联的ViewModel(IndexPickerViewModel)负责数据访问。我需要将此IndexPickerViewModel的一个属性(“Index”)绑定到之前的CustomViewModel属性“Id”。我想在StaticResources中实例化它并将它绑定到CustomViewModel(根据我之前的问题我认为是dataContext)
如果IndexPicker
没有明确设置的datacontext,那么IndexPicker
will inherit the datacontext来自它的父元素。
但是如果IndexPicker
已经有一个datacontext,那么你将不得不使用相对源绑定和祖先搜索:
Index="{Binding Id, RelaticeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, FallbackValue={x:Null}}"
当然,你可能已经感觉到这很混乱。追求UIElement或Control的标准属性是非常安全的(并且很常见),但是当你开始追踪自定义属性时,你就会在子控件和它的父控件之间引入依赖关系(当子控件不应该知道任何关于它的父级),你也必然会在某个阶段开始获取绑定错误(因此使用了回退值)。
答案 1 :(得分:0)
我似乎过早地问过,因为我自己找到了答案。
回答问题1
如果您有一个从DataTemplate动态创建的UserControl,它与另一个对象(属于ViewModel或资源)相关联,则此对象被定义为UserControl的DataContext。但是,你无法在UserControl的构造函数中访问它,你必须等到&#34; Loaded&#34;事件被提出:
public CustomUserControl()
{
InitializeComponent();
Console.WriteLine(this.DataContext.ToString());
// This doesn't work : DataContext is null
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Console.WriteLine(this.DataContext.ToString());
// or
Console.WriteLine((sender as UserControl).DataContext.ToString());
// this is Ok.
}
回答问题2
这是为了获取一个UserControl,其ViewModel在父UserControl.Resources中实例化:
你不能这样做。
相反,您在其父ViewModel中实例化其ViewModel。完整的例子:
<强> MainWindow.xaml:强>
<Window x:Class="MainWindow"
...
xmlns:local="clr-namespace:my_project_namespace"
xmlns:cust="clr-namespace:CustomUserControl;assembly=CustomUserControl"
...>
<Window.Resources>
<DataTemplate DataType="{x:Type cust:CustomControlViewModel}">
<cust:CustomControlView>
</DataTemplate>
<!-- Here are listed all the types inheriting from CustomControlViewModel and CustomControlView.-->
<!-- CustomControlViewModel and CustomControlView are used as "abstract" classes-->
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel>
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Path=CustomVMList}"/>
</Grid>
</Window>
<强> MainWindowViewModel.cs:强>
namespace my_project_namespace
{
public class MainWindowViewModel
{
public ObservableCollection<CustomControlViewModel> CustomVMList { get; set; }
public MainWindowViewModel()
{
CustomVMList = new ObservableCollection<CustomControlViewModel>();
// Fill in the list...
}
}
}
<强> CustomControlView.xaml 强>
<UserControl x:class="CustomUserControl.CustomControlView"
...
xmlns:my="clr-namespace:IndexPicker;assembly=IndexPicker"
...>
<UserControl.Resources>
<DataTemplate DataType="{x:Type my:IndexPickerViewModel}">
<my:IndexPickerView/>
</DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}/>
<ContentControl Content="{Binding Path=MyIndexPicker}"/>
</Grid>
</UserControl>
这就是它的有趣之处:
<强> CustomControlViewModel.cs:强>
namespace CustomUserControl
{
public class CustomControlViewModel : INotifyPropertyChanged
{
public IndexPickerViewModel MyIndexPicker{ get; set; }
public string Name { get ; set; }
public int Id
{
get
{
return MyIndexPicker.Index;
}
set
{
if (value != MyIndexPicker.Index)
{
MyIndexPicker.Index = value;
NotifyPropertyChanged("Id");
}
}
}
public CustomControlViewModel(string _name)
{
Name = _name;
MyIndexPicker = new IndexPickerViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
}
<强> IndexPickerView.xaml:强>
<UserControl x:Class="IndexPicker.IndexPickerView"
...
...>
<Grid>
<Combobox ItemsSource="{Binding Path=MyTable}"
DisplayMemberPath="ColumnXYZ"
SelectedItem={Binding Path=SelectedRow}/>
</Grid>
</UserControl>
最后
<强> IndexPickerViewModel.cs:强>
namespace IndexPicker
{
public class IndexPickerViewModel : INotifyPropertyChanged
{
private DataAccess data;
public DataView MyTable { get; set; }
private DataRowView selectedRow;
public DataRowView SelectedRow
{
get { return selectedRow; }
set
{
selectedRow = value;
NotifyPropertyChanged("SelectedRow");
}
}
public int? Index
{
get
{
if (SelectedRow != null) return (int?)selectedRow.Row["Column_Id"];
else return null;
}
set
{
SelectedRow = MyTable[MyTable.Find((int)value)];
NotifyPropertyChanged("Index");
}
}
public IndexPickerViewModel()
{
data = new DataAccess();
MyTable = data.GetTableView("tableName");
MyTable.Sort = "Column_Id";
}
// And don't forget INotifyPropertyChanged implementation
}
}
此配置与几个不同的 UserControls 一起使用,继承自 CustomControlView ,而 ViewModel 继承自 CustomControlViewModel 。它们是动态创建的,并在 CustomVMList 中列出。这里包含IndexPicker的CustomControlViewModel已经是一个专业化。
具体用法:CRUD数据库表的通用对话框,可根据每个表列动态创建UserControl。此处显示的专业化用于包含外键的列的情况。 我希望它清楚。
上面列出的代码可能包含错误。批评和评论是受欢迎的。