如何将视图模型的属性绑定到用作项目控件源的集合项目的属性

时间:2019-06-13 13:33:52

标签: c# wpf binding viewmodel itemscontrol

我要实现的目的是以分组方式将ViewModel(mvvm light)对象的属性绑定到一些自制的自定义控件上。

所以我创建了一个CustomControl1,它具有Title属性和我自己的自定义类型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); }
        }
    }

我还添加了一个样式,将控件的内容呈现到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进行收集,或将其类型更改为Freezable的简单列表,但没有任何帮助。

我真的很想在XAML中以这种方式声明组,而不是将所有内容放到ViewModel中以到达布局。

任何一种帮助都是很好的。

谢谢。

2 个答案:

答案 0 :(得分:0)

问题是你的束缚

<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需要一个项目源,但是直到到达结束标记后才可用。

因此,如果您不想在Viewmodel中,请在窗口的“资源”部分中创建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}/>

答案 1 :(得分:0)

实际上,我通过使用提示和一些谷歌搜索找到了答案。 主要问题是,我的SomeDataControl项不是可视化树的一部分,因此它们没有获得更高级别FrameworkElement的数据上下文。 所以我对这篇文章介绍了一个绑定代理:

Usage of Binding Proxy

所以我的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>

并且绑定代理的代码也非常好用,很容易,值得重新共享:

    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));
    }

链接的博客文章中也有一些解释

  

解决我们问题的方法实际上非常简单,并且利用了Freezable类。此类的主要目的是定义具有可修改状态和只读状态的对象,但是在我们的案例中,有趣的功能是,即使Freezable对象不在可视树或逻辑树中,它们也可以继承DataContext。 / p>

谢谢大家的支持。