Привязка только части свойства 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>
Другие вопросы по тегам