Привязка только части свойства margin элемента управления WPF
У меня есть это:
<TabControl Margin="0,24,0,0">...</TabControl>
Я хочу связать только "Top"
часть TabControl, которая интуитивно я бы сделал так:
<TabControl Margin="0,{Binding ElementName=TheMenu, Path=Height},0,0">
...
</TabControl>
Как мне это сделать?
6 ответов
Вы пытались использовать конвертер, как это?
в 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>
Редактировать:
Использование мультиконвертера
Также возможно получить все четыре значения во время выполнения и использовать MultiValueConverter. Свойство Top объекта-толщины не является объектом-зависимостью, поэтому вы не можете определить привязку к нему (если ваш источник не является объектом-зависимостью).
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>
Но у меня есть некоторые эффекты с этим.
Хитрость в том, что вы не привязываете часть Margin вашего TabControl к "чему-то другому", а привязываете "что-то еще" к Margin вашего TabControl и указываете Binding-Mode OneWayToSource.
На самом деле Margin
свойство элемента управления Thickness
Тип. Таким образом, мы можем привязать его к свойству, если тип Толщина.
public Thickness LeftMargin { get; set; }
и Вы можете установить часть объекта Толщина тоже. Подобно -
LeftMargin = new Thickness(20,0,0,0);
И в Xaml
мы можем связать это свойство непосредственно с полем для свойства любого элемента.
<TextBlock Text="Some Text" Margin="{Binding LeftMargin}" />
Вы можете попробовать что-то вроде этого ответа из другого вопроса.
В решении используется присоединенное свойство, которое допускает XAML, например:
<Button ap:MoreProps.MarginRight="10" />
Присоединенное свойство также поддерживается DependencyObject, поэтому будет работать привязка данных.
Я использовал этот обходной путь только для левого поля со 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 могут перекрываться, поэтому вы хотите использовать margin для их разделения. Я чувствую эту практику как две колонки CSS Layout.
Вернемся к делу, я думаю, что вы можете подать заявку TranslateFransform
в TabControl.RenderTransform
, Вы можете связать Y
имущество.
Чтобы расширить метод Ioop создания свойства для управления маржой вместо конвертера, если вы не присоединяете к другому элементу WPF:
Создайте 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
можно использовать для других элементов управления.
Хорошо, он старый, но я искал способ получше:
<TabControl>
<TabControl.Margin>
<Thickness Top="{Binding ElementName=TheMenu, Path=Height}" />
</TabControl.Margin>
</TabControl>