将Binding设置为其他Binding的源

时间:2011-03-22 19:46:24

标签: c# .net wpf xaml binding

我有TabControl,我想执行以下操作:

  1. TabItem
  2. 中获取TabControl.Items的第一个和最后一个
  3. 获取他们的Margin
  4. 将这些Thickness es提供给转换器,以将这两个结构转换为最终值
  5. 以下是显示我要做的事情的相关代码:

    <Border.Padding>
        <MultiBinding Converter="{StaticResource MarginsToPaddingConverter}">
            <Binding Path="Margin">
                <Binding.Source>
                    <Binding RelativeSource="{RelativeSource AncestorType=TabControl}" Path="Items" Converter="{StaticResource ItemCollectionToFirstItemConverter}" ConverterParameter="{x:Type TabItem}" />
                </Binding.Source>
            </Binding>
            <Binding Path="Margin">
                <Binding.Source>
                    <Binding RelativeSource="{RelativeSource AncestorType=TabControl}" Path="Items" Converter="{StaticResource ItemCollectionToLastItemConverter}" ConverterParameter="{x:Type TabItem}" />
                </Binding.Source>
            </Binding>
        </MultiBinding>
    </Border.Padding>
    

    但我无法将Binding设置为其他RelativeSource的{​​{1}}或Source。基本上,手头的解决方案是创建转换器,它将Binding并将其转换为最终值,但问题是我想要为TabControl.Items的两个Margin设置动画,所以我需要专门绑定这些属性。如果我绑定到TabItem,则TabControl.Items如果任何Border.Padding的{​​{1}}发生更改,则不会刷新Margin。那我该怎么办?

    更新

    好的,所以其中一个可能的解决方案是挂钩TabItem事件,然后使用TabItem.Loaded在适当的属性上挂钩DependencyPropertyDescriptor事件,然后挂钩{{1}中的所有项目收集,挂钩任何新项目并自动取消挂钩所有旧项目,并挂钩像其他万物。但这很复杂,就像400 LOC。有没有更简单的东西?最好是纯Changed

1 个答案:

答案 0 :(得分:1)

不幸的是,这个修改过的响应并不像我想的那么优雅,但它应该有用。

基本上,使用另一个虚拟控件作为绑定的中继。具体而言,当您想要拉取集合的第一个和最后一个时,就会出现这种情况。您不能只使用转换器同时获取第一个/最后一个属性和属性,因为如果您更改第一个或最后一个项目的属性,转换器将不会获取更改。所以你必须做一些与依赖属性融合的东西 - 几乎就像某种二阶依赖属性会很好。

无论如何,这里有一些代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Collections;

namespace WpfApplication1
{
    public class CollectionHelper : Control
    {
        public static DependencyProperty CollectionProperty = DependencyProperty.Register(
            "Collection",
            typeof(IEnumerable),
            typeof(CollectionHelper),
            new FrameworkPropertyMetadata(OnCollectionChanged));

        public IEnumerable Collection
        {
            get { return GetValue(CollectionProperty) as IEnumerable; }
            set { SetValue(CollectionProperty, value); }
        }

        private static void OnCollectionChanged(object rawSender, DependencyPropertyChangedEventArgs args)
        {
            CollectionHelper sender = (CollectionHelper)rawSender;
            IEnumerable value = args.NewValue as IEnumerable;
            if(value==null)
            {
                sender.First = null;
                sender.Last = null;
                return;
            }
            bool isFirstSet = false;
            object lastItemTemp = null;
            foreach (var item in value)
            {
                if (!isFirstSet)
                {
                    sender.First = item;
                    isFirstSet = true;
                }
                lastItemTemp = item;
            }
            if (!isFirstSet)
                sender.First = null;
            sender.Last = lastItemTemp;
        }

        public DependencyProperty FirstProperty = DependencyProperty.Register(
            "First",
            typeof(object),
            typeof(CollectionHelper));

        public object First
        {
            get { return GetValue(FirstProperty); }
            set { SetValue(FirstProperty, value); }
        }

        public DependencyProperty LastProperty = DependencyProperty.Register(
            "Last",
            typeof(object),
            typeof(CollectionHelper));

        public object Last
        {
            get { return GetValue(LastProperty); }
            set { SetValue(LastProperty, value); }
        }
    }
}

一个实际的用例:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Helper="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Helper:CollectionHelper x:Name="Helper" Collection="{Binding SomeStrings}" />
        <TextBlock Text="{Binding ElementName=Helper, Path=First}" />
        <TextBlock Text="{Binding ElementName=Helper, Path=Last}" />
    </StackPanel>
</Window>

所以这里的想法是使用中继控件抓住第一个和最后一个项目,然后绑定到边距。