我有TabControl
,我想执行以下操作:
TabItem
TabControl.Items
的第一个和最后一个
Margin
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
的{{1}}或Source
。基本上,手头的解决方案是创建转换器,它将Binding
并将其转换为最终值,但问题是我想要为TabControl.Items
的两个Margin
设置动画,所以我需要专门绑定这些属性。如果我绑定到TabItem
,则TabControl.Items
如果任何Border.Padding
的{{1}}发生更改,则不会刷新Margin
。那我该怎么办?
好的,所以其中一个可能的解决方案是挂钩TabItem
事件,然后使用TabItem.Loaded
在适当的属性上挂钩DependencyPropertyDescriptor
事件,然后挂钩{{1}中的所有项目收集,挂钩任何新项目并自动取消挂钩所有旧项目,并挂钩像其他万物。但这很复杂,就像400 LOC。有没有更简单的东西?最好是纯Changed
。
答案 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>
所以这里的想法是使用中继控件抓住第一个和最后一个项目,然后绑定到边距。