Установить привязку как источник другой привязки - PullRequest
0 голосов
/ 22 марта 2011

У меня есть TabControl, и я хочу сделать следующее:

  1. Возьмите первый и последний из TabItem s в TabControl.Items
  2. Получи их Margin с
  3. Подайте эти Thickness es в преобразователь, чтобы преобразовать эти две структуры в окончательное значение

Вот соответствующий код, показывающий, что я пытаюсь сделать:

<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 или Source из других Binding. В основном, решение под рукой - создать конвертер, который будет принимать TabControl.Items и преобразовывать его в конечное значение, но проблема в том, что я хочу анимировать Margin s обоих TabItem s, поэтому мне нужно привязать конкретно к эти свойства. Если бы я связался с TabControl.Items, Border.Padding не обновился бы, если бы Margin любого TabItem изменилось бы. Так что мне делать?

Обновление

Хорошо, поэтому одно из возможных решений - подключить событие TabItem.Loaded, а затем использовать DependencyPropertyDescriptor, чтобы подключить событие Changed к соответствующим свойствам, затем подключить все элементы в коллекции TabItem.Items, подключить все новые элементы. и автоматически отцепить все старые предметы, и подключить, как миллион других вещей. Но это довольно сложно, и это как 400 LOC. Разве нет ничего проще? Желательно в чистом виде XAML.

1 Ответ

1 голос
/ 02 апреля 2011

К сожалению, этот измененный ответ не так элегантен, как хотелось бы, но он должен работать.

В принципе, используйте другой фиктивный элемент управления в качестве реле для привязки.В частности, этот сценарий возникает, когда вы хотите получить первый и последний из коллекции.Вы не можете просто использовать конвертер, чтобы получить первый / последний и свойство одновременно, потому что если вы измените свойство первого или последнего элемента, конвертер не получит изменение.Поэтому вы должны сделать что-то, что сочетается со свойствами зависимости - почти как какое-то свойство зависимости второго порядка, было бы неплохо.

В любом случае, вот код:

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>

Таким образом, идея заключается в том, чтобы взять первый и последний элемент с помощью управления реле, а затем привязать к ним поля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...