【发布时间】:2011-09-09 02:55:50
【问题描述】:
我有这个:
<TabControl Margin="0,24,0,0">...</TabControl>
我只想绑定 TabControl 的 "Top" 部分,直觉上我会这样做:
<TabControl Margin="0,{Binding ElementName=TheMenu, Path=Height},0,0">
...
</TabControl>
我该怎么做?
【问题讨论】:
我有这个:
<TabControl Margin="0,24,0,0">...</TabControl>
我只想绑定 TabControl 的 "Top" 部分,直觉上我会这样做:
<TabControl Margin="0,{Binding ElementName=TheMenu, Path=Height},0,0">
...
</TabControl>
我该怎么做?
【问题讨论】:
你试过用这样的转换器吗?
在 VB.Net 中
Public Class MarginConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
Return New Thickness(0, CDbl(value), 0, 0)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Return Nothing
End Function
End Class
或者在 C# 中
public class MarginConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new Thickness(0, System.Convert.ToDouble(value), 0, 0);
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
XAML
<Window.Resources>
<local:MarginConverter x:Key="marginConverter"></local:MarginConverter>
</Window.Resources>
<Grid>
<StackPanel>
<Slider Name="Slider1"></Slider>
<TabControl Name="TabControl" Margin="{Binding ElementName=Slider1, Path=Value, Converter={StaticResource marginConverter}}">
<Button>Some content</Button>
</TabControl>
</StackPanel>
</Grid>
编辑:
使用 MultiConverter
还可以在运行时获取所有四个值并使用 MultiValueConverter。 Thickness-Object 的 Top-Property 不是 Dependency-Object,因此您不能定义对它的绑定(除非您的源不是 Dependency-Object)。
XAML
<Window.Resources>
<local:MarginConverter x:Key="marginConverter"></local:MarginConverter>
<local:MultiMarginConverter x:Key="multiMarginConverter"></local:MultiMarginConverter>
</Window.Resources>
<Grid>
<StackPanel>
<Slider Name="Slider1"></Slider>
<Slider Name="Slider2"></Slider>
<Slider Name="Slider3"></Slider>
<Slider Name="Slider4"></Slider>
<TabControl Name="TabControl">
<TabControl.Margin>
<MultiBinding Converter="{StaticResource multiMarginConverter}">
<Binding ElementName="Slider1" Path="Value"></Binding>
<Binding ElementName="Slider2" Path="Value"></Binding>
<Binding ElementName="Slider3" Path="Value"></Binding>
<Binding ElementName="Slider4" Path="Value"></Binding>
</MultiBinding>
</TabControl.Margin>
<Button>Some content</Button>
</TabControl>
</StackPanel>
</Grid>
...和c#
class MultiMarginConverter : IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new Thickness(System.Convert.ToDouble(values[0]),
System.Convert.ToDouble(values[1]),
System.Convert.ToDouble(values[2]),
System.Convert.ToDouble(values[3]));
}
public object[] ConvertBack(object value, System.Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
编辑(2) 反向绑定:
我不确定这是否会让你开心。以我的拙见,我会尽量避免这种情况,但是好的......如果你的源是依赖属性,你可以将它绑定到边距:
<Slider Name="Slider5" Minimum="-99" Maximum="0" Value="{Binding ElementName=TabControl, Path=Margin.Top, Mode=OneWayToSource}"></Slider>
但我对此有一些影响。
诀窍是,您不要将 TabControl 的 Margin 的一部分绑定到“其他”,而是将“其他”绑定到 TabControl 的 Margin 并指定 Binding-Mode OneWayToSource。
【讨论】:
<TabControl.Margin.Thikness.Top="{Binding...}"...>
FallBackValue 添加到Binding 时,它会在传递给MultiMarginConverter 之前转换为Thickness,这会导致它出错。有什么可以防止这种情况发生,还是我必须在转换器中将其拆开?
你可以尝试类似this answer from another question。
该解决方案使用允许 XAML 的附加属性,如下所示:
<Button ap:MoreProps.MarginRight="10" />
附加属性也由 DependencyObject 支持,因此数据绑定将起作用。
【讨论】:
从您的代码中,我认为您的菜单和 tabControl 可能重叠,因此您想使用边距将它们分开。我感觉这种做法像两列 CSS Layout。
言归正传,我觉得你可以申请TranslateFransform 到TabControl.RenderTransform。可以绑定Y属性。
【讨论】:
实际上一个控件的Margin属性是Thickness类型。所以我们可以将它绑定到 Property if type Thickness。
public Thickness LeftMargin { get; set; }
您也可以设置厚度对象的一部分。喜欢 -
LeftMargin = new Thickness(20,0,0,0);
在Xaml 中,我们可以将此属性直接绑定到任何元素的边距属性......就像这样......
<TextBlock Text="Some Text" Margin="{Binding LeftMargin}" />
【讨论】:
Margin 属性来完成。据我所知,MultiBinding 是唯一的方法。
如果您不附加到另一个 WPF 元素,则扩展 Ioop 的方法来控制边距而不是转换器:
创建 4 个标准属性和一个只读属性,就像这样-
Public Class CustomMargin
Implements INotifyPropertyChanged
Private _Left As Double
Private _Right As Double
Private _Up As Double
Private _Down As Double
Public Sub New()
_Up = 0
_Down = 0
_Left = 0
_Right = 0
End Sub
Public Sub New(Vertical as Double, Horizontal as Double)
_Up = Vertical
_Down = Vertical
_Left = Horizontal
_Right = Horizontal
End Sub
Public Sub New(Left as Double, Up as Double, Right as Double, Down as Double)
_Up = Up
_Down = Down
_Left = Left
_Right = Right
End Sub
Public Property Left As Double
Get
Return _Left
End Get
Set(value As Double)
_Left = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public Property Right As Double
Get
Return _Right
End Get
Set(value As Double)
_Right = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public Property Up As Double
Get
Return _Up
End Get
Set(value As Double)
_Up = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public Property Down As Double
Get
Return _Down
End Get
Set(value As Double)
_Down = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public ReadOnly Property MyMargin As Thickness
Get
Return New Thickness(Left, Up, Right, Down)
End Get
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
If Not PropertyChangedEvent Is Nothing Then
RaiseEvent PropertyChanged(Me, e)
End If
End Sub
End Class
然后你只需要添加XAML-
<Label x:Name="MyLabel" Margin="{Binding Path=MyMargin, FallbackValue=0 0 0 0, Mode=OneWay}"/>
然后在WPF窗口后面的代码上-
Private _NewMargin as New CustomMargin
Public Sub New()
InitializeComponent()
MyLabel.DataContext = _NewMargin
End Sub
从那里您可以使用任何您希望单独更改所有 4 个边距的控件,Class 可重复用于其他控件。
【讨论】:
我只在 StackPanel 中使用了这种解决方法来处理左边距。好处是你不需要任何转换器。
<DockPanel VerticalAlignment="Top">
<TextBlock Name="tbkFulltextCaption"
Text="Static Caption:"
DockPanel.Dock="Left" />
<StackPanel Orientation="Horizontal"
DockPanel.Dock="Bottom">
<FrameworkElement Name="feLeftMargin"
Width="{Binding Width, ElementName=tbkFulltextCaption, Mode=OneWay}" />
<TextBlock Text="(some text with margin of tbkFulltextCaption.Width)"
Name="tbkUnderNonsense"
FontSize="8"
Foreground="Gray">
</TextBlock>
</StackPanel>
<TextBox Name="tbFulltextSearch" />
</DockPanel>
【讨论】:
好的,它很旧,但我正在寻找更好的方法:
<TabControl>
<TabControl.Margin>
<Thickness Top="{Binding ElementName=TheMenu, Path=Height}" />
</TabControl.Margin>
</TabControl>
【讨论】: