如何将视图模型的属性绑定到用作项目控件源的 collection 的项目属性
How can I bind properties of a view model to properties of items of a collection used as source for an items control
我想要实现的是将 ViewModel (mvvm light) object 的属性以分组方式绑定到一些自制的自定义控件。
所以我创建了一个 CustomControl1,它有一个标题 属性 和一个我自己的自定义类型 SomeDataControl 的 ItemsCollection。
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(CustomControl1), new PropertyMetadata(default(string)));
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public ObservableCollection<SomeDataControl> _ItemsCollection;
public ObservableCollection<SomeDataControl> ItemsCollection {
get
{
if(_ItemsCollection == null) _ItemsCollection = new ObservableCollection<SomeDataControl>();
return _ItemsCollection;
}
}
}
public class SomeDataControl : DependencyObject
{
public static readonly DependencyProperty LAbelProperty = DependencyProperty.Register(
"LAbel", typeof(string), typeof(SomeDataControl), new PropertyMetadata(default(string)));
public string LAbel
{
get { return (string) GetValue(LAbelProperty); }
set { SetValue(LAbelProperty, value); }
}
public static readonly DependencyProperty DValueProperty = DependencyProperty.Register(
"DValue", typeof(double), typeof(SomeDataControl), new PropertyMetadata(default(double)));
public double DValue
{
get { return (double) GetValue(DValueProperty); }
set { SetValue(DValueProperty, value); }
}
}
我还添加了一个 stlye 来将我的控件的内容呈现到 ItemsControl 中,并将值绑定到适当的字段,如下所示:
<Style x:Key="ControlStyle" TargetType="local:CustomControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}"></Label>
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=LAbel}" />
<Label Content="{Binding Path=DValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我将所有这些都放在我的视图中,并将视图模型作为 DataContext,这样我就可以获取这些值。
<Window x:Class="BindingTest.MainWindow" x:Name="thisControl" DataContext="{Binding Source={StaticResource Locator}, Path=VM}">
...
<local:CustomControl1 Text="{Binding Path=DataContext.Text, ElementName=thisControl}" Style="{StaticResource ControlStyle}" >
<local:CustomControl1.ItemsCollection>
<local:SomeDataControl LAbel="Apple" DValue="{Binding Path=DataContext.DVal1, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Peach" DValue="{Binding Path=DataContext.DVal2, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Pear" DValue="{Binding Path=DataContext.DVal3, Mode=TwoWay, ElementName=thisControl}"></local:SomeDataControl>
</local:CustomControl1.ItemsCollection>
</local:CustomControl1>
一切顺利,直到我想将 DVal1、2 和 3 绑定到特定项目。它们都具有默认值。
我已经寻找答案 3 天了,但我找不到任何有用的东西。我也尝试过将 DependenyProperty 用于 collection,或将其类型更改为一个简单的列表,也是 Freezable,但没有任何帮助。
我真的很想在 XAML 中以这种方式声明我的组,而不是将所有内容放在我的 ViewModel 中以达到布局。
任何形式的帮助都会很棒。
提前谢谢你。
问题是你的绑定
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=LAbel}" />
<Label Content="{Binding Path=DValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>``
当您正在构建 Itemsource 集合时无法实现
<local:CustomControl1.ItemsCollection>
<local:SomeDataControl LAbel="Apple" DValue="{Binding Path=DataContext.DVal1, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Peach" DValue="{Binding Path=DataContext.DVal2, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Pear" DValue="{Binding Path=DataContext.DVal3, Mode=TwoWay, ElementName=thisControl}"></local:SomeDataControl>
</local:CustomControl1.ItemsCollection>
当您首先创建 SomeDataControl 时,Style 需要一个 itemsource 但它在到达结束标记之前不可用。
因此,如果您不想在 Viewmodel 中使用,请在 window 的资源部分创建 itemsource 并将其绑定到您的 customControl。
<Window.Resources>
<x:Array x:Key="Mycollection" Type="local:SomeDataControl">
<local:SomeDataControl LAbel="Apple"
DValue="{Binding Path=DataContext.DVal1, Mode=TwoWay}"/>
<local:SomeDataControl LAbel="Peach"
DValue="{Binding Path=DataContext.DVal2, Mode=TwoWay}"/>
</x:Array>
</Window.Resources>
并绑定到集合
<local:CustomControl1.ItemsCollection ItemsSource={StaticResource Mycollection}/>
实际上,我通过使用这两个提示和一些谷歌搜索找到了答案。
主要问题是,我的 SomeDataControl 项目不是可视化树的一部分,因此它们没有获得更高级别 FrameworkElement 的数据上下文。
所以我引入了一个绑定代理感谢这个 post:
所以我的 XAML 看起来像这样:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</ResourceDictionary>
</Window.Resources>
...
<local:SomeDataControl LAbel="Apple" DValue="{Binding Path=Data.DVal1, Source={StaticResource proxy}}"></local:SomeDataControl>
绑定代理的代码也很好很简单,值得re-share:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object) GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
链接博客中也有一些解释 post
The solution to our problem is actually quite simple, and takes advantage of the Freezable class. The primary purpose of this class is to define objects that have a modifiable and a read-only state, but the interesting feature in our case is that Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree.
感谢大家的支持。
我想要实现的是将 ViewModel (mvvm light) object 的属性以分组方式绑定到一些自制的自定义控件。
所以我创建了一个 CustomControl1,它有一个标题 属性 和一个我自己的自定义类型 SomeDataControl 的 ItemsCollection。
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(CustomControl1), new PropertyMetadata(default(string)));
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public ObservableCollection<SomeDataControl> _ItemsCollection;
public ObservableCollection<SomeDataControl> ItemsCollection {
get
{
if(_ItemsCollection == null) _ItemsCollection = new ObservableCollection<SomeDataControl>();
return _ItemsCollection;
}
}
}
public class SomeDataControl : DependencyObject
{
public static readonly DependencyProperty LAbelProperty = DependencyProperty.Register(
"LAbel", typeof(string), typeof(SomeDataControl), new PropertyMetadata(default(string)));
public string LAbel
{
get { return (string) GetValue(LAbelProperty); }
set { SetValue(LAbelProperty, value); }
}
public static readonly DependencyProperty DValueProperty = DependencyProperty.Register(
"DValue", typeof(double), typeof(SomeDataControl), new PropertyMetadata(default(double)));
public double DValue
{
get { return (double) GetValue(DValueProperty); }
set { SetValue(DValueProperty, value); }
}
}
我还添加了一个 stlye 来将我的控件的内容呈现到 ItemsControl 中,并将值绑定到适当的字段,如下所示:
<Style x:Key="ControlStyle" TargetType="local:CustomControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}"></Label>
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=LAbel}" />
<Label Content="{Binding Path=DValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我将所有这些都放在我的视图中,并将视图模型作为 DataContext,这样我就可以获取这些值。
<Window x:Class="BindingTest.MainWindow" x:Name="thisControl" DataContext="{Binding Source={StaticResource Locator}, Path=VM}">
...
<local:CustomControl1 Text="{Binding Path=DataContext.Text, ElementName=thisControl}" Style="{StaticResource ControlStyle}" >
<local:CustomControl1.ItemsCollection>
<local:SomeDataControl LAbel="Apple" DValue="{Binding Path=DataContext.DVal1, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Peach" DValue="{Binding Path=DataContext.DVal2, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Pear" DValue="{Binding Path=DataContext.DVal3, Mode=TwoWay, ElementName=thisControl}"></local:SomeDataControl>
</local:CustomControl1.ItemsCollection>
</local:CustomControl1>
一切顺利,直到我想将 DVal1、2 和 3 绑定到特定项目。它们都具有默认值。 我已经寻找答案 3 天了,但我找不到任何有用的东西。我也尝试过将 DependenyProperty 用于 collection,或将其类型更改为一个简单的列表,也是 Freezable,但没有任何帮助。
我真的很想在 XAML 中以这种方式声明我的组,而不是将所有内容放在我的 ViewModel 中以达到布局。
任何形式的帮助都会很棒。
提前谢谢你。
问题是你的绑定
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=LAbel}" />
<Label Content="{Binding Path=DValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>``
当您正在构建 Itemsource 集合时无法实现
<local:CustomControl1.ItemsCollection>
<local:SomeDataControl LAbel="Apple" DValue="{Binding Path=DataContext.DVal1, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Peach" DValue="{Binding Path=DataContext.DVal2, Mode=TwoWay, ElementName=thisControl}">
<local:SomeDataControl LAbel="Pear" DValue="{Binding Path=DataContext.DVal3, Mode=TwoWay, ElementName=thisControl}"></local:SomeDataControl>
</local:CustomControl1.ItemsCollection>
当您首先创建 SomeDataControl 时,Style 需要一个 itemsource 但它在到达结束标记之前不可用。
因此,如果您不想在 Viewmodel 中使用,请在 window 的资源部分创建 itemsource 并将其绑定到您的 customControl。
<Window.Resources>
<x:Array x:Key="Mycollection" Type="local:SomeDataControl">
<local:SomeDataControl LAbel="Apple"
DValue="{Binding Path=DataContext.DVal1, Mode=TwoWay}"/>
<local:SomeDataControl LAbel="Peach"
DValue="{Binding Path=DataContext.DVal2, Mode=TwoWay}"/>
</x:Array>
</Window.Resources>
并绑定到集合
<local:CustomControl1.ItemsCollection ItemsSource={StaticResource Mycollection}/>
实际上,我通过使用这两个提示和一些谷歌搜索找到了答案。 主要问题是,我的 SomeDataControl 项目不是可视化树的一部分,因此它们没有获得更高级别 FrameworkElement 的数据上下文。 所以我引入了一个绑定代理感谢这个 post:
所以我的 XAML 看起来像这样:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</ResourceDictionary>
</Window.Resources>
...
<local:SomeDataControl LAbel="Apple" DValue="{Binding Path=Data.DVal1, Source={StaticResource proxy}}"></local:SomeDataControl>
绑定代理的代码也很好很简单,值得re-share:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object) GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
链接博客中也有一些解释 post
The solution to our problem is actually quite simple, and takes advantage of the Freezable class. The primary purpose of this class is to define objects that have a modifiable and a read-only state, but the interesting feature in our case is that Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree.
感谢大家的支持。